Сохранение моделей со связями: AfterSave + link

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

Сохранение моделей со связями: AfterSave + link

Сообщение adamasantares »

Я только изучаю yii2 и столкнулся с такой вот проблемой.

Есть модель Post, и две связные модели PostTag и PostImage
Я пытаюсь сохранять все это таким вот способом:

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

    public function afterSave($insert)
    {
        // TAGS
        if (!$insert) { // remove oldest tag's relations
            $this->unlinkAll('tag', true);
        }
        $tags = array_map(
            function ($val) {
                return trim($val);
            },
            explode(',', $this->tags)
        );
        $tags = array_filter($tags);
        if (!empty($tags)) {
            foreach ($tags as $name) {
                $tag = Tag::find()->where(['name' => $name])->one();
                if (!$tag) {
                    $tag = new Tag();
                    $tag->name = $name;
                    $tag->save();
                }
                $this->link('tag', $tag); // Здесь возникает ошибка (но только при insert)
            }
        }

        // IMAGES
        // картинки по тому же принципу, но один ко многим

        parent::afterSave($insert);
    }
Так вот, при добавлении новой записи возникает ошибка
Unable to link models: both models must NOT be newly created.
Которая связана с тем, что до окончания AfretSave текущая запись Post считается еще не сохраненной. И все потому, что
в ActiveRecord->insertInternal() написано вот так:

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

$this->afterSave(true);
$this->setOldAttributes($values);
то есть $this->isNewRecord внутри afterSave() остается false, что подтверждается еще и комментом
https://github.com/yiisoft/yii2/issues/ ... t-24259542

:?: А теперь собственно вопрос, где правильнее было бы сохранять связи, если этого нельзя сделать в самой модели?
Делать последовательное сохранение моделей из контроллера не хотелось бы но пока других вариантов не вижу.
Megas
Сообщения: 4
Зарегистрирован: 2012.11.13, 15:50

Re: Сохранение моделей со связями: AfterSave + link

Сообщение Megas »

Недавно была статья в которой описываются связи, как раз на вашем примере
http://habrahabr.ru/post/226103/
Аватара пользователя
adamasantares
Сообщения: 8
Зарегистрирован: 2014.06.20, 09:59

Re: Сохранение моделей со связями: AfterSave + link

Сообщение adamasantares »

Спасибо за ответ,

но именно оттуда я пример и брал. Там аналогичная проблема $this->link('tags', $tag); внутри afterSave().
И где при $insert==true объект записи $this все еще isNewRecord и не может быть передан в метод link().
С обновлением записи проблем нет, так как запись уже существующая.

На данный момент я временно вынес код из afterSave в другой метод $this->saveRel() и вызываю его в контроллере так:

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

public function actionAdd()
{
$model = new Post();

if ($model->load(Yii::$app->request->post()) && $model->save()) {
    $model->saveRel(); // SAVE RELATIONS bad method
    return $this->redirect(['view', 'id' => $model->id]);
} else {
    return $this->render(
        'create',
        [
            'model' => $model,
        ]
    );
}
} 
но есть мнение, что это "дубовое" решение.

Возможно, вероятно, что я невнимательно прочитал ту статью и что-то не заметил. Прошу ткнуть пальцем, в статью разумеется.
codrilla
Сообщения: 173
Зарегистрирован: 2013.03.06, 12:24
Откуда: Молдова, Тирасполь

Re: Сохранение моделей со связями: AfterSave + link

Сообщение codrilla »

Точно с такой же проблемой сейчас столкнулся. И вы правы, та статья не даёт ответ на этот вопрос. Написал в разделе багов viewtopic.php?f=27&t=18510 Может что-то прояснится.
Megas
Сообщения: 4
Зарегистрирован: 2012.11.13, 15:50

Re: Сохранение моделей со связями: AfterSave + link

Сообщение Megas »

Да, действительно, статья не подсказывает решения (
И проблема действительно, в том что в функции link не обоснованно используется проверка на isNewRecord.
Необоснованно, на мой взгляд, потому что, для создании реляции, нам важно наличие первичного ключа (ключей) в моделях которые мы связываем, а не состояние модели (новая/старая). А первичный ключ уже в AfterSave присутствует.
Как возможный хак можно в aftersave запоминать состояние isNewRecord, принудительно его ставить в false, а затем после применения link возвращать как было.

А по хорошему надо создать багрепорт(pull request) на гитхабе и послушать разработчиков.
lynicidn
Сообщения: 2222
Зарегистрирован: 2014.05.24, 15:12

Re: Сохранение моделей со связями: AfterSave + link

Сообщение lynicidn »

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

 if (!$tag) {
                    $tag = new Tag();
                    $tag->name = $name;
                    $tag->save();
                }
                $this->link('tag', $tag); // Здесь возникает ошибка (но только при insert) 
ну так все верно вам пишет- че не понятного? 1. save может вернуть нул
lynicidn
Сообщения: 2222
Зарегистрирован: 2014.05.24, 15:12

Re: Сохранение моделей со связями: AfterSave + link

Сообщение lynicidn »

>Которая связана с тем, что до окончания AfretSave текущая запись Post считается еще не сохраненной.
а если сначала parent::afterSave() а потом вашу логику?
Аватара пользователя
adamasantares
Сообщения: 8
Зарегистрирован: 2014.06.20, 09:59

Re: Сохранение моделей со связями: AfterSave + link

Сообщение adamasantares »

lynicidn писал(а):

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

 if (!$tag) {
                    $tag = new Tag();
                    $tag->name = $name;
                    $tag->save();
                }
                $this->link('tag', $tag); // Здесь возникает ошибка (но только при insert)  
ну так все верно вам пишет- че не понятного? 1. save может вернуть нул
Да я кстати тоже думал об этом и проверял этот момент, сам тег сохраняется успешно.
Аватара пользователя
adamasantares
Сообщения: 8
Зарегистрирован: 2014.06.20, 09:59

Re: Сохранение моделей со связями: AfterSave + link

Сообщение adamasantares »

lynicidn писал(а):>Которая связана с тем, что до окончания AfretSave текущая запись Post считается еще не сохраненной.
а если сначала parent::afterSave() а потом вашу логику?
И так тоже пробовал. отследив логику выяснилось, что в базовом классе ActiveRecord нет ничего кроме вызова триггера, который не находя никаких подписанных событий, просто возвращает false.
К тому же происходит такой жизненный цикл:

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

$this->save()
    $this->insert()
         $this->insertInternal()
               $this->afterSave()
                      parent::afterSave() // return false;
               $this->afterSave() <- back
         $this->insertInternal()  <- back
         { а вот тут после возврата и сбрасывается флаг isNewRecord }
    $this->insert() <- back
$this->save()  <- back
то есть проще говоря, флаг сбрасывается после вызова aftersave нашего наследованного объекта, а не после вызова родительского, в последовательности все дело.
Аватара пользователя
adamasantares
Сообщения: 8
Зарегистрирован: 2014.06.20, 09:59

Re: Сохранение моделей со связями: AfterSave + link

Сообщение adamasantares »

codrilla писал(а):Точно с такой же проблемой сейчас столкнулся. И вы правы, та статья не даёт ответ на этот вопрос. Написал в разделе багов viewtopic.php?f=27&t=18510 Может что-то прояснится.
Спасибо, если ответят сообщите в теме пожалуйста :)
6pblcb
Сообщения: 71
Зарегистрирован: 2012.03.02, 11:24
Откуда: Omsk

Re: Сохранение моделей со связями: AfterSave + link

Сообщение 6pblcb »

YII2?, начинаю продолжать заканчивать делать. I'm gonna go build my own php-framework, with blackjack and hookers. In fact, forget the php-framework!
Ответить