Валидация динамически добавлемых полей в Yii

Все началось с новой работы, на которой пришлось отказать от Zend и перейти на Yii. При создании личного кабинета для сайта потребовались динамическое добавление полей в форме. После ковыряния в интернете пришло такое решение. Поехали:

Проблем было 2. Первое это организовать форму в несколько шагов — с этим помог справиться wizard-behavior (http://www.yiiframework.com/extension/wizard-behavior). Немного переделав, сделал на один action и одну модель. Валидация полей происходит на основе сценариев.
Добавление динамических полей сделал через ajax через render-partial вьюшки, которая содержала необходимые поля.

Форма создается во view:
$form=$this->beginWidget('CActiveForm', array(
                    'id'=>'create_order_step1',
                    'enableClientValidation'=>true,
                    'clientOptions'=>array(
                        'validateOnSubmit'=>true,
                        'validateOnType' => true,
                    ),));


В ней же выводим кнопку для добавление полей:
echo CHtml::button('добавить место', array('class' => 'place-add btn'));
<script>
$(".place-add").click(function(){
    var index = $(".new-place").size()+1;       /// считаем  кол-во уже добавленных блоков с полями. 
    $.ajax({
        success: function(html){
            $("#add_place").append(html);      // выводим во вью наши поля.
        },
        type: 'get',
        url: '/index.php/site/field',      // делаем ajax зарос к action
        data: {
            index: index
        },
        cache: false,
        dataType: 'html'
    });
});
</script>

Action:
public function actionField($index = false)
    {
        $model = new CreateOrderForm();
        $this->renderPartial('_add_place_fields', array(
            'model' => $model,
            'index' => $index,
        ));
    }

Получаем номер блока, который уже есть +1.

Динамические поля:
<table class="new-place" id="place_n<?php echo$index; ?>">                                   
        <tr>
            <td>
                <?php
                echo CHtml::activeTextField($model, "place_weight[$index]");       /// создаем поля с индексами.
                echo CHtml::error($model, "place_weight[$index]");               // вываливаем ошибки валидации
                 ?>
            </td>
        </tr>
</table>

В основном action валидацию делаем просто $model->validate(scenario) // сценарий менялся в зависимости от шага.
Если валидация не прошла тогда смотрим кол-во полей в модели и отдаем во вью. (Чтобы при ошибки валидации динамически добавленные поля не исчезли)
if(isset($model->attributes['place_weight'])){
        $count = count($model->attributes['place_weight']);
}
$this->render('view', compact('model', 'count');

Во вью для этого выводим эти поля так:
for($i =1; $i<$count; $i++){
     $this->renderPartial('_add_place_fields', array(
              'model' => $model,
              'index' => $i,
     ));
}


Ну и самое главное — Модель и валидация:

public function rules()
    {
        return array(array('place_weight', 'validatePlace', 'on'=>'step1'), );
    }
public function validatePlace($attribute,$params)       //здесь волшебная функция которая валидует массив динамически добавляемых полей.
    {
        foreach($this->place_weight as $key_w => $weight){
            if (empty($weight)) {
                $this->addError('place_weight['.$key_w.']', 'Поле должно быть заполнено');
                break;
            }
        }
}

Собственно все. Думаю начинающим в yii, вроде меня это сократит много времени.
  • +2
  • 15.3k
  • 8
Share post

Comments 8

    0
    Совсем не Yii-style. А почему бы не сделать поле просто с именем param[]? Пришел бы массив значений, который легко завернуть в модель.
      +1
      В принципе наверное можно было бы param[]. Я хотел описать именно валидацию динамических полей. А вообще тут много можно сделать на свой вкус. Это лишь пример.
        0
        Неудачный пример
          0
          Для этого в модели нужно создать валидатор, который в качестве названия полей может принять регулярки и нормально описать в модель. Не спорю, можно и велосипедом, но по-нормальному смотрится лучше и запутаться не получится.
        0
        С ajax валидацией, как я понимаю, вы заморачиваться не стали?
          0
          вместе empty я бы использовал CValidation::createVaslidator()->validate()
            0
            Приведите, пожалуйста код CreateOrderForm().
              0
              Это простая модель. Выше я уже приводил часть кода. Если хотите вот полностью. Я только вырезал кучу других переменных, не относящихся к теме.

              class CreateOrderForm extends CFormModel
              {
              public $place_weight; 
              public function rules()
                  {
                      return array(array('place_weight', 'validatePlace', 'on'=>'step1'), );
                  }
              public function validatePlace($attribute,$params)      
                  {
                      foreach($this->place_weight as $key_w => $weight){
                          if (empty($weight)) {
                              $this->addError('place_weight['.$key_w.']', 'Поле должно быть заполнено');
                              break;
                          }
                      }
              }
              }
              
              

            Only users with full accounts can post comments. Log in, please.