错误发生在控制器中我的更新操作的这一部分附近://从 POST 获取 Slots 数据$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);模型::loadMultiple($modelsSlots[$i], $loadsData);$newSlotIds = array_merge($newSlotIds, ArrayHelper::getColumn($loadsData['MissaoSlot'], 'id'));}在 Yii 日志上,我看到:无法在app\models\MissaoGrupo"中设置不安全的属性id"./var/www/html/qg_armapoint/modules/admin/controllers/MissaoController.php (134)第 134 行:Model::loadMultiple($modelsGrupos, Yii::$app->request->post());我已经按照谢谢!添加了 MissaoGrupo 模型namespace app\models;使用 Yii;/*** 这是表{{%missao_grupo}}"的模型类.** @property 整数 $id* @property 字符串 $nome* @property 整数 $missao_id** @property Missao $missao* @property MissaoSlot[] $missaoSlots*/类 MissaoGrupo 扩展了 \yii\db\ActiveRecord{/*** @inheritdoc*/公共静态函数 tableName(){返回'{{%missao_grupo}}';}/*** @inheritdoc*/公共函数规则(){返回 [[['名称'],'必需'],[['missao_id'], '整数'],[['nome'], 'string', 'max' =>[255]];}/*** @inheritdoc*/公共函数attributeLabels(){返回 ['id' =>'ID','名字' =>'集团','missao_id' =>'Missao ID',];}/*** @return \yii\db\ActiveQuery*/公共函数 getMissao(){返回 $this->hasOne(Missao::className(), ['id' => 'missao_id']);}/*** @return \yii\db\ActiveQuery*/公共函数 getMissaoSlots(){返回 $this->hasMany(MissaoSlot::className(), ['grupo_id' => 'id']);}} 解决方案 很简单.考虑这种情况:在代码中:$modelsGrupos = MissaoGrupo::findAll(['id' => $oldGrupoIds]);$modelsGrupos = (空($modelsGrupos)) ?[新的 MissaoGrupo]:$modelsGrupos;如果选择正确,则可能没有 id 等于2"的数据库记录.因为在下面的代码中://检索现有的插槽数据$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 个模型组,但没有一个的 id 等于 '2') for ($i=0; $i request->post()['MissaoSlot'][$i];$modelsSlots[$i] = Model::createMultiple(MissaoSlot::classname(), $modelsSlots[$i], $loadsData);模型::loadMultiple($modelsSlots[$i], $loadsData);$newSlotIds = array_merge($newSlotIds, ArrayHelper::getColumn($loadsData['MissaoSlot'], 'id'));}我建议将此代码更改为类似的内容:$i = 0;foreach ($modelsGrupos 作为 $id => $value) {$loadsData['MissaoSlot'] = Yii::$app->request->post()['MissaoSlot'][$i];$modelsSlots[$id] = Model::createMultiple(MissaoSlot::classname(), $modelsSlots[$id], $loadsData);模型::loadMultiple($modelsSlots[$id], $loadsData);$newSlotIds = array_merge($newSlotIds, ArrayHelper::getColumn($loadsData['MissaoSlot'], 'id'));$i++;}除此之外,您应该在模型的验证规则中添加 'id' 参数作为安全属性以消除日志文件中的错误:公共函数规则(){返回 [[['名称'],'必需'],[['missao_id'], '整数'],[['nome'], 'string', 'max' =>[255],[['id'],'安全']];}这个样本怎么样:$i = 0;foreach ($modelsGrupos 作为 $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);模型::loadMultiple($modelsSlots[$id], $loadsData);$newSlotIds = array_merge($newSlotIds, ArrayHelper::getColumn($loadsData['MissaoSlot'], 'id'));$i++;}Firstly sorry for my bad english!I'm using the Yii2-dynamicforms extension to nested forms in Yii 2.Following this guide, I was able to make Create Action work properly, but Update Action partially.Let me explain using my case...I can create a Mission (missao) with many Groups (grupo) and many Slots for each group;On Update, I can add some Slots for existing Groups, but when I try to add a new Group and new slots for this new Group, I got an error Undefined offset: 2 // for exampleThe error occurs near this section of my Update Action in 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')); }On Yii log, i see this: Failed to set unsafe attribute 'id' in 'app\models\MissaoGrupo'./var/www/html/qg_armapoint/modules/admin/controllers/MissaoController.php (134)The line 134:Model::loadMultiple($modelsGrupos, Yii::$app->request->post());I have reviewed all my code following his example and didn't find the error.Can anyone help me, please?My Update Action in 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;}app\base\Model 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; }} The view<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>Thank You!EDIT: Added MissaoGrupo Modelnamespace 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']); }} 解决方案 It's quite simple. Consider this scenario:In code:$modelsGrupos = MissaoGrupo::findAll(['id' => $oldGrupoIds]);$modelsGrupos = (empty($modelsGrupos)) ? [new MissaoGrupo] : $modelsGrupos;if it's properly selected there might be no DB record with id equal to '2'. Because of that in code below:// 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 will never be equal to '2' so there won't be $modelsSlots[2]. But in following code $i might have value of '2' so you have error that you try to use not existing array offset. (e.g. there are 5 modelsGroups but none of them has id equal to '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 advise to change this code to something like that:$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++; }Beside of that you should add 'id' parameter as safe attribute in Model's validation rules to get rid of error in log file:public function rules(){ return [ [['nome'], 'required'], [['missao_id'], 'integer'], [['nome'], 'string', 'max' => 255], [['id'], 'safe'] ];}EDIT:How about this sample:$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++; } 这篇关于Yii 2 嵌套表单的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
09-22 10:33