Yii2 multiple models in one form

jflizandro picture jflizandro · Jan 26, 2016 · Viewed 18.1k times · Source

How to use multiple models in one form in Yii2?

My case: ER diagram

In my create action I can save into agenda_fiscalizacao table, but in update I receive this error when I try to load the form:

Call to a member function formName() on array    

My Update Action:

public function actionUpdate($id)
{
    $model = $this->findModel($id);
    $modelAgenda = AgendaFiscalizacao::findAll(['fiscalizacao_id' => $id]);

    if ($model->load(Yii::$app->request->post()) && Model::loadMultiple($modelAgenda, Yii::$app->request->post())) {
        $valid = $model->validate();
        $valid = $modelAgenda->validade() && $valid;

        if ($valid) {
            $model->save(false);
            $modelAgenda->save(false);
            return $this->redirect(['view', 'id' => $model->id]);
        }
    }

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

My form view

<?= $form->field($modelAgenda, 'agenda_id')->checkboxList(Agenda::combo(), ['class' => 'checkbox']) ?>
<?= $form->field($model, 'bioma_id')->dropDownList(Bioma::combo(), ['prompt' => $prompt]) ?>
<?= $form->field($model, 'nome')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'tipo_doc')->radioList(['CPF'=>'CPF', 'CNPJ'=>'CNPJ'], ['class' => 'radio']) ?>
<?= $form->field($model, 'n_doc')->widget(MaskedInput::className(), ['mask' => ['999.999.999-99', '99.999.999/9999-99']]) ?>
<?= $form->field($model, 'observacao')->textarea(['rows' => 7]) ?>    

What could be wrong?

EDIT (full error):

enter image description here

Answer

arogachev picture arogachev · Jan 26, 2016

1) If you mean working with multiple models of the same type, the error is in this line:

$valid = $modelAgenda->validade() && $valid;

First, it should be $modelAgenda->validate(), second $modelAgenda contains array of models, validate() method can be called only on single model.

For validating multiple models Yii2 suggests using built-in method validateMultiple():

use yii\base\Model;

...

$valid = Model::validateMultiple($modelAgenda) && $valid;

Working with multiple models is well covered in official docs (Collecting Tabular Input).

Note that they recommend to index models array by id before like this:

$models = YourModel::find()->index('id')->all();

2) If you need just two models of different type, don't use findAll() because it's for finding multiple models and always returns array (even on empty result). Use new for create action and findOne() for update action to initialize models. Let's say you initialized two models, $firstModel and $secondModel, then you can load and save them like this:

$isSuccess = false;

Yii::$app->db->transaction(function () use ($isSuccess) {
    $areLoaded = $firstModel->load(Yii::$app->request->post()) && $secondModel->load(Yii::$app->request->post();
    $areSaved = $firstModel->save() && $secondModel->save();
    $isSuccess = $areLoaded && $areSaved;
});

if ($isSuccess) {
    return $this->redirect(['view', 'id' => $model->id]);
}

Transaction is added in case of saving of second model will fail (so the first model also will not be saved).

Alternatively, you can declare transactions inside your model, for example:

return [
    'admin' => self::OP_INSERT,
    'api' => self::OP_INSERT | self::OP_UPDATE | self::OP_DELETE,
    // the above is equivalent to the following:
    // 'api' => self::OP_ALL,
];

Then just use:

$firstModel->scenario = 'scenarioForTransaction';
$secondModel->scenario = 'scenarioForTransaction';
$areLoaded = $firstModel->load(Yii::$app->request->post()) && $secondModel->load(Yii::$app->request->post();
$areSaved = $firstModel->save() && $secondModel->save();

if ($areLoaded && $areSaved) {
    return $this->redirect(['view', 'id' => $model->id]);
}

For more than two models, it's better to use loops.

P.S. I'd recommend to separate saving to different controllers / actions and call it via AJAX, it will be more user friendly.

For saving relations read - Saving Relations.