Task. There are 3 tables in the database: event , bundle , event_bundle . The event_bundle table links event and bundle tables. That is, there are such columns: id , event_id , bundle_id . It turns out the relationship of many to many.

It is necessary on the update/create page for the event display all bundle via the ActiveForm checkboxlist. Give the ability to choose the bundle to which the current event attached.

My code is:

action from controller:

 public function actionUpdate($id) { $model = $this->findModel($id); $model->setScenario(Event::SCENARIO_UPDATE); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } return $this->render('update', [ 'model' => $model, 'bundleList' => ArrayHelper::map(Bundle::find()->all(), 'id', 'name'), ]); } 

my _form.php

 <?php $form = ActiveForm::begin(); ?> <?= $form->field($model, 'title')->textInput(['maxlength' => true]) ?> <?= $form->field($model, 'description')->textarea(['rows' => 6]) ?> <?= $form->field($model, 'is_active')->dropDownList([0 => 'No', 1 => 'Yes']) ?> <?= $form->field($model, 'bundles') ->checkboxList($bundleList, ['separator' => '<br />']) ?> <div class="form-group"> <?= Html::submitButton('Save', ['class' => 'btn btn-success']) ?> </div> <?php ActiveForm::end(); ?> 

and model (basic, I keep in common \ models):

 class Event extends \yii\db\ActiveRecord { /** * @inheritdoc */ public static function tableName() { return 'event'; } /** * @inheritdoc */ public function rules() { return [ [['title'], 'required'], [['description'], 'string'], [['is_active'], 'integer'], [['title'], 'string', 'max' => 256], ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => 'ID', 'title' => 'Title', 'description' => 'Description', 'is_active' => 'Is Active', ]; } /** * @return \yii\db\ActiveQuery */ public function getEventBundles() { return $this->hasMany(EventBundle::className(), ['event_id' => 'id']); } /** * @return \yii\db\ActiveQuery */ public function getOrders() { return $this->hasMany(Order::className(), ['event_id' => 'id']); } /** * @return \yii\db\ActiveQuery */ public function getTimetables() { return $this->hasMany(Timetable::className(), ['event_id' => 'id']); } } 

in the backend, I inherit the previous model and its code is:

 class Event extends \common\models\Event { const SCENARIO_EDIT = 'eventEdit'; const SCENARIO_UPDATE = 'eventUpdate'; const SCENARIO_CREATE = 'eventCreate'; private $bundles = array(); public function rules() { $rules = parent::rules(); $rules[] = ['bundles', 'each', 'rule' => ['integer']]; return $rules; } public function afterSave($insert, $changedAttributes) { EventBundle::deleteAll(['event_id' => $this->id]); foreach ($this->bundles as $bundle) { $eventBundle = new EventBundle([ 'event_id' => $this->id, 'bundle_id' => $bundle, ]); $eventBundle->save(); } } public function setBundles($value) { $this->bundles = $value; } /** * @return \yii\db\ActiveQuery */ public function getBundles() { // TODO узнать, можно ли сразу получить массив без обхода и избавиться от него $result = $this->hasMany(EventBundle::className(), ['event_id' => 'id'])->select(['bundle_id'])->asArray()->all(); $bundles = []; foreach ($result as $item) { $bundles[] = $item['bundle_id']; } return $bundles; } public function scenarios() { return [ self::SCENARIO_EDIT => ['title', 'is_active'], self::SCENARIO_UPDATE => ['title', 'description', 'is_active', 'bundles'], self::SCENARIO_CREATE => ['title', 'description', 'is_active', 'bundles'], ]; } } 

Problem. When saving, a validation error occurs:

Bundles must be an integer.

I looked and realized that when I save the $model->save() model, validation begins. And the Validator calls the setBundles method in the Event class and for some reason, in $value , it passes each of the elements of the array, which it accepted to $_POST , and not the array itself ... And as I understood it was the reason for this.

I have 2 options:

  1. Or I do not know how setters and getters work in Yii.
  2. Or I do not know how EachValidator works.

Please help.

1 answer 1

instead of private $bundles = array(); try public $bundlesData = []; Well, in other places to change.

Well, instead of 'bundleList' => ArrayHelper::map(Bundle::find()->all(), 'id', 'name'), I would do this logic in afterFind

 public function afterFind(){ parent::afterFind(); $this->bundlesData = ArrayHelper::map($this->bundles, 'id', 'name'); } 

In getBundles() should be a connection, and not something that you have there now