Лучшая практика мастера многостраничных форм Yii

Я пытаюсь создать многостраничную форму с помощью 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?

Спасибо!


person Stinky Tofu    schedule 23.08.2010    source источник


Ответы (2)


Есть несколько подходов к этому. Я вижу, что вы разместили сообщение на форуме Yii, поэтому я предполагаю, что вы тоже искали там, но на случай, если вы этого не сделали:

Что я сделал, так это (только для простой двухэтапной формы ActiveRecord) предпринял одно действие и разделил его на условные блоки на основе имени кнопки, которые Yii POSTs при отправке формы (примечание: не работает с ajax submits ). Затем, в зависимости от того, какая кнопка была нажата, я визуализирую правильную форму и устанавливаю правильный сценарий в своей модели для целей проверки.

Скрытое поле «шаг», как у вас, может служить той же цели, что и проверка имени 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(); ?>

Я надеюсь, что это полезно!

person thaddeusmt    schedule 23.08.2010
comment
Спасибо, это было очень полезно! Мне очень нравится ваш подход к использованию кнопок отправки, чтобы определить, какой шаг отображать. Я также рассмотрю подход ActiveForm, который звучит интересно. - person Stinky Tofu; 25.08.2010
comment
Большое спасибо. Где я могу узнать больше о подобных вещах? Например, профессиональная разработка с помощью yii framework. - person FDisk; 21.12.2010
comment
относительно примечания: не работает с отправкой ajax, это верно, особенно в jquery, но вы можете просто добавить имя/значение кнопки к результату form.serialize и будет работать - person Neil McGuigan; 26.03.2011
comment
Я пробовал это по имени кнопки отправки, но он никогда не читал кнопку step2. Когда я нажимаю кнопку шага 1, я перехожу к шагу 2, когда я нажимаю кнопку шага 2, он пропускает и возвращается к шагу 1. - person FosAvance; 05.11.2013
comment
Важная часть информации, чтобы сделать ваше сообщение более полным, поскольку класс модели должен управлять сценариями проверки полей формы. - person crafter; 01.02.2015
comment
вау, я долго искал это в сети, я уверен, что приведенный выше ответ - лучшее решение для разделения одной формы на несколько, но моя проблема в том, что я начинающий программист php, в настоящее время работаю над проектом с использованием yii2. 0, но я раньше не работал с фреймворком yii 1.0, поэтому может быть мой вопрос глупый, но он сложный для меня :), мой вопрос: работает ли приведенный выше синтаксис с фреймворком yii2.0? синтаксис изменен в фреймворке yii2.0 для многоэтапной формы ??? пожалуйста помоги - person Sadia Naseeba; 16.07.2015
comment
@Sadia Naseeba: это наверняка не будет работать с Yii 2, и даже логика может немного измениться. - person MEM; 27.08.2015

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;
        }
    }
}
person SaV    schedule 05.09.2014