24

我正在尝试使用 Yii 构建一个多页表单,但对 PHP 和 Yii 还是很陌生,我想知道编写多页表单的最佳实践是什么。到目前为止,我计划做的是添加一个名为“step”的隐藏字段,其中包含用户在表单中的当前步骤(表单分为 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 中有更好、更优化的方法来做到这一点?

谢谢!

4

2 回答 2

36

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

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

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

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

这是我基本上为 ActiveRecord 表单所做的示例。这在“actionCreate”中:

<?php if (isset($_POST['cancel'])) {
  $this->redirect(array('home'));
} elseif (isset($_POST['step2'])) {
  $this->setPageState('step1',$_POST['Model']); // save step1 into form state
  $model=new Model('step1');
  $model->attributes = $_POST['Model'];
  if($model->validate())
    $this->render('form2',array('model'=>$model));
  else {
    $this->render('form1',array('model'=>$model));
  }
} elseif (isset($_POST['finish'])) {
  $model=new Model('finish');
  $model->attributes = $this->getPageState('step1',array()); //get the info from step 1
  $model->attributes = $_POST['Model']; // then the info from step2
  if ($model->save())
    $this->redirect(array('home'));
  else {
    $this->render('form2',array('model'=>$model));
} else { // this is the default, first time (step1)
  $model=new Model('new');
  $this->render('form1',array('model'=>$model));
} ?>

表格看起来像这样:

表格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(); ?>

表格 2:

<?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(); ?>

我希望这会有所帮助!

于 2010-08-23T21:06:56.033 回答
4

Yii 提供了一个称为页面状态的功能来实现诸如多步骤/多页面表单向导之类的东西。

让我们先看一下 Yii 文档:

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

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

因此,如果您的多页表单向导的实现是在没有 AJAX 请求的传统样式中进行的,那么这些基础知识可以很好地工作。

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

为什么?所有页面状态都通过隐藏输入字段中的 HTTP-POST 传输。输入字段由 Yii 填充,而所谓的输出处理。输出处理在渲染开始,并将替换部分输出。所以Yii 的页面状态功能需要输出处理。另一方面,AJAX 响应可能会被它破坏,因为输出处理也可能在输出的开头添加<link><script>标记以加载所需的 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;
        }
    }
}
于 2014-09-05T08:32:31.407 回答