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.

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

报告相同问题?

悬赏问题

  • ¥50 易语言把MYSQL数据库中的数据添加至组合框
  • ¥20 求数据集和代码#有偿答复
  • ¥15 关于下拉菜单选项关联的问题
  • ¥20 java-OJ-健康体检
  • ¥15 rs485的上拉下拉,不会对a-b<-200mv有影响吗,就是接受时,对判断逻辑0有影响吗
  • ¥15 使用phpstudy在云服务器上搭建个人网站
  • ¥15 应该如何判断含间隙的曲柄摇杆机构,轴与轴承是否发生了碰撞?
  • ¥15 vue3+express部署到nginx
  • ¥20 搭建pt1000三线制高精度测温电路
  • ¥15 使用Jdk8自带的算法,和Jdk11自带的加密结果会一样吗,不一样的话有什么解决方案,Jdk不能升级的情况