Yii многостраничный мастер форм
Я пытаюсь создать многостраничную форму с 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?
Спасибо!
Ответы
Ответ 1
Есть несколько способов приблизиться к этому. Я вижу, что вы размещены на форуме Yii, поэтому я предполагаю, что вы обыскали там тоже, но в случае, если вы этого не сделали:
Что я сделал (просто для простой двухэтапной формы ActiveRecord), предпринял одно действие и разделил его на условные блоки на основе имени кнопки, которые Yii POSTs на форме submit (примечание: не работает с помощью ajax). Затем, в зависимости от того, какая кнопка была удалена, я отображаю правильную форму и задаю правильный сценарий для моей модели для целей проверки.
Скрытое поле "шаг", как и вы, может служить той же цели, что и проверка имени submitButton. Я бы, возможно, сохранил "шаг" в состоянии формы вместо добавления скрытого поля, хотя и было бы хорошо.
Некоторые люди используют атрибут activeful для stateful, чтобы сохранять данные с одного шага в мастере, или вы можете использовать сеанс или даже сохранять в таблице базы данных temp. В моем полностью непроверенном примере ниже я использую функциональность формы с сохранением состояния.
Вот пример того, что я в основном сделал для формы 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));
} ?>
Формы выглядят примерно так:
Form1:
<?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(); ?>
Я надеюсь, что это будет полезно!
Ответ 2
Yii предоставляет функцию, называемую состояниями страницы, для реализации таких функций, как мастер создания нескольких шагов/нескольких страниц.
Давайте сначала посмотрим на документы Yii:
Состояние страницы - это переменная, которая является постоянной для запросов POST на той же странице. Чтобы использовать постоянные состояния страниц, форма (формы) должна быть сгенерирована с использованием {@link CHtml:: statefulForm}.
Таким образом, формы каждого шага/страницы должны быть формами с сохранением состояния. Чтобы создать форму с сохранением состояния, вам просто нужно установить свойство CActiveForm::stateful
в true
при запуске виджета ActiveForm.
Внутри вашего контроллера вы можете получить и установить свои состояния страниц с помощью CController::getPageState()
или CController::setPageState()
.
Итак, вот те основы, которые работают очень хорошо, если реализация вашего мастера создания нескольких страниц выполняется в традиционном стиле без запросов AJAX.
Если вы хотите использовать вызовы AJAX для отправки данных шага и отображения следующего шага, состояния страниц Yii непригодны для использования.
Почему? Все состояния страниц переносятся через HTTP-POST в скрытом поле ввода. Поле ввода заполняется Yii при так называемой выходной обработке. Обработка вывода начинает после рендеринга и заменяет части вывода. Таким образом, функция Yii page states требует обработки вывода. Ответы 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;
}
}
}