Yii2: Handling multiple models with a single form

Created on 30 Apr 2014  路  12Comments  路  Source: yiisoft/yii2

Possible use in actionCreate?

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]);
    }
docs

Most helpful comment

In controller:

public function actionCreate()
{
    $model = new LearnQuestion();
    // http://www.yiiframework.com/doc-2.0/guide-input-tabular-input.html
    $count = count(Yii::$app->request->post('LearnQuestionPart', []));
    $partModels = [new LearnQuestionPart()];
    for($i = 1; $i < $count; $i++) {
        $partModels[] = new LearnQuestionPart();
    }
    $post = Yii::$app->request->post();
    if ($model->load($post) && Model::loadMultiple($partModels, $post) && $model->save()) {
        $error = false;
        foreach ($partModels as $partModel) {
            $partModel->question_id = $model->id;
            if (!$partModel->save()) {
                $model->delete();
                $error = true;
                break;
            }
        }
        if (!$error) {
            return $this->redirect(['view', 'id' => $model->id]);
        }
    }
    return $this->render('create', [
        'model' => $model,
        'partModels' => $partModels
    ]);
}

In view:

        <? foreach($partModels as $index => $partModel): ?>
        <tr>
            <td>
                <?= $form->field($partModel, '['.$index.']description')->label(false)->textInput();?>
            </td>
            <td>
                <?= $form->field($partModel, '['.$index.']answer')->label(false)->checkbox([], false);?>
            </td>
        </tr>
        <? endforeach; ?>

$(function(){

$.fn.addRow = function() {
    var $table = $('table tbody', $(this));
    var $tr = $table.find('tr:last');
    var index = $table.find('tr').length - 2;
    var rowHtml = $tr.html().replace(/LearnQuestionPart\[\d+\]/, "LearnQuestionPart[" + (index + 1) + "]");
    $table.append('<tr>' + rowHtml + '</tr>');
}

});

Not user friendly.

All 12 comments

Please use our forum to ask questions. Thanks.

This is not a question for the forum, there is a way to use loadMultiple to update, not to create more, should have documentation or create some method in model.php for multiple forms using loadMultiple.

I see. I didn't understand your initial post. Re-opened for doc.

My example for two methods

public function actionCreate() {

    $model = [];
    $model[] = new Settings();

    $postData = Yii::$app->request->post();
    if ($postData) {
        foreach ($postData as $i => $single) {
            $model[$i] = new Settings();
            }
       }

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

            $validate = false;
            foreach ($model as $index => $setting) {
                /*valitade first form*/
                if($index==0){

                    if($setting->validate($setting->load(Yii::$app->request->post()))){
                        $validate = true;
                    }else{
                         /*return message validation here*/
                    }
                }

                if($validate==true){
                    if($setting->save(false)){
                        $save = true;
                    }
                }
            }

            if($save==true)
            {
                 /*return save message here*/
            }

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

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

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

            $validate = false;
            foreach ($model as $index => $setting) {
                /*valitade first form*/
                if($index==0)
                {
                    if($setting->validate($setting->load(Yii::$app->request->post()))){
                        $validate = true;
                    }else{
                         /*return message validation here*/
                    }
                }

                if($validate==true){
                    if($setting->save(false)){
                        $save = true;
                    }
                }
            }

            if($save==true)
            {
                /*return save message here*/
            }

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

This example is good for 2 or more models, great.

What about dinamic count of each model? It is posible?

@sieulog @StagnantIce please open new issues for this including more detailed description about your case.

In controller:

public function actionCreate()
{
    $model = new LearnQuestion();
    // http://www.yiiframework.com/doc-2.0/guide-input-tabular-input.html
    $count = count(Yii::$app->request->post('LearnQuestionPart', []));
    $partModels = [new LearnQuestionPart()];
    for($i = 1; $i < $count; $i++) {
        $partModels[] = new LearnQuestionPart();
    }
    $post = Yii::$app->request->post();
    if ($model->load($post) && Model::loadMultiple($partModels, $post) && $model->save()) {
        $error = false;
        foreach ($partModels as $partModel) {
            $partModel->question_id = $model->id;
            if (!$partModel->save()) {
                $model->delete();
                $error = true;
                break;
            }
        }
        if (!$error) {
            return $this->redirect(['view', 'id' => $model->id]);
        }
    }
    return $this->render('create', [
        'model' => $model,
        'partModels' => $partModels
    ]);
}

In view:

        <? foreach($partModels as $index => $partModel): ?>
        <tr>
            <td>
                <?= $form->field($partModel, '['.$index.']description')->label(false)->textInput();?>
            </td>
            <td>
                <?= $form->field($partModel, '['.$index.']answer')->label(false)->checkbox([], false);?>
            </td>
        </tr>
        <? endforeach; ?>

$(function(){

$.fn.addRow = function() {
    var $table = $('table tbody', $(this));
    var $tr = $table.find('tr:last');
    var index = $table.find('tr').length - 2;
    var rowHtml = $tr.html().replace(/LearnQuestionPart\[\d+\]/, "LearnQuestionPart[" + (index + 1) + "]");
    $table.append('<tr>' + rowHtml + '</tr>');
}

});

Not user friendly.

Was this page helpful?
0 / 5 - 0 ratings