Building multipage forms (Intermediate)

Learn to create multi-page AJAX enabled forms using Yii

(For more resources related to this topic, see here.)

Getting ready

We'll separate our existing user registration form already created to multipage forms. The sections will be for personal information, address details, and contact information.

How to do it...

  1. All code related to the form is written in a file named _form under protected/views/user.

  2. We are dividing the input fields into three sections, so create three separate files in the same folder with the names _page1, _page2, and _page3. Separate the code's respective files. Some sample lines are as follows:

    <?php $form=$this->beginWidget('CActiveForm', array( 'id'=>'user-form', 'enableAjaxValidation'=>false, 'stateful'=>true, )); ?> <div class="row"> <?php echo $form->labelEx($model,'first_name'); ?> <?php echo $form->textField($model,'first_name', array( 'size'=>50, 'maxlength'=>50 )); ?> <?php echo $form->error($model,'first_name'); ?> </div> ..... ..... <div class="row buttons"> <?php echo CHtml::submitButton('Next', array( 'name'=>'page2' )); ?> </div> <?php $this->endWidget(); ?> .... <div class="row buttons"> <?php echo CHtml::submitButton('back', array( 'name'=>'page1' )); ?> <?php echo CHtml::submitButton('Next', array( 'name'=>'page3' )); ?> </div> <div class="row buttons"> <?php echo CHtml::submitButton('Back', array( 'name'=>'page2' )); ?> <?php echo CHtml::submitButton('submit', array( 'name'=>'submit' )); ?> </div>

  3. Now, in the User controller, change the code for actionCreate as follows:

    public function actionCreate() { if(isset($_POST['page1'])) { $model = new User('page1'); $this->checkPageState($model, $_POST['User']); $view = '_page1'; } elseif(isset($_POST['page2'])) { $model = new User('page1'); $this->checkPageState($model, $_POST['User']); if($model->validate()) { $view = '_page2'; $model->scenario = 'page2'; } else { $view = '_page1'; } } .... $this->render($view, array('model'=>$model)); }

  4. And add a function, checkPageState(), as follows:

    private function checkPageState(&$model, $data) { $model->attributes = $this->getPageState('page', array()); $model->attributes = $data; $this->setPageState('page', $model->attributes); }

  5. Lastly, create scenarios in the model User to validate each page of the form separately. Add three arrays specifying all the required fields per page, as follows:

    return array( array('first_name, last_name, gender, dob', 'required', 'on'=>'page1' ), array('address_1, city, state, country', 'required', 'on'=>'page2' ), array('phone_number_1, email_1', 'required', 'on'=>'page3' ),

How it works...

We have separated all our input fields into three forms. Each page contains an entire standalone form that accepts the input from the user, validates it from the server, and stores the data till we finally submit this form. The parameter stateful passed to the CactiveForm widget specifies the form needed to maintain the state across the pages. To do this, Yii creates a hidden field in each form with the name YII_PAGE_STATE, as shown in the following screenshot:

All the data submitted on the first page is stored in this hidden field and passed to the server with the second page.

To read the data from this field we have used the method getPageState(), and to write we have used setPageState(). We have added a private method checkPageState() to the User controller, which reads the page state, if any, and assigns it to $model->attributes, then assigns data from the current form using $model->attributes = $_POST['User'], and finally overwrites the page state with freshly combined data.

When we click on Next on _page1, we set the POST variable page2, which in turn executes the second block in the if-else ladder in actionCreate. In this article, we create an instance of the model User with scenario set to _page1 (as we need to validate the data received from _page1). With a call to checkPageState(), we check the current page state and add any new data from _page1 to the page state.

Then we check if the data filled is valid using $model->validate() . If the model passes the validation we set, apply view to _page2 and set $model->scenario to _page2, to mark the required fields on _page2. If the validation fails, we set the view to _page1 with the validation errors set in the model.

At the end of the action, we render the selected view with the current state of the model. If any validation errors are set, they are listed on the same page; else, the next page will be rendered. The same steps are repeated for _page2 as well.

When the submit button is clicked on on _page3, we retrieve the previous data from the page state using getPageState(). Here we are not using checkPageState() as now we do not need to store any data to the page state. We simply assign the data from _page3 to the model, and if the model validates we save all the data to the database with $model->save(). After saving, we are redirected to actionView(), where data from all three forms is listed as shown in the following screenshot:


In this article, we saw the importance of dividing big single forms into mulitpage forms. The article provided an insight into the making of multipage forms.

Resources for Article :

Further resources on this subject:

Books to Consider

Instant Building Multi-Page Forms with Yii How-to
$ 12.99
ChronoForms 3.1 for Joomla! site Cookbook
$ 26.99
Hybrid Cloud Management with Red Hat CloudForms
$ 31.99
comments powered by Disqus