问题描述
限时送ChatGPT账号..首先很抱歉我的英语不好!
我在 Yii 2 中使用
我可以创建一个包含许多组 (grupo) 和每个组的许多插槽的任务 (missao);
在更新时,我可以为现有组添加一些插槽,但是当我尝试为这个新组添加新组和新插槽时,出现错误 Undefined offset: 2//例如
>
错误发生在控制器中我的更新操作的这一部分附近:
//从 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 example
The 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 Model
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']);
}
}
解决方案
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 嵌套表单的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
更多推荐
[db:关键词]
发布评论