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 PointNet++的onnx模型只能使用一次
  • ¥20 西南科技大学数字信号处理
  • ¥15 有两个非常“自以为是”烦人的问题急期待大家解决!
  • ¥30 STM32 INMP441无法读取数据
  • ¥15 R语言绘制密度图,一个密度曲线内fill不同颜色如何实现
  • ¥100 求汇川机器人IRCB300控制器和示教器同版本升级固件文件升级包
  • ¥15 用visualstudio2022创建vue项目后无法启动
  • ¥15 x趋于0时tanx-sinx极限可以拆开算吗
  • ¥500 把面具戴到人脸上,请大家贡献智慧,别用大模型回答,大模型的答案没啥用
  • ¥15 任意一个散点图自己下载其js脚本文件并做成独立的案例页面,不要作在线的,要离线状态。