Php Yii多页表单向导最佳实践

Php Yii多页表单向导最佳实践,php,yii,yii1.x,Php,Yii,Yii1.x,我正在尝试用Yii构建一个多页表单,但是我对PHP和Yii还很陌生,我想知道编写多页表单的最佳实践是什么。到目前为止,我计划做的是添加一个名为“步骤”的隐藏字段,其中包含用户在表单中的当前步骤(表单分为3个步骤/页)。因此,考虑到这一点,我计划如何处理用户单击控制器中的上一个/下一个按钮: public function actionCreate() { $userModel = new User; $data['activityModel'] = $activityModel;

我正在尝试用Yii构建一个多页表单,但是我对PHP和Yii还很陌生,我想知道编写多页表单的最佳实践是什么。到目前为止,我计划做的是添加一个名为“步骤”的隐藏字段,其中包含用户在表单中的当前步骤(表单分为3个步骤/页)。因此,考虑到这一点,我计划如何处理用户单击控制器中的上一个/下一个按钮:

public function actionCreate()
 {
  $userModel = new User;

  $data['activityModel'] = $activityModel; 
  $data['userModel'] = $userModel; 

  if (!empty($_POST['step']))
  {
   switch $_POST['step']:
    case '1':
     $this->render('create_step1', $data);
     break;

    case '2':
     $this->render('create_step2', $data);
     break;

  }else
  {
   $this->render('create_step1', $data);
  }
 }
这种方法有意义吗?或者说我有点离谱,在Yii/PHP中有一种更好、更优化的方法来实现这一点


谢谢

有几种方法可以解决这个问题。我在Yii论坛上看到你的帖子,所以我假设你也在那里搜索过,但如果你没有:

我所做的是(仅针对一个简单的两步ActiveRecord表单)执行单个操作,并根据按钮名称将其划分为条件块,Yii将其发布在表单提交上(注意:不适用于ajax提交)。然后,根据按下的按钮,我呈现正确的表单,并在模型上设置正确的场景,以进行验证

像您这样的隐藏“步骤”字段可以起到与检查submitButton名称相同的作用。我可能会将“步骤”保存到表单状态,而不是添加一个隐藏字段,但两者都可以

有些人使用有状态activeForm属性保存向导中单个步骤的数据,或者您可以使用会话,甚至可以保存到临时数据库表。在下面完全未经测试的示例中,我使用的是有状态表单功能

下面是我基本上为ActiveRecord表单所做的一个示例。这在“actionCreate”中出现:


这些表单看起来像这样:

表格1:

<?php $form=$this->beginWidget('CActiveForm', array(
    'enableAjaxValidation'=>false,
    'id'=>'model-form',
    'stateful'=>true,
));
<!-- form1 fields go here -->
echo CHtml::submitButton("Cancel",array('name'=>'cancel');
echo CHtml::submitButton("On to Step 2 >",array('name'=>'step2');
$this->endWidget(); ?>

Yii提供了一种称为页面状态的功能,以实现多步骤/多页面表单向导之类的功能

让我们先看一下Yii文档:

页面状态是一个在同一页面的POST请求之间保持不变的变量。为了使用持久页面状态,表单必须是有状态的,使用{@link CHtml::statefulForm}生成

因此,每个步骤/页面的表单都需要是有状态的表单。要呈现有状态表单,启动ActiveForm小部件时只需将
CActiveForm::stateful
属性设置为
true
。 在控制器中,您可以使用
CController::getPageState()
CController::setPageState()
获取和设置页面状态

因此,如果您的多页表单向导的实现是以传统样式进行的,而不需要AJAX请求,那么这些是非常有效的基础

但是,如果您想使用AJAX调用提交步骤数据并显示下一步,Yii的页面状态将不可用。

为什么??所有页面状态都通过隐藏输入字段中的HTTP-POST传输。在进行所谓的输出处理时,输入字段由Yii填充。输出处理在渲染后开始,并将替换部分输出。因此,Yii的页面状态功能需要进行输出处理。另一方面,AJAX响应可能会被它损坏,因为输出处理可能还会在输出的开头添加
标记,以加载所需的JS和CSS文件

最后,我实现了自己版本的有状态表单。每次需要时,我都可以通过静态函数调用
ActiveFormWidget::getRequestMultiStepData()
获取有状态数据

注意:我的实现有一个缺点:在初始化表单小部件之前,需要收集所有有状态的数据。但直到现在我才有过问题。但代码如下:

class ActiveFormWidget extends CActiveForm
{
    public static $inputNameMultiStepData = '_multiStepData';

    public $multiStep = false;
    public $multiStepData = array();

    public function init()
    {
        parent::init();

        # Hidden-Fields
        if ($this->multiStep) {
            echo Html::hiddenField(static::$inputNameMultiStepData, static::encodeInputData($this->multiStepData));
        }
    }

    /**
     * Gets all multi step data sent.
     * @return array|mixed
     */
    public static function getRequestMultiStepData()
    {
        return isset($_REQUEST[static::$inputNameMultiStepData]) ? static::decodeInputData($_REQUEST[static::$inputNameMultiStepData]) : array();
    }


    /**
     * Encodes form data like Yii does for stateful forms.
     * @param $data
     * @return string
     */
    public static function encodeInputData($data)
    {
        $data = Yii::app()->getSecurityManager()->hashData(serialize($data));

        return base64_encode($data);
    }

    /**
     * Decodes form data like Yii does for stateful forms.
     * @param $data
     * @return bool|mixed
     */
    public static function decodeInputData($data)
    {
        $data = base64_decode($data);
        $data = Yii::app()->getSecurityManager()->validateData($data);
        if ($data !== false) {
            return unserialize($data);
        } else {
            return false;
        }
    }
}

看一看
CHtml::statefulForm
,这是一个非常有用的感谢!我非常喜欢您使用提交按钮来确定显示哪个步骤的方法。我还将研究ActiveForm方法,它听起来很有趣。谢谢。我在哪里可以学到更多这样的东西?就像使用yii框架进行专业开发一样。关于“注意:不适用于ajax提交”,这是正确的,尤其是在jquery中,但您可以将按钮名称/值附加到form.serialize的结果中,并将工作。我已经尝试通过提交按钮名称来实现这一点,但它从未读取step2按钮。当我单击step1按钮时,它会将我带到step2,当我单击step2时,它会跳过并返回到step1。这是使您的文章更完整的一条重要信息,因为模型类必须管理表单字段验证的场景。
<?php $form=$this->beginWidget('CActiveForm', array(
    'enableAjaxValidation'=>false,
    'id'=>'model-form',
    'stateful'=>true,
));
<!-- form2 fields go here -->
echo CHtml::submitButton("Back to Step 1",array('name'=>'step1');
echo CHtml::submitButton("Finish",array('name'=>'finish');
$this->endWidget(); ?>
class ActiveFormWidget extends CActiveForm
{
    public static $inputNameMultiStepData = '_multiStepData';

    public $multiStep = false;
    public $multiStepData = array();

    public function init()
    {
        parent::init();

        # Hidden-Fields
        if ($this->multiStep) {
            echo Html::hiddenField(static::$inputNameMultiStepData, static::encodeInputData($this->multiStepData));
        }
    }

    /**
     * Gets all multi step data sent.
     * @return array|mixed
     */
    public static function getRequestMultiStepData()
    {
        return isset($_REQUEST[static::$inputNameMultiStepData]) ? static::decodeInputData($_REQUEST[static::$inputNameMultiStepData]) : array();
    }


    /**
     * Encodes form data like Yii does for stateful forms.
     * @param $data
     * @return string
     */
    public static function encodeInputData($data)
    {
        $data = Yii::app()->getSecurityManager()->hashData(serialize($data));

        return base64_encode($data);
    }

    /**
     * Decodes form data like Yii does for stateful forms.
     * @param $data
     * @return bool|mixed
     */
    public static function decodeInputData($data)
    {
        $data = base64_decode($data);
        $data = Yii::app()->getSecurityManager()->validateData($data);
        if ($data !== false) {
            return unserialize($data);
        } else {
            return false;
        }
    }
}