dseslyh6662605 2015-08-22 13:56
浏览 48
已采纳

触发CakePHP多步表单验证

I have implemented a CakePHP multistep form. When a user clicks 'next step', data on the page is validated, saved and, if it's not the first/last step, merged with previous data in session. Besides 'next step' and 'previous step', users can also navigate between the steps that they have already passed using the navigation bar.

However, data saving and validation is only triggered by clicking 'next step'. Problems happen, for instance, when a user in step 3 clicks 'step 1' to go back and update the data, and clicks 'step 3' to return, the data will not be revised. How do I make validation work on clicking navigation links as well?

In controller (PostController):

/**
 * this method is executed before starting the form and retrieves one important parameter:
 * the form steps number
 * you can hardcode it, but in this example we are getting it by counting the number of files that start with msf_step_
 */
public function msf_setup() {
    App::uses('Folder', 'Utility');
    $this->Session->delete('form');
    $usersViewFolder = new Folder(APP.'View'.DS.'Posts');
    $steps = count($usersViewFolder->find('msf_step_.*\.ctp'));
    $this->Session->write('form.params.steps', $steps);
    $this->Session->write('form.params.maxProgress', 0);
    $this->redirect(array('action' => 'msf_step', 1));
}

/**
 * this is the core step handling method
 * it gets passed the desired step number, performs some checks to prevent smart users skipping steps
 * checks fields validation, and when succeding, it saves the array in a session, merging with previous results
 * if we are at last step, data is saved
 * when no form data is submitted (not a POST request) it sets this->request->data to the values stored in session
 */
public function msf_step($stepNumber) {
    if (null == ($this->Session->read('form.params.steps'))) {
        $this->redirect(array('action' => 'msf_setup'));
    }       
    $this->set('stepNumber', $stepNumber);

    /**
     * check if a view file for this step exists, otherwise redirect to index
     */
    if (!file_exists(APP.'View'.DS.'Posts'.DS.'msf_step_'.$stepNumber.'.ctp')) {
        $this->redirect('/posts/msf_setup');
    }
    /**
     * determines the max allowed step (the last completed + 1)
     * if choosen step is not allowed (URL manually changed) the user gets redirected
     * otherwise we store the current step value in the session
     */
    $maxAllowed = $this->Session->read('form.params.maxProgress') + 1;
    if ($stepNumber > $maxAllowed) {
        $this->redirect('/posts/msf_step/'.$maxAllowed);
    } else {
        $this->Session->write('form.params.currentStep', $stepNumber);
    }

    /**
     * check if some data has been submitted via POST
     * if not, sets the current data to the session data, to automatically populate previously saved fields
     */
    if ($this->request->is('post')) {
        /**
         * if data validates we merge previous session data with submitted data, using CakePHP powerful Hash class (previously called Set)
         */
        if ($this->Post->saveAll($this->request->data, array('validate' => 'only', 'deep' => true))) {

            $prevSessionData = $this->Session->read('form.data');
            $currentSessionData = Hash::merge( (array) $prevSessionData, $this->request->data);

            /**
             * if this is not the last step we replace session data with the new merged array
             * update the max progress value and redirect to the next step
             */
            if ($stepNumber < $this->Session->read('form.params.steps')) {
                $this->Session->write('form.data', $currentSessionData);
                $this->Session->write('form.params.maxProgress', $stepNumber);
                $this->redirect(array('action' => 'msf_step', $stepNumber+1));

            } else {
                /**
                 * otherwise, this is the final step, so we have to save the data to the database
                 */

                if(AuthComponent::user('id')) {
                    $currentSessionData['Post']['email'] = AuthComponent::user('username');
                    $currentSessionData['Post']['user_id'] = AuthComponent::user('id');
                    unset($currentSessionData['User']); //Just in case a user is logged in after Step 1 that User data is already entered
                } else {
                    // We can save the User data:
                    // it should be in $this->request->data['User']
                    $currentSessionData['User']['group_id'] = '4';
                    $user = $this->Post->User->save($currentSessionData);

                    // The ID of the newly created user has been set
                    // as $this->User->id.
                    $currentSessionData['Post']['email'] = $currentSessionData['User']['username'];
                    $currentSessionData['Post']['user_id'] = $this->Post->User->id;
                    unset($currentSessionData['User']);
                }

                $this->Post->create();
                unset($this->Post->Student->validate['post_id']);
                if ($this->Post->saveAssociated($currentSessionData, array('deep' => true))) {
                    $this->Session->setFlash(__('The post has been saved.'), 'alert_box', array('class' => 'alert-success'));
                    //$this->Session->delete('form');
                    return $this->redirect(array('action' => 'index'));
                } else {
                    $this->Session->setFlash(__('The post could not be saved. Please, try again.'), 'alert_box', array('class' => 'alert-danger'));
                }                         
            }
        }
    } else {
        $this->request->data = $this->Session->read('form.data');
    }

    /**
     * here we load the proper view file, depending on the stepNumber variable passed via GET
     */
    $this->render('msf_step_'.$stepNumber);
}

In the form, except for the first and last page, 'previous step', navigation and 'next page' is as follows:

//Previous Step
<?php echo $this->Html->link(__('Previous Step'),
    array('action' => 'msf_step', $params['currentStep'] -1),
    array('class' => 'btn btn-default')
); ?>

//navigation
<?php for ($i=1; $i <= $params['steps']; $i++) {
    if ($i > $params['maxProgress'] + 1) { ?>
        <a href="" class="btn btn-default" disabled><?php echo 'Step '.$i.''; ?></a>  
<?php } else {
    $class = ($i == $params['currentStep']) ? 'btn btn-default disabled' : 'btn btn-default';
    echo $this->Html->link('Step '. $i,
        array('action' => 'msf_step', $i),
        array('class' => $class)
        );
    }
} ?>

//Next Step
<?php $options = array(
    'label' => __('Next Step'),
    'class' => 'btn btn-default pull-right', 
    'div' => array(
        'class' => 'form-group'
    )
);
echo $this->Form->end($options); ?>
  • 写回答

1条回答 默认 最新

  • du7999 2015-08-22 14:36
    关注

    If the user clicks to go directly to step3 then you are not validating the data because the request is not a post request. You are validating the data only on post requests.

    if ($this->request->is('post'))
    

    In order to make links as post request you can use the function http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::postLink

    It uses similar syntax like $this->Html->link. But as reading the docs it is not a good approach to use this method inside forms. Can't you put the buttons outside the form?

    Otherwise you might need to review your if statement.

    if ($this->request->is('post'))
    

    I guess there are many ways to work around this issue.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 (标签-STM32|关键词-智能小车)
  • ¥20 关于#stm32#的问题,请各位专家解答!
  • ¥15 (标签-python)
  • ¥15 第一个已完成,求第二个做法
  • ¥20 搭建awx,试了很多版本都有错
  • ¥15 java corba的客户端该如何指定使用本地某个固定IP去连接服务端?
  • ¥15 activiti工作流问题,求解答
  • ¥15 有人写过RPA后台管理系统么?
  • ¥15 Bioage计算生物学年龄
  • ¥20 如何将FPGA Alveo U50恢复原来出厂设置哇?