php - Yii 2 嵌套形式

标签 php yii2

首先抱歉我的英语不好!

我正在使用Yii2-dynamicforms extension到 Yii 2 中的嵌套表单。
正在关注this guide ,我能够使创建操作正常工作,但部分更新操作。

让我用我的案例来解释一下...... EER Model

我可以创建一个包含多个组 (grupo) 的任务 (missao),并为每个组创建多个槽位;
在更新时,我可以为现有组添加一些插槽,但是当我尝试添加新组并为这个新组添加新插槽时,我收到错误 Undefined offset: 2//例如

Controller 中更新操作的此部分附近发生错误:

// get Slots data from POST
$newSlotIds = [];
$loadsData['_csrf'] =  Yii::$app->request->post()['_csrf'];
for ($i=0; $i < count($modelsGrupos); $i++) {
    $loadsData['MissaoSlot'] =  Yii::$app->request->post()['MissaoSlot'][$i];
    // ERROR POINTS TO THE LINE BELOW
    $modelsSlots[$i] = Model::createMultiple(MissaoSlot::classname(), $modelsSlots[$i], $loadsData);
    Model::loadMultiple($modelsSlots[$i], $loadsData);
    $newSlotIds = array_merge($newSlotIds, ArrayHelper::getColumn($loadsData['MissaoSlot'], 'id'));
 }

在 Yii 日志上,我看到这个:

Failed to set unsafe attribute 'id' in 'app\models\MissaoGrupo'.
/var/www/html/qg_armapoint/modules/admin/controllers/MissaoController.php (134)

第 134 行:
Model::loadMultiple($modelsGrupos, Yii::$app->request->post());

我已经检查了 his example 之后的所有代码并没有发现错误。
请问有人可以帮我吗?

Controller 中我的更新操作:

use app\models\Missao;
use app\models\MissaoGrupo;
use app\models\MissaoSlot;
use app\base\Model;
use yii\helpers\ArrayHelper;

/**
........
*/
public function actionUpdate($id)
{
    // retrieve existing Missao (mission) data
    $model = $this->findModel($id);

    // retrieve existing Grupos (groups) data
    $oldGrupoIds = MissaoGrupo::find()->select('id')->where(['missao_id' => $id])->asArray()->all();
    $oldGrupoIds = ArrayHelper::getColumn($oldGrupoIds,'id');
    $modelsGrupos = MissaoGrupo::findAll(['id' => $oldGrupoIds]);
    $modelsGrupos = (empty($modelsGrupos)) ? [new MissaoGrupo] : $modelsGrupos;

    // retrieve existing Slots data
    $oldSlotIds = [];
    foreach ($modelsGrupos as $i => $modelGrupo) {
        $oldSlots = MissaoSlot::findAll(['grupo_id' => $modelGrupo->id]);
        $modelsSlots[$i] = $oldSlots;
        $oldSlotIds = array_merge($oldSlotIds, ArrayHelper::getColumn($oldSlots, 'id'));
        $modelsSlots[$i] = (empty($modelsSlots[$i])) ? [new MissaoSlot()] : $modelsSlots[$i];
    }

    // handle POST
    if ($model->load(Yii::$app->request->post())) {

        // get Groups data from POST
        $modelsGrupos = Model::createMultiple(MissaoGrupo::classname(), $modelsGrupos);
        Model::loadMultiple($modelsGrupos, Yii::$app->request->post());
        $newGrupoIds = ArrayHelper::getColumn($modelsGrupos, 'id');

        // get Slots data from POST
        $newSlotIds = [];
        $loadsData['_csrf'] =  Yii::$app->request->post()['_csrf'];
        for ($i=0; $i < count($modelsGrupos); $i++) {
            $loadsData['MissaoSlot'] =  Yii::$app->request->post()['MissaoSlot'][$i];
            $modelsSlots[$i] = Model::createMultiple(MissaoSlot::classname(), $modelsSlots[$i], $loadsData);
            Model::loadMultiple($modelsSlots[$i], $loadsData);
            $newSlotIds = array_merge($newSlotIds, ArrayHelper::getColumn($loadsData['MissaoSlot'], 'id'));
        }

        // delete removed data
        $delSlotIds = array_diff($oldSlotIds, $newSlotIds);
        if (! empty($delSlotIds)) MissaoSlot::deleteAll(['id' => $delSlotIds]);
        $delGrupoIds = array_diff($oldGrupoIds, $newGrupoIds);
        if (! empty($delGrupoIds)) MissaoGrupo::deleteAll(['id' => $delGrupoIds]);

        // validate all models
        $valid = $model->validate();
        $valid = $this->validaMissao($modelsGrupos, $modelsSlots) && $valid;

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

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

protected function saveMissao($modelMissao, $modelsGrupos, $modelsSlots)
{
    $transaction = \Yii::$app->db->beginTransaction();
    try {
        if ($go = $modelMissao->save(false)) {

            foreach ($modelsGrupos as $i => $modelGrupo) {

                $modelGrupo->missao_id = $modelMissao->id;
                if ($go = $modelGrupo->save(false)) {

                    foreach ($modelsSlots[$i] as $j => $modelSlot) {

                        $modelSlot->grupo_id = $modelGrupo->id;
                        if (! ($go = $modelSlot->save(false))) {
                            $transaction->rollBack();
                            break;
                        }

                    }

                }
            }

        }
        if ($go) {
            $transaction->commit();
        }
    } catch (Exception $e) {
        $transaction->rollBack();
    }

    return $go;
}

应用程序\基础\模型

namespace app\base;

use Yii;
use yii\helpers\ArrayHelper;

class Model extends \yii\base\Model
{
    /**
     * Creates and populates a set of models.
     *
     * @param string $modelClass
     * @param array $multipleModels
     * @return array
     */
    public static function createMultiple($modelClass, $multipleModels = [], $data = null)
    {
        $model    = new $modelClass;
        $formName = $model->formName();
        $post     = empty($data) ? Yii::$app->request->post($formName) : $data[$formName];
        $models   = [];

        if (! empty($multipleModels)) {
            $keys = array_keys(ArrayHelper::map($multipleModels, 'id', 'id'));
            $multipleModels = array_combine($keys, $multipleModels);
        }

        if ($post && is_array($post)) {
            foreach ($post as $i => $item) {
                if (isset($item['id']) && !empty($item['id']) && isset($multipleModels[$item['id']])) {
                    $models[] = $multipleModels[$item['id']];
                } else {
                    $models[] = new $modelClass;
                }
            }
        }

        unset($model, $formName, $post);

        return $models;
    }
}    

景色

<div role="tabpanel" class="tab-pane" id="aba-slots">
        <br/>
        <div class="panel panel-default">
            <div class="panel-body">
                <?php DynamicFormWidget::begin([
                    'widgetContainer' => 'dynamicform_wrapper',
                    'widgetBody' => '.container-grupos',
                    'widgetItem' => '.grupo',
                    //'limit' => 4,
                    'min' => 1,
                    'insertButton' => '.add-grupo',
                    'deleteButton' => '.remove-grupo',
                    'model' => $modelsGrupos[0],
                    'formId' => 'frm_missao',
                    'formFields' => [
                        'nome',
                    ],
                ]); ?>
                <div class="panel panel-default">
                    <div class="panel-heading">Grupos / Slots</div>
                    <div class="panel-body container-grupos">
                        <?php foreach ($modelsGrupos as $i => $modelGrupo): ?>
                        <div class="row grupo"> <!-- Item Grupo - INICIO -->
                            <table class="table">
                                <tr>
                                    <th style="width:5%">#</th>
                                    <th>Grupo</th>
                                    <th>Slot</th>
                                </tr>
                                <tr>
                                    <td><button class="btn btn-danger remove-grupo"><i class="fa fa-minus"></i></button></td>
                                    <td class="col-md-4">
                                        <?php
                                        // necessary for update action.
                                        if (! $modelGrupo->isNewRecord) {
                                            echo Html::activeHiddenInput($modelGrupo, "[{$i}]id");
                                        }
                                        ?>
                                        <?= $form->field($modelGrupo, "[{$i}]nome")->textInput(['maxlength' => true, 'placeholder' => 'Nome do Grupo'])->label(false) ?>
                                    </td>
                                    <td><!-- Slots -->
                                        <?php DynamicFormWidget::begin([
                                            'widgetContainer' => 'dynamicform_inner',
                                            'widgetBody' => '.container-slots',
                                            'widgetItem' => '.slot',
                                            //'limit' => 10,
                                            'min' => 1,
                                            'insertButton' => '.add-slot',
                                            'deleteButton' => '.remove-slot',
                                            'model' => $modelsSlots[$i][0],
                                            'formId' => 'frm_missao',
                                            'formFields' => [
                                                'nome',
                                            ],
                                        ]); ?>
                                        <table class="table container-slots">
                                            <?php foreach ($modelsSlots[$i] as $j => $modelSlot): ?>
                                            <tr class="slot">
                                                <td>
                                                    <?php
                                                    // necessary for update action.
                                                    if (! $modelSlot->isNewRecord) {
                                                        echo Html::activeHiddenInput($modelSlot, "[{$i}][{$j}]id");
                                                    }
                                                    ?>
                                                    <?= $form->field($modelSlot, "[{$i}][{$j}]nome")->textInput(['maxlength' => true, 'placeholder' => 'Nome do Slot'])->label(false) ?>
                                                </td>
                                                <td style="width:5%"><button class="btn btn-danger btn-xs remove-slot"><i class="fa fa-minus"></i></button></td>
                                            </tr>
                                            <?php endforeach; ?>
                                            <tfoot>
                                            <tr>
                                                <td colspan="2"><button class="btn btn-info btn-xs add-slot"><i class="fa fa-plus"></i> Novo Slot</button></td>
                                            </tr>
                                            </tfoot>
                                        </table>
                                        <?php DynamicFormWidget::end(); ?>
                                    </td><!--Slots - fim -->
                                </tr>
                            </table>
                        </div> <!-- Item Grupo - FIM -->
                        <?php endforeach; ?>
                    </div>
                    <div class="panel-footer">
                        <button class="btn btn-primary btn-xs add-grupo"><i class="fa fa-plus"></i> Novo Grupo</button>
                    </div>
                </div>
                <?php DynamicFormWidget::end(); ?>
            </div>
        </div>
    </div>

enter image description here

谢谢!


编辑:添加了 MissaoGrupo 模型

namespace app\models;

use Yii;

/**
 * This is the model class for table "{{%missao_grupo}}".
 *
 * @property integer $id
 * @property string $nome
 * @property integer $missao_id
 *
 * @property Missao $missao
 * @property MissaoSlot[] $missaoSlots
 */
class MissaoGrupo extends \yii\db\ActiveRecord
{
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return '{{%missao_grupo}}';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['nome'], 'required'],
            [['missao_id'], 'integer'],
            [['nome'], 'string', 'max' => 255]
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'nome' => 'Grupo',
            'missao_id' => 'Missao ID',
        ];
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getMissao()
    {
        return $this->hasOne(Missao::className(), ['id' => 'missao_id']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getMissaoSlots()
    {
        return $this->hasMany(MissaoSlot::className(), ['grupo_id' => 'id']);
    }
}

最佳答案

这很简单。考虑这种情况:

在代码中:

$modelsGrupos = MissaoGrupo::findAll(['id' => $oldGrupoIds]);
$modelsGrupos = (empty($modelsGrupos)) ? [new MissaoGrupo] : $modelsGrupos;

如果选择正确,可能不会有 id 等于“2”的数据库记录。因为下面的代码:

// retrieve existing Slots data
$oldSlotIds = [];
foreach ($modelsGrupos as $i => $modelGrupo) {
    $oldSlots = MissaoSlot::findAll(['grupo_id' => $modelGrupo->id]);
    $modelsSlots[$i] = $oldSlots;
    $oldSlotIds = array_merge($oldSlotIds, ArrayHelper::getColumn($oldSlots, 'id'));
    $modelsSlots[$i] = (empty($modelsSlots[$i])) ? [new MissaoSlot()] : $modelsSlots[$i];
}

$i 永远不会等于“2”,因此不会有 $modelsSlots[2]。但在下面的代码中 $i 的值可能为“2”,因此您尝试使用不存在的数组偏移量时会出现错误。 (例如,有 5 个 modelsGroup,但没有一个 id 等于“2”)

    for ($i=0; $i < count($modelsGrupos); $i++) {
        $loadsData['MissaoSlot'] =  Yii::$app->request->post()['MissaoSlot'][$i];
        $modelsSlots[$i] = Model::createMultiple(MissaoSlot::classname(), $modelsSlots[$i], $loadsData);
        Model::loadMultiple($modelsSlots[$i], $loadsData);
        $newSlotIds = array_merge($newSlotIds, ArrayHelper::getColumn($loadsData['MissaoSlot'], 'id'));
    }

我建议将此代码更改为类似的内容:

$i = 0;
foreach ($modelsGrupos as $id => $value) {
        $loadsData['MissaoSlot'] =  Yii::$app->request->post()['MissaoSlot'][$i];
        $modelsSlots[$id] = Model::createMultiple(MissaoSlot::classname(), $modelsSlots[$id], $loadsData);
        Model::loadMultiple($modelsSlots[$id], $loadsData);
        $newSlotIds = array_merge($newSlotIds, ArrayHelper::getColumn($loadsData['MissaoSlot'], 'id'));
        $i++;
    }

除此之外,您应该在模型的验证规则中添加“id”参数作为安全属性,以消除日志文件中的错误:

public function rules()
{
    return [
        [['nome'], 'required'],
        [['missao_id'], 'integer'],
        [['nome'], 'string', 'max' => 255],
        [['id'], 'safe']
    ];
}

编辑:

这个示例怎么样:

$i = 0;
foreach ($modelsGrupos as $id => $value) {
        $loadsData['MissaoSlot'] =  Yii::$app->request->post()['MissaoSlot'][$i];
        if (!isset($modelsSlots[$id])
            $modelsSlots[$id] = [new MussaoSlot()];
        $modelsSlots[$id] = Model::createMultiple(MissaoSlot::classname(), $modelsSlots[$id], $loadsData);
        Model::loadMultiple($modelsSlots[$id], $loadsData);
        $newSlotIds = array_merge($newSlotIds, ArrayHelper::getColumn($loadsData['MissaoSlot'], 'id'));
        $i++;
    }

关于php - Yii 2 嵌套形式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32585832/

相关文章:

php - html表中的多个foreach循环

php - WordPress - 在子类别 slug 上显示自定义分类法父类别

yii2 - 如何在 Gridview 中将 Pjax 与自定义按钮一起使用

php - 登录用户访问其他用户配置文件

php - filter_var vs htmlentities vs htmlspecialchars

php - 剪掉10个字后的内容

javascript - Yii2:比较表格/数组输入中的开始和结束时间

php - MySQL 或 Yii2 ActiveRecord

file-upload - 上传后 Yii2 文件验证不起作用

php - 未找到 Yii2 类