Иногда вам нужно обрабатывать несколько моделей одного типа в одной форме. Например, несколько настроек, где каждый параметр хранится как пара имя-значение и представлен моделью Setting активной записи. Этот вид формы также часто называют «табличным вводом». 

Ниже показано, как реализовать табличный ввод с Yii. Есть три различные ситуации для покрытия, которые приходится решать несколько иначе:

  • Обновление фиксированного набора записей из базы данных
  • Создание динамического набора новых записей
  • Обновление, создание и удаление записей на одной странице

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

  • Model::loadMultiple() загружает данные сообщения в массив моделей.
  • Model::validateMultiple() проверяет массив моделей.

Обновление фиксированного набора записей

Начнем с действия контроллера:

<?php

namespace app\controllers;

use Yii;
use yii\base\Model;
use yii\web\Controller;
use app\models\Setting;

class SettingsController extends Controller
{
    // ...

    public function actionUpdate()
    {
        $settings = Setting::find()->indexBy('id')->all();

        if (Model::loadMultiple($settings, Yii::$app->request->post()) && Model::validateMultiple($settings)) {
            foreach ($settings as $setting) {
                $setting->save(false);
            }
            return $this->redirect('index');
        }

        return $this->render('update', ['settings' => $settings]);
    }
}

В приведенном выше коде мы используем indexBy() для извлечения моделей из базы данных для заполнения массива, индексированного первичными ключами моделей. Впоследствии они будут использоваться для определения полей формы. Model::loadMultiple() заполняет несколько моделей данными формы, поступающими из POST, и Model::validateMultiple() проверяет все модели сразу. Поскольку мы проверили наши модели раньше, используя validateMultiple(), мы теперь передаем false как параметр для save(), чтобы не запускать проверку дважды.

Теперь форму, которая находится в представлении update:

<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;

$form = ActiveForm::begin();

foreach ($settings as $index => $setting) {
    echo $form->field($setting, "[$index]value")->label($setting->name);
}

ActiveForm::end();

Здесь для каждого параметра мы отображаем имя и вход со значением. Важно добавить правильный индекс для имени входа, поскольку именно так Model::loadMultiple() определяет, какую модель заполнить, с какими значениями.

Создание динамического набора новых записей

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

public function actionCreate()
{
    $count = count(Yii::$app->request->post('Setting', []));
    $settings = [new Setting()];
    for($i = 1; $i < $count; $i++) {
        $settings[] = new Setting();
    }

    // ...
}

Здесь мы создаем начальный массив $settings, содержащий одну модель по умолчанию, так что в представлении всегда будет хотя бы одно текстовое поле. Кроме того, мы добавляем больше моделей для каждой строки ввода, которую мы, возможно, получили. В представлении вы можете использовать javascript для динамического добавления новых входных строк.