Запись в промежуточную таблицу

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Igor346
Сообщения: 87
Зарегистрирован: 2016.09.13, 22:59

Запись в промежуточную таблицу

Сообщение Igor346 »

Как правильно оформить запись в промежуточную таблицу?
Есть таблицы Series и Colors, связаны как hasMany через промежуточную таблицу SeriesColor. В форме добавления серий получаю в поле цвета, через

Код: Выделить всё

<?=$form->field($model, 'color')->listBox(ArrayHelper::map(Colors::find()->all(), 'id', 'name'),
            [
                'multiple' => true,

            ]);
?>
В форме выбрать могу, т.е. ползадачи выполнил, а вот записать не могу. Потому, что нигде и не прописал ничего про то, что надо записать, потому, что не знаю куда и как нужно записывать, подозреваю в контроллер осталось дописать, но что именно не знаю. Насколько понимаю, записать нужно в промежуточную таблицу, т.к. связь в ней по id серий и цветов. Подскажите как правильно организовать запись.

Модель

Код: Выделить всё

<?php

namespace app\modules\secret\models;

use Yii;
use app\models\CategoriesPrice;
use app\models\Styles;
use app\models\Country;
use app\models\Colors;
use app\models\SeriesColor;
use yii\db\ActiveRecord;
use app\models\Products;

class Series extends ActiveRecord
{

    public function behaviors()
    {
        return [
            'image' => [
                'class' => 'rico\yii2images\behaviors\ImageBehave',
            ]
        ];
    }

    public $image;
    public $gallery;
    public $color;

    public static function tableName()
    {
        return 'series';
    }


    public function rules()
    {
        return [
            [['category_id', 'title', 'keywords', 'description', 'content', 'material', 'thickness'], 'string'],
            [['name', 'category_id', 'title', 'keywords', 'description', 'category_price', 'material', 'style', 'countries'], 'required'],
            [['category_price', 'style', 'countries'], 'integer'],
            [['name'], 'string', 'max' => 80],
            [['img'], 'string', 'max' => 200],
            [['image'], 'file', 'extensions' => 'jpg, png, gif, tiff'],
            [['gallery'], 'file', 'extensions' => 'jpg, png, gif, tiff', 'maxFiles' => 30],
        ];
    }

    public function getCategories()
    {
        return $this->hasOne(Categories::className(), ['id' => 'category_id']);
    }

    public function getCategoriesPrice()
    {
        return $this->hasOne(CategoriesPrice::className(), ['id' => 'category_price']);
    }

    public function getStyles()
    {
        return $this->hasOne(Styles::className(), ['id' => 'style']);
    }

    public function getCountry()
    {
        return $this->hasOne(Country::className(), ['id' => 'countries']);
    }

    public function getSeriesColor()
    {
        return $this->hasMany(SeriesColor::className(), ['serie_id' => 'id']);
    }

    public function getColors()
    {
        return $this->hasMany(Colors::className(), ['id' => 'color_id'])->via('seriesColor');
    }
    
    public function getProducts()
    {
        return $this->hasMany(Products::className(), ['serie_id' => 'id']);
    }
    
    

    public function attributeLabels()
    {
        return [
            'id' => '№',
            'title' => 'Заголовок',
            'keywords' => 'Ключевики',
            'description' => 'Мета описание',
            'content' => 'Описание серии',
            'name' => 'Название',
            'category_id' => 'Категория',
            'color' => 'Цвет',
            'category_price' => 'Ценовая категория',
            'material' => 'Используемые материалы',
            'style' => 'Стиль',
            'thickness' => 'Толщина деталей',
            'countries' => 'Страна производства',
            'image' => 'Картинка',
            'gallery' => 'Множественная загрузка изображений (< 30 шт)',
        ];
    }

    public function upload(){
        if ($this->validate()){
            $path = 'upload/store/' . $this->image->baseName . '.' . $this->image->extension;
            $this->image->saveAs($path);
            $this->attachImage($path, true);
            @unlink($path);
            return true;
        }
        else{
            return false;
        }
    }

    public function uploadGallery(){
        if ($this->validate()){
            foreach ($this->gallery as $file){
                $path = 'upload/store/' . $file->baseName . '.' . $file->extension;
                $file->saveAs($path);
                $this->attachImage($path);
                @unlink($path);
            }

            return true;
        }
        else{
            return false;
        }
    }
    
}

Контроллер

Код: Выделить всё

<?php

namespace app\modules\secret\controllers;


use Yii;
use app\models\Colors;
use app\modules\secret\models\Series;
use yii\data\ActiveDataProvider;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use yii\web\UploadedFile;


class SeriesController extends Controller
{
    
    public function behaviors()
    {
        return [
            'verbs' => [
                'class' => VerbFilter::className(),
                'actions' => [
                    'delete' => ['POST'],
                ],
            ],
        ];
    }


    public function actionIndex()
    {

        $dataProvider = new ActiveDataProvider([
            'query' => Series::find()->with('categories', 'colors'),

        ]);



        return $this->render('index', [
            'dataProvider' => $dataProvider,

        ]);
    }

    
    public function actionView($id)
    {
        return $this->render('view', [
            'model' => $this->findModel($id),

        ]);
    }

    public function actionCreate()
    {
        $model = new Series();

 if ($model->load(Yii::$app->request->post()) && $model->save()) {

            $model->image = UploadedFile::getInstance($model, 'image');
            if ($model->image){
                $model->upload();
            }

            unset($model->image);
            $model->gallery = UploadedFile::getInstances($model, 'gallery');
            $model->uploadGallery();

            Yii::$app->session->setFlash('info', "Серия {$model->name} создана.");
            return $this->redirect(['view', 'id' => $model->id]);

        } else {

//            Yii::$app->session->setFlash('info', 'Ошибка');
           
            
            return $this->render('create', [
                'model' => $model,
            ]);
        }
  }


    public function actionUpdate($id)
    {
        $model = $this->findModel($id);

  if ($model->load(Yii::$app->request->post()) && $model->save()) {
            
            $model->image = UploadedFile::getInstance($model, 'image');
            if ($model->image){
                $model->upload();
            }

            unset($model->image);
            $model->gallery = UploadedFile::getInstances($model, 'gallery');
            $model->uploadGallery();

   Yii::$app->session->setFlash('info', "Серия {$model->name} обновлена.");
            return $this->redirect(['view', 'id' => $model->id]);
        } else {
            return $this->render('update', [
                'model' => $model,
            ]);
        }
    }


    public function actionDelete($id)
    {
        $model = $this->findModel($id);
        $this->findModel($id)->delete();
        Yii::$app->session->setFlash('info', "Серия {$model->name} удалена.");
        return $this->redirect(['index']);
    }


    protected function findModel($id)
    {
        if (($model = Series::findOne($id)) !== null) {
            return $model;
        } else {
            throw new NotFoundHttpException('The requested page does not exist.');
        }
    }   
}

Вид-форма

Код: Выделить всё

<?php

use yii\helpers\Html;
use yii\bootstrap\ActiveForm;
use yii\helpers\ArrayHelper;
use app\modules\secret\models\Categories;
use app\models\Colors;
use app\models\CategoriesPrice;
use app\models\Country;
use app\models\Styles;
use mihaildev\ckeditor\CKEditor;
use mihaildev\elfinder\ElFinder;
use app\models\SeriesColor;
use app\models\Series;

mihaildev\elfinder\Assets::noConflict($this);
?>

<div class="series-form">

    <?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]); ?>

    <?= $form->field($model, 'name')->textInput(['maxlength' => true]) ?>

    <?= $form->field($model, 'content')->widget(CKEditor::className(),[
        'editorOptions' => ElFinder::ckeditorOptions(['elfinder', 'path' => 'some/sub/path'],[/* Some CKEditor Options */]),
    ]); ?>

    <?= $form->field($model, 'title')->textInput(['maxlength' => true]) ?>

    <?= $form->field($model, 'keywords')->textInput(['maxlength' => true]) ?>

    <?= $form->field($model, 'description')->textarea(['rows' => 3]) ?>

    <?= $form->field($model, 'category_id')->dropDownList(ArrayHelper::map(Categories::find()->all(), 'id', 'name')) ?>

    <?= $form->field($model, 'category_price')->dropDownList(ArrayHelper::map(CategoriesPrice::find()->all(), 'id', 'name')) ?>

    <?=$form->field($model, 'color')->listBox(ArrayHelper::map(Colors::find()->all(), 'id', 'name'),
            [
                'multiple' => true,
//                'prompt' => 'Выберите один или несколько вариантов',
//                'style' => 'background:gray;color:#fff;'
            ]);
    ?>

    <?= $form->field($model, 'material')->textarea(['rows' => 6]) ?>

    <?= $form->field($model, 'thickness')->textarea(['rows' => 6]) ?>

    <?= $form->field($model, 'style')->dropDownList(ArrayHelper::map(Styles::find()->all(), 'id', 'name')) ?>

    <?= $form->field($model, 'countries')->dropDownList(ArrayHelper::map(Country::find()->all(), 'id', 'name')) ?>

    <?= $form->field($model, 'image')->fileInput() ?>

    <?= $form->field($model, 'gallery[]')->fileInput(['multiple' => true, 'accept' => 'image/*']) ?>

    <div class="form-group">
        <?= Html::submitButton($model->isNewRecord ? 'Создать' : 'Обновить', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
    </div>

    <?php ActiveForm::end(); ?>

</div>

Аватара пользователя
onmotion
Сообщения: 31
Зарегистрирован: 2015.12.25, 16:03
Откуда: Санкт-Петербург
Контактная информация:

Re: Запись в промежуточную таблицу

Сообщение onmotion »

У вас выведено даже неверно, откуда в главной модели colors, если они получаются через реляцию $model->colors. После того как переделаете у вас в POST будет прилетать модель с цветами, которую вы сможете загрузить, например через ->load(\Yii::$app->request->post()), конечно перед этим создав или найдя эту модель.

Для того чтобы проще понять как это работает, создайте внешние ключи в БД, а потом сгенерируйте GridView с помощью https://github.com/mootensai/yii2-enhanced-gii он вам создаст все что нужно с реляциями, поймете как это работает и сделаете как вам нужно.
Igor346
Сообщения: 87
Зарегистрирован: 2016.09.13, 22:59

Re: Запись в промежуточную таблицу

Сообщение Igor346 »

Не совсем понял о каком colors в главной моделе вы говорите,
далее:
onmotion писал(а): 2017.04.29, 10:36 После того как переделаете у вас в POST будет прилетать модель с цветами, которую вы сможете загрузить, например через ->load(\Yii::$app->request->post()), конечно перед этим создав или найдя эту модель.
Имеете ввиду переделаю создав внешние ключи в БД, а потом сгенерирую GridView с помощью https://github.com/mootensai/yii2-enhanced-gii?
Огромная просьба пояснить про внешние ключи. Я правильно понял, что создаются они командой yii migrate/create в консоли? Помогите разобраться в этом процессе т.к. миграции я использовал только один раз при установке CostaRico/yii2-images. В какую таблицу мне нужно сгенерировать поля и на какую? В Series на Colors или в SeriesColor на Series и на Colors или как-то иначе?
Аватара пользователя
onmotion
Сообщения: 31
Зарегистрирован: 2015.12.25, 16:03
Откуда: Санкт-Петербург
Контактная информация:

Re: Запись в промежуточную таблицу

Сообщение onmotion »

Igor346 писал(а): 2017.04.29, 19:09 Имеете ввиду переделаю создав внешние ключи в БД, а потом сгенерирую GridView с помощью https://github.com/mootensai/yii2-enhanced-gii?
Верно.
Igor346 писал(а): 2017.04.29, 19:09 Я правильно понял, что создаются они командой yii migrate/create в консоли?
Вы конечно можете написать это миграцией, но зачем? Сделайте это в БД. Просто добавьте к существующей схеме.
Igor346
Сообщения: 87
Зарегистрирован: 2016.09.13, 22:59

Re: Запись в промежуточную таблицу

Сообщение Igor346 »

Так, что добавить? Сейчас есть связь через промежуточную, напрямую 2 таблицы связать вроде не получается, так как hasMany в обоих. Вы как предлагаете сделать?
Аватара пользователя
onmotion
Сообщения: 31
Зарегистрирован: 2015.12.25, 16:03
Откуда: Санкт-Петербург
Контактная информация:

Re: Запись в промежуточную таблицу

Сообщение onmotion »

1. Добавить внешние ключи.
2. Воспользоваться генератором.

Либо сделать все руками, модель у вас уже есть.
Попробуйте так:
Поправьте

Код: Выделить всё

<?=$form->field($model, 'color[b]s[/b]')->listBox(ArrayHelper::map(Colors::find()->all(), 'id', 'name'),
            [
                'multiple' => true,
            ]);
    ?>
В модели добавьте

Код: Выделить всё

public function setColors($values)
    {
       return $this->colors = $values;
    }
И там же

Код: Выделить всё

 public function afterSave($insert, $changedAttributes)
    {
        //обновляем связи с цветами
        $this->unlinkAll('colors', true);
        foreach($this->colors as $colorId){
            $color = Colors::findOne($colorId);
            $this->link('colors', $color);
        }
        parent::afterSave($insert, $changedAttributes);
    }
Igor346
Сообщения: 87
Зарегистрирован: 2016.09.13, 22:59

Re: Запись в промежуточную таблицу

Сообщение Igor346 »

Добавил, поправил, ничего не изменилось.
Аватара пользователя
onmotion
Сообщения: 31
Зарегистрирован: 2015.12.25, 16:03
Откуда: Санкт-Петербург
Контактная информация:

Re: Запись в промежуточную таблицу

Сообщение onmotion »

Простите, но телепаты в отпуске. Поставьте xdebug и посмотрите что приходит с post запросом в экшн, есть ли какие-нибудь ошибки при валидации.
Igor346
Сообщения: 87
Зарегистрирован: 2016.09.13, 22:59

Re: Запись в промежуточную таблицу

Сообщение Igor346 »

xdebug я не ставил, тестирую на локальном сервере, вот результат проверки debug(Yii::$app->request->post());

Array
(
[_csrf] => QUxRUTJKSWwHPDxkVj0wOXMNOBlhBwcnDAAiGXl9GyYLdR4gWiMtHw==
[Series] => Array
(
[name] => Serie 1
[content] =>

Serie description


[title] => Serie 1
[keywords] => Serie
[description] => Description
[category_id] => 11
[category_price] => 1
[color] => Array
(
[0] => 1
[1] => 2
[2] => 4
[3] => 7
)

[material] => Materials
[thickness] => 25 mm
[style] => 1
[countries] => 1
[image] =>
[gallery] => Array
(
[0] =>
)

)

)

после обновления серии

Код: Выделить всё

public function actionUpdate($id)
    {
        $model = $this->findModel($id);



        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            
            $model->image = UploadedFile::getInstance($model, 'image');
            if ($model->image){
                $model->upload();
            }

            unset($model->image);
            $model->gallery = UploadedFile::getInstances($model, 'gallery');
            $model->uploadGallery();

            debug(Yii::$app->request->post());

            Yii::$app->session->setFlash('info', "Серия {$model->name} обновлена.");

            return $this->redirect(['view', 'id' => $model->id]);

        } else {
            return $this->render('update', [
                'model' => $model,
            ]);
//            $color = Colors::findOne($model);

        }
    }
Вроде выбранные цвета есть.

Немного ошибся, в вашем варианте попадает только последний выбранный цвет подозреваю из-за

Код: Выделить всё

color[b]s[/b]
, но после исправления не поменялось ничего. Показывает тоже, что и в моем случае было.

Если я правильно понял, что нужно было выяснить. Или я все не так понял и нужно именно xdebug'ом смотреть?
Аватара пользователя
onmotion
Сообщения: 31
Зарегистрирован: 2015.12.25, 16:03
Откуда: Санкт-Петербург
Контактная информация:

Re: Запись в промежуточную таблицу

Сообщение onmotion »

Уберите теги из

Код: Выделить всё

color[b]s[/b].
Должно быть colors. Я выделял жирным букву s, чтобы вы обратили внимание что нужно исправить, тег не сработал, я не заметил.
Igor346
Сообщения: 87
Зарегистрирован: 2016.09.13, 22:59

Re: Запись в промежуточную таблицу

Сообщение Igor346 »

Я понял, сначала каюсь, скопировал как есть. Но потом написал, что поправил, результат debug'a написал. В post() выбранные цвета попадают, а дальше нет. Посоветуйте где еще искать причину.
Аватара пользователя
onmotion
Сообщения: 31
Зарегистрирован: 2015.12.25, 16:03
Откуда: Санкт-Петербург
Контактная информация:

Re: Запись в промежуточную таблицу

Сообщение onmotion »

В вашем выводе в POST попадает color а не colors, сначала исправьте это.
Аватара пользователя
futbolim
Сообщения: 2051
Зарегистрирован: 2012.07.08, 19:28

Re: Запись в промежуточную таблицу

Сообщение futbolim »

Igor346 писал(а): 2017.04.30, 12:10 Я понял, сначала каюсь, скопировал как есть
В этом и проблема. Думать не хочется...
Igor346
Сообщения: 87
Зарегистрирован: 2016.09.13, 22:59

Re: Запись в промежуточную таблицу

Сообщение Igor346 »

futbolim писал(а): 2017.04.30, 12:56
Igor346 писал(а): 2017.04.30, 12:10 Я понял, сначала каюсь, скопировал как есть
В этом и проблема. Думать не хочется...
Не вижу смысла оправдываться, но как бы нет, вы не правы. Проблема есть, но она не в лени, а в отсутствие понимания как это работает, понимание приходит по мере продвижения вперед, это даже по моим вопросам видно, от элементарных к чуть более сложным.
Igor346
Сообщения: 87
Зарегистрирован: 2016.09.13, 22:59

Re: Запись в промежуточную таблицу

Сообщение Igor346 »

onmotion писал(а): 2017.04.30, 12:42 В вашем выводе в POST попадает color а не colors, сначала исправьте это.
Исправил
public $image;
public $gallery;
public $color; на public $colors;
и
'color' => 'Цвет', на 'colors' => 'Цвет',
Вылетает ошибка
Invalid argument supplied for foreach()

Код: Выделить всё

        return $this->colors = $values;
    }
 public function afterSave($insert, $changedAttributes)
    {
        //обновляем связи с цветами
        $this->unlinkAll('colors', true);
        foreach($this->colors as $colorId){   //- ругается на эту строку
            $color = Colors::findOne($colorId);
            $this->link('colors', $color);
        }
        parent::afterSave($insert, $changedAttributes);
    }
    public static function tableName()
Аватара пользователя
onmotion
Сообщения: 31
Зарегистрирован: 2015.12.25, 16:03
Откуда: Санкт-Петербург
Контактная информация:

Re: Запись в промежуточную таблицу

Сообщение onmotion »

Уберите это свойство вообще, оставьте только геттер и сеттер
Igor346
Сообщения: 87
Зарегистрирован: 2016.09.13, 22:59

Re: Запись в промежуточную таблицу

Сообщение Igor346 »

Array
(
[_csrf] => MjBMT0xsaU50QCF6KBsQGwBxJQcfIScFf3w/BwdbOwR4CQM.JAUNPQ==
[Series] => Array
(
[name] => Serie 1
[content] => Serie description
[title] => Serie 1
[keywords] => Serie
[description] => Description
[category_id] => 11
[category_price] => 1
[colors] => Array
(
[0] => 1
[1] => 3
)
[material] => Materials
[thickness] => 25 mm
[style] => 1
[countries] => 1
[image] =>
[gallery] => Array
(
[0] =>
)
)
)

Убрал, попадают в colors, но дальше ничего не происходит, не записывается.
Аватара пользователя
onmotion
Сообщения: 31
Зарегистрирован: 2015.12.25, 16:03
Откуда: Санкт-Петербург
Контактная информация:

Re: Запись в промежуточную таблицу

Сообщение onmotion »

Модель то сохраняется?
Igor346
Сообщения: 87
Зарегистрирован: 2016.09.13, 22:59

Re: Запись в промежуточную таблицу

Сообщение Igor346 »

Все остальное обновляется. Может в контроллере описать, что-то нужно?
Ответить