Form Using Confirmation Screen Before Saving

Hello,

I have been working on a form that presents a confirmation screen before saving the data. In this screen the user should be able to go back and modify or confirm and submit (save to database) the fields.

One approach was using just a js confirmation message but this is not what I needed. What I did is redirecting the user to another view the first time the form is submitted. This view creates a form with invisible fields and also displays the input as text. Something like this:




<div class="row">

        <b> <?php echo CHtml::activeLabel($model,'first_name'); ?></b>

        <?php echo CHtml::activeHiddenField($model,'first_name'); ?>

        <?= $model->first_name ?>

 </div>



To differentiate between the first submission of the form and the submission of the confirmation screen form I added in the confirmation screen another hidden field named ‘conf’


<?php echo CHtml::hiddenField('conf','1'); ?>

The controller looks for this ‘conf’ field and if present it saves the data. To be sure that the data is still valid it calls the verify() method. The problem is that the form includes a captcha so by the time the confirmation form is submitted yii says that the captcha code is not valid anymore. The way I solved this is using scenarios:




   public function actionRegister()

	{

                 

		$form=new User();

                

                // collect user input data

                if(isset($_POST['User']))

                {

                        $conf = false;

                        if(isset($_POST['conf']) && $_POST['conf'] == 1) {

                            $form=new User('conf');  // create user with 'conf' scenario so that the captcha code is not needed

                            $conf = true;

                        }


                        $form->attributes=$_POST['User']; 

                        

                        if($form->validate())

                        {                               

                                if ($conf === true) {

                                    $form->save();

                                    $this->redirect($this->render('success'));  // the success screen after the form is saved to db 

                                }


                                $this->redirect($this->render('confscreen',array('model'=>$form))); // the confirmation form with hidden fields


                        }

                }


                $this->render('myform',array('model'=>$form)); // display the registration form

	}



I can see one flaw in my design (the captcha code can be bypassed if the form is submitted directly from the conffirmation form screen). I would like to know of there is a better approach, but until I know a better solution I leave it like this.

The form works. The only thing is that I do not have a button to go back from confirmation screen to the initial form so that the user can change the data instead of submitting. The only way I know how to do this is using a javascript go back function sent to the browser. I am very sure that this is not a good solution (the captcha will probably be loaded from the browsers cache but when submitted it is going to be marked as not valid). ALso, I am sure there is a very easy solution on how to implement the go back button. Any ideas?

Edit: Now that I wrote this message I noticed that even though I am rendering different views using $this->redirect, the same initial controller action is called in all of them. This makes the confirmation screen inaccessible unless called from the original form so it is not possible to submit the form without filling out the captcha, right?

You can have separate actions and views for the form and the confirmation. The submit button on the form sends the user to the confirmation page. On the confirmation page, a button sends the user to the form again, with all data loaded to the controls (no need to use history.back()) and another button confirms the data and saves it to the database.

Also, I suggest that you use the captcha only in the confirmation view.

But how can I make one of the buttons on the confirmation page send the user to the form page? I only know how to add a submit button and that button for some reason triggers the controller action of the first form page (that is why I added a hidden field to identify if the request comes from the first form page or the confirmation page).

What you are telling me to do, is what I am trying to do but I don’t know how to :s

Sorry if I confused you :)

You can keep using the same action, but render different views. Check these forum posts for references on how to do it:

http://www.yiiframework.com/forum/index.php/topic/31038-two-submit-buttons-one-form/

http://www.yiiframework.com/forum/index.php/topic/9416-multiple-submit-buttons-in-a-cactiveform/

Then you don’t need the hidden field “conf” anymore.

By checking which button was submitted, you can show the correct view or save the data.

PS: I heard that some weird browsers (IE, I’m looking at you) submit the names of all buttons. You will want to test if this approach works correctly.

Thank you Rodrigo, now I understand how to do it :)

The thing about IE worries me because most of the end users of the application I am making will use IE. I do not have IE to test if it works. I did a search in the web and it looks like IE has problems with <button type="submit"…> tag but not with <input type="submit"…>. Yii is using the <input…> tag so I really hope it works with all browsers.

Happy to help!

If IE indeed causes trouble for you, you can use Javascript to fill a hidden field with the name of the button that was clicked.

I added two buttons to my form




 <?php echo CHtml::submitButton('Cancel', array('name' => 'cancel')); ?>

 <?php echo CHtml::submitButton('Accept', array('name' => 'accept')); ?>



but now I am getting a PHP Warning!! (when in the confirmation screen)




PHP warning

Cannot modify header information - headers already sent by (output started at /yii-1.1.12/framework/web/CController.php:794)



This only happens if I add two buttons to my form (it does not happen with one button).

In the controller I am doing a redirect:




public function actionRegister() {

    ...

    if($form->validate())

       $this->redirect($this->render('confirmation',array('model'=>$form))); 

    ...

}



Edit: solved the issue suppressing the php warning:


 @$this->redirect($this->render('conf'.$group,array('model'=>$form)));

But I do not know the reason of the warning when I add two buttons to the form and if what I am doing is the correct move

Please check if you have a white space or a blank line before your PHP open tag on the view called by that line.

The view does not have a space nor a blank line. The first thing in the view file is:




<?php $this->pageTitle=Yii::app()->name . ' - Register' ; ?>

...



If I only include one button (any of the two buttons) in the confirmation form then I do not get the php warning. If I include two or more buttons I get the warning. I have tried other things like including one submitbutton and a link button with the same result-- a php warning.

I also tried to hard code the form buttons. Instead of using CHtml:: I wrote the html directly like this:




<input type="submit" value="Submit" name="Submit">

<input type="submit" value="Submit2" name="Submit2">



The result was that if I have more than one button in the form I get the php warning, but if I have only one I do not get the warning. This is very strange!

I am startung to think it could be a Yii bug??

Well, actually I now read your code throughly and found the issue: you are trying to redirect the user after rendering a view. That’s not a bug in Yii.

When you render a view, data is output by the application.

When you redirect the user, a header must be sent. PHP can’t send the header if data is already output.

The fix for this is using the redirection method correctly. You don’t need to call render: the user will be redirected and, in the next request, the view will be rendered.

See the docs for CController::redirect.

Hmm funny thing the code worked when I had just one button in my form. I copied that part of the code from some other place and I never thought it was not correct because it worked until I added two buttons to my form. Now that I read how the redirect works makes sense that it was not correct and my problem was solved by using this code:




$this->render('conf',array('model'=>$form)); 

exit;



Basically, I removed the redirect and used just the render but I had to write the "exit;" statement so that the other render commands would not take place.

I’ll probably write a wiki some of these days on how to do the whole form with confirmation thing.

You should use CApplication::end instead.