Табличный (пакетный) ввод в yii2

Для обработки нескольких моделей одного типа в yii2 присутствует механизм пакетной обработки, который называется "табличный ввод" (пакетный ввод).

Например, в одной форме необходимо ввести неограниченное количество наименований товара. Создадим миграцию для товаров:

php yii migrate/create create_product_table

$this->createTable('{{%product}}', [
    'id' => $this->primaryKey(),
    'title' => $this->string()->notNull()->comment('Название'),
    'description' => $this->string()->notNull()->comment('Описание'),
    'cost' => $this->double()->notNull()->comment('Стоимость'),
]);

Через gii создадим модель Product и crud-контроллер ProductController.

Теперь изменим функцию actionCreate:

public function actionCreate()
{
   $count = count(Yii::$app->request->post('Product', []));
   $model = [new Product()];

   for($i = 1; $i < $count; $i++) {
       $model[] = new Product();
   }

   if (Model::loadMultiple($model, Yii::$app->request->post()) && Model::validateMultiple($model)) {

       foreach ($model as $item) {
           $item->save(false);
       }
       return $this->redirect('index');
    }

    return $this->render('create', [
        'model' => $model,
    ]);
}

В файле @app\views\product\_form.php также произведем замену:

<?php $form = ActiveForm::begin(); ?>

    <?php foreach ($model as $index => $item) : ?>

        <?= $form->field($item, "[$index]title")->textInput(['maxlength' => true]) ?>

        <?= $form->field($item, "[$index]description")->textInput(['maxlength' => true]) ?>

        <?= $form->field($item, "[$index]cost")->textInput() ?>

    <?php endforeach; ?>

    <div id="ajax_forms"></div>

    <div class="form-group">
        <?= Html::submitButton('Save', ['class' => 'btn btn-success']) ?>
    </div>

<?php ActiveForm::end(); ?>

Теперь займемся динамичным добавлением полей, для этого в @app\views\product\_form.php добавим кнопку и скрипт. После всех изменений:

<div class="product-form">

    <?= Html::a('Добавить продукт', 'javascript:void(0)', ['class' => 'btn btn-success btn-add-product', 'data-count' => $count]) ?>

    <?php $form = ActiveForm::begin(); ?>

    <?php foreach ($model as $index => $item) : ?>

        <?= $form->field($item, "[$index]title")->textInput(['maxlength' => true]) ?>

        <?= $form->field($item, "[$index]description")->textInput(['maxlength' => true]) ?>

        <?= $form->field($item, "[$index]cost")->textInput() ?>

    <?php endforeach; ?>

    <div id="ajax_forms"></div>

    <div class="form-group">
        <?= Html::submitButton('Save', ['class' => 'btn btn-success']) ?>
    </div>

    <?php ActiveForm::end(); ?>

</div>

<?php
$script = <<< JS
    $('.btn-add-product').on('click', function() {
      var ths = $(this);  
      var cnt = ths.attr('data-count');

      $.ajax({
            'dataType' : 'html',
            'data': {'cnt': cnt},
            'success' : function(data) {
                $('#ajax_forms').append(data);
                ths.attr('data-count', parseInt(cnt)+1);
            },
            'type' : 'post',
            'url' : '/product/add'
        });
    })
JS;
$this->registerJs($script, yii\web\View::POS_READY);

В @app\controllers\ProductController.php добавим новый метод:

public function actionAdd()
{
    $cnt = Yii::$app->request->post('cnt');

    return $this->renderAjax('_add_form', [
       'cnt' => $cnt,
       'model' => new Product()
    ]);
}

Содержимое файла @app\views\product\_add_form.php:

<div class="form-group field-product-0-title">

    <?= Html::label(
        $model->getAttributeLabel('title'), 
        "product-$cnt-title", 
        ['class' => 'control-label']) 
    ?>
    <?= Html::textInput(
        "Product[$cnt][title]", 
        '', 
        ['class' => 'form-control', 'id' => "product-$cnt-title"]) 
    ?>
    <div class="help-block"></div>

    <?= Html::label(
        $model->getAttributeLabel('description'), 
        "product-$cnt-description", 
        ['class' => 'control-label']) 
    ?>
    <?= Html::textInput(
        "Product[$cnt][description]", 
        '', 
        ['class' => 'form-control', 'id' => "product-$cnt-description"]) 
    ?>
    <div class="help-block"></div>

    <?= Html::label(
        $model->getAttributeLabel('cost'), 
        "product-$cnt-cost", 
        ['class' => 'control-label']) 
    ?>
    <?= Html::textInput(
        "Product[$cnt][cost]", 
        '', 
        ['class' => 'form-control', 'id' => "product-$cnt-cost"]) 
    ?>
    <div class="help-block"></div>

</div>

После всех изменений у нас готов функционал для пакетного ввода данных.

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

{{ message }}

{{ 'Comments are closed.' | trans }}