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

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

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

Сообщение onmotion »

Смотрите что происходит в afterSave(). Именно там происходит сохранение вашей реляции. unlinkAll все удаляет, потом link добавляет.
Igor346
Сообщения: 87
Зарегистрирован: 2016.09.13, 22:59

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

Сообщение Igor346 »

Вот такой debug:

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

public function afterSave($insert, $changedAttributes)
    {
        //обновляем связи с цветами
        $this->unlinkAll('colors', true);
        foreach($this->colors as $colorId){
            $color = Colors::findOne($colorId);
            $this->link('colors', $color);
        }
        debug($this->afterSave($insert, $changedAttributes));
        parent::afterSave($insert, $changedAttributes);
    }
приводит к такой ошибке:

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

Database Exception – yii\db\Exception
PDOStatement::execute(): MySQL server has gone away
The SQL being executed was: DELETE FROM `series_color` WHERE `serie_id`=26
↵
Caused by: yii\base\ErrorException
PDOStatement::execute(): MySQL server has gone away

in D:\OpenServer\domains\site.loc\vendor\yiisoft\yii2\db\Command.php at line 846
а приведенная ниже часть выводится около 800 раз :) Я где-то не там или как-то не так проверку делаю?

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


1. in D:\OpenServer\domains\site.loc\vendor\yiisoft\yii2\db\Schema.php at line 636


 
        $exceptionClass = '\yii\db\Exception';
        foreach ($this->exceptionMap as $error => $class) {
            if (strpos($e->getMessage(), $error) !== false) {
                $exceptionClass = $class;
            }
        }
        $message = $e->getMessage()  . "\nThe SQL being executed was: $rawSql";
        $errorInfo = $e instanceof \PDOException ? $e->errorInfo : null;
        return new $exceptionClass($message, $errorInfo, (int) $e->getCode(), $e); // - Здесь ругается
    }
 
    /**
     * Returns a value indicating whether a SQL statement is for read purpose.
     * @param string $sql the SQL statement
     * @return bool whether a SQL statement is for read purpose.
     */
    public function isReadQuery($sql)
    {

2. in D:\OpenServer\domains\site.loc\vendor\yiisoft\yii2\db\Command.php at line 856 – yii\db\Schema::convertException(yii\base\ErrorException, 'DELETE FROM `series_color` WHERE...')
3. in D:\OpenServer\domains\site.loc\vendor\yiisoft\yii2\db\ActiveRecord.php at line 296 – yii\db\Command::execute()
4. in D:\OpenServer\domains\site.loc\vendor\yiisoft\yii2\db\BaseActiveRecord.php at line 1452 – yii\db\ActiveRecord::deleteAll(['serie_id' => 26])
5. in D:\OpenServer\domains\site.loc\modules\secret\models\Series.php at line 63 – yii\db\BaseActiveRecord::unlinkAll('colors', true)
57585960616263646566676869

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

6. in D:\OpenServer\domains\site.loc\modules\secret\models\Series.php at line 70 – app\modules\secret\models\Series::afterSave(false, ['category_id' => 11, 'category_price' => 2, 'style' => 1, 'countries' => 1])
64656667686970717273747576

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

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

Сообщение onmotion »

Поздравляю, только что вы узнали что такое рекурсия.
Igor346
Сообщения: 87
Зарегистрирован: 2016.09.13, 22:59

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

Сообщение Igor346 »

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

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

Сообщение onmotion »

Уберите этот рекурсивный debug и выясните по какой причине не происходит сохранение в БД. Посмотрите как описана схема в БД и что вы туда пытаетесь записать.
Igor346
Сообщения: 87
Зарегистрирован: 2016.09.13, 22:59

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

Сообщение Igor346 »

Дебаг убрал. Про схему не совсем понял, но работает вообще так:
Поле id в таблице series и поле serie_id в таблице series_color связаны как hasMany; поле id в colors связано с полем color_id в таблице series_color так же hasMany. Руками когда данные вношу в series_color все работает. То, что прописал я можно увидеть в начале топика, то что посоветовали Вы я попробовал (сам не совсем понял, но в общих чертах понимаю, вроде все правильно там, color везде на colors заменил). Ну еще никакая связь с Series и Colors в модели SeriesColor не описана, я дописал но не знаю надо ли. Еще раз просмотрел имена полей в таблицах series, colors и series_color вроде правильно все.
Связь в моделях просмотрел:
Series

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

<?php
namespace app\models;
use yii\db\ActiveRecord;

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

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

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

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

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

<?php

namespace app\models;
use yii\db\ActiveRecord;

class Colors extends ActiveRecord
{

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

    public function getSeriesColor()
    {
        return $this->hasMany(SeriesColor::className(), ['color_id' => 'id']); // Здесь вместо color_id было serie_id
    }
    
    public function getSeries()
    {
        return $this->hasMany(Series::className(), ['id' => 'serie_id'])->via('seriesColor');
    }


}
SeriesColor

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

<?php

namespace app\models;
use yii\db\ActiveRecord;

class SeriesColor extends ActiveRecord
{
    public static function tableName()
    {
        return 'series_color';
    }
    
    public function getSeries()
    {
        return $this->hasMany(Series::className(), ['id' => 'serie_id']);
    }

    public function getColors()
    {
        return $this->hasMany(Colors::className(), ['id' => 'color_id']);
    }
}
В модели Colors нашел ошибку(вместо color_id было serie_id), но что удивительно работало все правильно. В остальном ошибок не увидел. Подскажите как посмотреть схему в БД и, что я пытаюсь туда записать?
Также выяснил, что unlink срабатывает, т.е. при обновлении серии где связь руками прописывал, связь из таблицы исчезла. Подозреваю, что в $this->colors что-то не то попадает или скорее ничего не попадает.

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

public function afterSave($insert, $changedAttributes)
    {
debug($this->colors);
        //обновляем связи с цветами
        $this->unlinkAll('colors', true);
        foreach($this->colors as $colorId){
            $color = Colors::findOne($colorId);
            $this->link('colors', $color);
           
        }
        parent::afterSave($insert, $changedAttributes);
    }
Дало:

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

Array
(
)
Хотя результат debug(Yii::$app->request->post()); приходит с выбранными цветами.
Аватара пользователя
onmotion
Сообщения: 31
Зарегистрирован: 2015.12.25, 16:03
Откуда: Санкт-Петербург
Контактная информация:

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

Сообщение onmotion »

В валидатор добавьте [['colors'], 'safe']
Igor346
Сообщения: 87
Зарегистрирован: 2016.09.13, 22:59

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

Сообщение Igor346 »

Вы не поверите! Заработало! Почти все, только во view в админской части, не выводится. Над этим подумаю еще. А в остальном все супер, цель достигнута, цвета добавляются и изменяются. СПАСИБО!
Igor346
Сообщения: 87
Зарегистрирован: 2016.09.13, 22:59

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

Сообщение Igor346 »

Если не затруднит подскажите где почитать как правильно выводить прилетевшие массивом цвета в DetailView и вообще как правильно в таком случае поступить? foreach'ем по model->colors внутри открытого php не получается пройтись, какое решение есть и в виде ли нужно это делать или необходимо подготовить данные в модели или контроллере и только вывести их в виде?
vasjaTikhonov
Сообщения: 1
Зарегистрирован: 2020.03.11, 19:32

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

Сообщение vasjaTikhonov »

верните свойство $colors , удалите сеттер, или переименнуйте его и добавьте в правила ['colors','safe'],я так понял что косяк в том что в модель не попадает значение
unknownby
Сообщения: 749
Зарегистрирован: 2019.11.05, 16:34
Контактная информация:

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

Сообщение unknownby »

Для сохранения в БД нужно (вначале не промежуточная таблица)
Во-1 чтобы была таблица с полями
Во-2 чтобы поля были определены в rules()

Чтобы сохранять в промежуточную через основную.
Во-1 повторяем условия выше
Во-2 должна быть связь в основной таблице к промежуточной, если табуляром сохраняешь, тогда используешь Behavior, где используешь связь с таблицей и название для атрибута

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

    public function behaviors()
    {
        return [
            'tabularInputBehavior' => [
                'class' => TabularInputBehavior::className(),
                'fields' => [
                    'my_tabular' => [
                        'myRelation',
                    ],
                ],
            ],
        ];
    }
В-3 поле my_tabular должно быть указано в rules как safe

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

public function rules()
    {
        return [
            [['my_tabular', ], 'safe'],
        ];
    }
    
Связь выглядит примерно так в модели

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

public function getMyRelation(){
        return $this->hasMany(MyTable::className(), ['mytable_id' => 'mytable_id'])->from([MyTable::TABLE_ALIAS => MyTable::tableName()]);
    }
В твоем проекте связь может выглядеть попроще

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

public function getMyRelation(){
        return $this->hasMany(MyTable::className(), ['mytable_id' => 'mytable_id']);
    }
Для табуляра можешь посмотреть unclead

Можно через список сохранять
ManyToManyBehavior by voskobovich
В модели

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

public function behaviors()
    {
        return [
            'manyToManyBehavior' => [
                'class' => ManyToManyBehavior::className(),
                'relations' => [
                    'my_list' => [
                        'myRelationList',
                    ],
                ],
            ],
        ];
    }
Так же в модели

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

public function getMyRelationList(){
        return $this->hasMany(MyModel::className(), ['my_id' => 'my_id'])->from([MyModel::TABLE_ALIAS => MyModel::tableName()])
            ->viaTable(RelationWithIntermediateTable::tableName(), ['my_intermediate_id' => 'my_intermediate_id']);
    }
MyModel - это модель, например, с цветами
RelationWithIntermediateTable - это модель, где соединяется цвет с записью в виде строки БД (my_intermediate_id = 1, my_id = 2 (твой цвет), id = 1 (идентификатор какой-то основной таблицы) )

В представлении используется Select2 от kartik-v

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

<?= $form->field($model, 'my_list')->widget(Select2::classname(), [
	'theme' => Select2::THEME_KRAJEE,
	'model' => $model,
	'data' => ArrayHelper::map(MyModel::find()->all(), 'my_id', 'my_filed'),
	'options' => [
		'multiple' => true,
	],
	'pluginOptions' => [
		'allowClear' => true,
		'closeOnSelect' => false,
		'placeholder' => \Yii::t('main', 'button.select') . ' ' . \Yii::t('main', 'MyModel'),
	],
]) ?>
И не забываем

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

public function rules()
    {
        return [
            [['my_list', ], 'safe'],
        ];
    }
    
Для сохранения информации можно пользоваться разными средствами. Чтобы не заниматься ерундой в afterSave и beforeSave можно использовать Behavior (очень упрощают разработку во многих случаях).
Если же обновление какого-то определенного поля по какой-либо связи, то проще будет через afterSave и beforeSave. Учитывай скорость разработки/доработки, простоту чтения кода и возможности масштабирования твоего кода.
Надеюсь будет полезна информация ;)
Аватара пользователя
Alexum
Сообщения: 683
Зарегистрирован: 2016.09.26, 10:00

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

Сообщение Alexum »

Зачем тратить столько сил для ответа на некропост? :?
Ответить