Book Image

Instant Building Multi-Page Forms with Yii How-to

By : Uday Sawant
Book Image

Instant Building Multi-Page Forms with Yii How-to

By: Uday Sawant

Overview of this book

Yii is a high-performance PHP framework for rapid web application development. It is well-designed, well-supported, easy to learn, and easy to maintain. Forms are the basic method used to collect information on the Internet, and in cases like user registration and payment process, there's a lot of data to be presented and the lists can get lengthy. Instant Building Multi-Page Forms with Yii is a simple and descriptive how-to that provides step-by-step recipes to help you in converting your lengthy forms into short, interactive forms. It will guide you through the inbuilt features of Yii framework to help you with this complex job. The book starts with the basics of Yii and will guide you through creating simple forms before then converting your simple forms into multi-page forms. It will use the capabilities of Yii to ease the tasks required to do this. Yii eases your work by providing options like database connectivity. We will also take a look at the automated code generation capabilities of Yii in generating forms and necessary CRUD operations, as well as looking at how to divide a single long form into multiple views with same controller/action, and then add form validations and AJAX support to the forms. We will also cover the upload process and how to customise Yii's looks.
Table of Contents (7 chapters)

Building multipage forms (Intermediate)


In this recipe, we will separate our lengthy registration form into multiple pages.

Why do we need multipage forms? Because we don't want our visitors to scroll too much and want to enable them to fill out forms as quickly as possible. Multipage forms look much shorter than a single form and fit better without much change in design; most importantly, we can group the form fields in logical sections.

Getting ready

We'll separate our existing user registration form created in the Creating basic forms recipe, 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 recipe, 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: