php Yii 多页表单向导最佳实践
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3548528/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Yii multi page form wizard best practice
提问by Stinky Tofu
I am trying to build a multi-page form with Yii, but am quite new to PHP and Yii and am wondering what the best practice is for writing a multi page form. So far, what I am planning to do is to add a hidden field named 'step' which contains the current step the user is on in the form (the form is broken into 3 steps/pages). So with that in mind, this is how I plan to handle the user clicking on previous/next buttons in the Controller:
我正在尝试用 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);
}
}
Does this approach make sense? Or am I way off base and there is a much better and more optimized way of doing this in Yii/PHP?
这种方法有意义吗?或者我离基地很远,并且在 Yii/PHP 中有更好和更优化的方法来做到这一点?
Thanks!
谢谢!
回答by thaddeusmt
There are a couple of ways to approach this. I see you posted in the Yii forum so I assume you've searched around there too but in case you haven't:
有几种方法可以解决这个问题。我看到你在 Yii 论坛上发帖,所以我假设你也在那里搜索过,但如果你还没有:
- http://www.yiiframework.com/forum/index.php?/topic/6203-stateful-forms-and-wizards-howto/
- http://www.yiiframework.com/forum/index.php?/topic/8413-wizard-forms/
- http://www.yiiframework.com/forum/index.php?/topic/6203-stateful-forms-and-wizards-howto/
- http://www.yiiframework.com/forum/index.php?/topic/8413-wizard-forms/
What I have done is (just for a simple 2-step ActiveRecord form) taken a single action and divided it up into conditional blocks based on the button name, which Yii POSTs on a form submit (note: doesn't work with ajax submits). Then, depending on which button was hit I render the correct form and set the correct scenario on my model for validation purposes.
我所做的是(仅针对一个简单的 2 步 ActiveRecord 表单)采取了一个单一的行动,并根据按钮名称将其划分为条件块,Yii 在表单提交上发布(注意:不适用于 ajax 提交)。然后,根据点击的按钮,我呈现正确的表单并在我的模型上设置正确的场景以进行验证。
A hidden "step" field like you have could serve the same purpose as the checking the submitButton name. I would perhaps save the "step" into the form state instead of adding a hidden field though, but either would be fine.
像您这样隐藏的“步骤”字段可以起到与检查 submitButton 名称相同的目的。我可能会将“步骤”保存到表单状态而不是添加隐藏字段,但两者都可以。
Some people use the stateful activeForm attribute to save the data from a single step in the wizard, or you can use the session, or even save to a temp database table. In my completely untested example below I am using a the stateful form functionality.
有些人使用有状态的 activeForm 属性来保存向导中单个步骤的数据,或者您可以使用会话,甚至保存到临时数据库表。在下面我完全未经测试的示例中,我使用了有状态表单功能。
Here is an example of what I basically did for an ActiveRecord form. This goes in the "actionCreate":
这是我对 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));
} ?>
The forms would look something like this:
表格看起来像这样:
Form1:
表格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(); ?>
Form 2:
表格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(); ?>
I hope that is helpful!
我希望这是有帮助的!
回答by SaV
Yii provides a feature called page states to implement things like a multi step / multi page form wizard.
Yii 提供了一个称为页面状态的功能来实现多步骤/多页面表单向导之类的功能。
Lets have a look at the Yii docs first:
让我们先看看 Yii 文档:
A page state is a variable that is persistent across POST requests of the same page. In order to use persistent page states, the form(s) must be stateful which are generated using {@link CHtml::statefulForm}.
页面状态是在同一页面的 POST 请求中持续存在的变量。为了使用持久的页面状态,表单必须是有状态的,它是使用 {@link CHtml::statefulForm} 生成的。
So the forms of every step / page need to be stateful forms. To render a stateful form you just need to set the CActiveForm::stateful
property to true
when you start the ActiveForm-widget.
Within your controller you can get and set your page states with CController::getPageState()
or CController::setPageState()
.
所以每一步/页面的表单都需要是有状态的表单。要呈现有状态的表单,您只需要在启动 ActiveForm-widget 时将该CActiveForm::stateful
属性设置为true
。在您的控制器中,您可以使用CController::getPageState()
或获取和设置页面状态CController::setPageState()
。
So these are the basics that work quite well if the implementation of your multi page form wizard is made in the traditional style without AJAX requests.
因此,如果您的多页表单向导的实现是在没有 AJAX 请求的传统风格中实现的,那么这些是非常有效的基础知识。
If however you want to use AJAX calls to submit step data and display the next step, Yii's page states are not usable.
但是,如果您想使用 AJAX 调用提交步骤数据并显示下一步,则 Yii 的页面状态将不可用。
Why? All the page states are transported through HTTP-POST within a hidden input field. The input field gets filled by Yii while the so called output processing. The output processing starts afterthe rendering and will replace parts of the output. So Yii's page states feature requires the output processing. AJAX responses on the other hand may become corrupted by it because the output processing may also add <link>
or <script>
tags at the beginning of the output to load required JS and CSS files.
为什么?所有页面状态都通过隐藏输入字段中的 HTTP-POST 传输。输入字段由 Yii 填充,同时进行所谓的输出处理。输出处理在渲染后开始,并将替换部分输出。所以Yii 的页面状态功能需要输出处理。另一方面,AJAX 响应可能会被它破坏,因为输出处理也可能在输出的开头添加<link>
或<script>
标记以加载所需的 JS 和 CSS 文件。
In the end I implemented my own version of stateful forms. I am able to get my stateful data with the static function call ActiveFormWidget::getRequestMultiStepData()
every time I need it.
最后,我实现了我自己的有状态表单版本。ActiveFormWidget::getRequestMultiStepData()
每次需要时,我都可以通过静态函数调用获取我的有状态数据。
Notice: There is one disadvantage in my implementation: all stateful data needs to be collected before the form widget will be initialized. But I never had a problem with it until now. However here is the code:
注意:我的实现有一个缺点:在初始化表单小部件之前需要收集所有有状态的数据。但直到现在我从来没有遇到过问题。但是这里是代码:
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;
}
}
}