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 微信小程序协议怎么写
  • ¥15 c语言怎么用printf(“\b \b”)与getch()实现黑框里写入与删除?
  • ¥20 怎么用dlib库的算法识别小麦病虫害
  • ¥15 华为ensp模拟器中S5700交换机在配置过程中老是反复重启
  • ¥15 java写代码遇到问题,求帮助
  • ¥15 uniapp uview http 如何实现统一的请求异常信息提示?
  • ¥15 有了解d3和topogram.js库的吗?有偿请教
  • ¥100 任意维数的K均值聚类
  • ¥15 stamps做sbas-insar,时序沉降图怎么画
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看