Я пытаюсь создать многостраничную форму с 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?
Благодаря!
Есть несколько способов приблизиться к этому. Я вижу, что вы размещены на форуме Yii, поэтому я предполагаю, что вы обыскали там тоже, но если у вас нет:
То, что я сделал (просто для простой двухэтапной формы ActiveRecord), предприняло одно действие и разделило его на условные блоки на основе имени кнопки, которые Yii POSTs на форме submit (примечание: не работает с ajax отправляет ). Затем, в зависимости от того, какая кнопка была удалена, я делаю правильную форму и задаю правильный сценарий моей модели для целей проверки.
Скрытое поле «шаг», как и вы, может служить той же цели, что и проверка имени submitButton. Я бы, возможно, сохранил «шаг» в состоянии формы вместо добавления скрытого поля, хотя и все было бы хорошо.
Некоторые люди используют атрибут activeForm для сохранения данных с одного шага в мастере, или вы можете использовать сеанс или даже сохранять таблицу базы данных 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(); ?>
с<?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(); ?>
с<?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(); ?>
Надеюсь, это полезно!
Yii предоставляет функцию, называемую состояниями страниц, для реализации таких вещей, как мастер создания нескольких шагов / нескольких страниц.
Давайте сначала посмотрим на документы Yii:
Состояние страницы – это переменная, которая является постоянной для запросов POST на той же странице. Чтобы использовать постоянные состояния страниц, форма (формы) должна быть с сохранением состояния, которые генерируются с помощью {@link CHtml :: statefulForm}.
Таким образом, формы каждого шага / страницы должны быть формами с сохранением состояния. Чтобы отобразить форму с сохранением состояния, вам просто нужно установить для CActiveForm::stateful
значение true
при запуске виджета ActiveForm. Внутри вашего контроллера вы можете получить и установить свои состояния страниц с помощью 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; } } }