Сохранение связанных моделей

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
codrilla
Сообщения: 173
Зарегистрирован: 2013.03.06, 12:24
Откуда: Молдова, Тирасполь

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

Сообщение codrilla »

vova07, спасибо за populateRelation
Переделал в итоге на

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

/**
     * Creates a new Car model.
     * If creation is successful, the browser will be redirected to the 'view' page.
     * @throws \yii\web\HttpException
     * @return mixed
     */
    public function actionCreate()
    {
        if (Yii::$app->user->can('createCar')) {
            $model = new Car;
            $modelPhoto = new Photo;

            if ($model->load(Yii::$app->request->post())) {
                $images = UploadedFile::getInstances($modelPhoto, 'image');
                $photos = [];

                foreach ($images as $image) {
                    $modelPhoto = new Photo;
                    $modelPhoto->image = $image;
                    $photos[] = $modelPhoto;
                }

                $model->populateRelation('photos', $photos);
                if ($model->save(false)) {
                    return $this->redirect(['view', 'id' => $model->id]);
                }
            } else {
                $modelPhoto = new Photo();
                return $this->render(
                    'create',
                    [
                        'model' => $model,
                        'modelPhoto' => $modelPhoto
                    ]
                );
            }
        } else {
            throw new HttpException('403', Yii::t('app', 'У вас нет прав для публикации объявлений'));
        }
    }

    /**
     * Updates an existing Car model.
     * If update is successful, the browser will be redirected to the 'view' page.
     * @param integer $id
     * @throws \yii\web\HttpException
     * @return mixed
     */
    public function actionUpdate($id)
    {
        $model = $this->findModel($id);
        if (Yii::$app->user->can('updateCar', ['car' => $model])) {

            $modelPhoto = new Photo;
            $modelsPhoto = $model->photos;

            if ($model->load(Yii::$app->request->post())) {
                $images = UploadedFile::getInstances($modelPhoto, 'image');
                $photos = [];

                foreach ($images as $image) {
                    $modelPhoto = new Photo;
                    $modelPhoto->image = $image;
                    $photos[] = $modelPhoto;
                }

                $model->populateRelation('photos', $photos);
                if ($model->save(false)) {
                    return $this->redirect(['view', 'id' => $model->id]);
                }
            } else {
                return $this->render(
                    'update',
                    [
                        'model' => $model,
                        'modelsPhoto' => $modelsPhoto,
                        'modelPhoto' => $modelPhoto
                    ]
                );
            }
        } else {
            throw new HttpException('403', Yii::t('app', 'У вас нет прав для редактирования данного объявления'));
        }
    }
 
Осталось добавить транзакции, так наверно будет правильнее. Не приведёте пример, как вы это делаете?
Аватара пользователя
vova07
Сообщения: 1004
Зарегистрирован: 2012.11.29, 14:52
Откуда: Chisinau, Moldova

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

Сообщение vova07 »

Штатными средсвами. Пример смотрите в комментах метода.
codrilla
Сообщения: 173
Зарегистрирован: 2013.03.06, 12:24
Откуда: Молдова, Тирасполь

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

Сообщение codrilla »

Благодарю.
lynicidn
Сообщения: 2222
Зарегистрирован: 2014.05.24, 15:12

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

Сообщение lynicidn »

codrilla писал(а):
mihail_dev писал(а):public function setPhotos($value){
$this->some = $value;
}
И что такое в моём контексте $this->some?
:lol: хороший вопрос
codrilla
Сообщения: 173
Зарегистрирован: 2013.03.06, 12:24
Откуда: Молдова, Тирасполь

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

Сообщение codrilla »

Спасибо за пост.
Аватара пользователя
Insolita
Сообщения: 788
Зарегистрирован: 2011.06.06, 01:39
Контактная информация:

Чёт ничего не выходит у меня... :-(

Сообщение Insolita »

Контроллер

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

public function actionCreate(){
        $model = new News();
        $cover = new Covers();
        $model->scenario='create';
        $cover->scenario='create';
        if(Yii::$app->request->isPost){
            $model->load(Yii::$app->request->post());
            $cover->image=UploadedFile::getInstance($cover,'image');
            if ($model->loadCover($cover) && $model->save()) {
                return (Yii::$app->request->isAjax)?['state'=>true,'error'=>'']:$this->redirect(Url::to(['index']));
            }else{
                return (Yii::$app->request->isAjax)?['state'=>false,'error'=>Helper::errorSummary([$model,$cover])]:$this->render('create',['model'=>$model,'cover'=>$cover]);
            }
        }elseif(Yii::$app->request->isAjax){
            return ['title'=>Helper::Fa('plus-circle', 'lg').' Добавить '.$model::modelTitle('vin'), 'body'=>$this->renderAjax('_form',['model'=>$model,'cover'=>$cover])];
        }else{
            return $this->render('create',['model'=>$model,'cover'=>$cover]);
        }
    }
 
Модель News

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

 /**
     * @return \yii\db\ActiveQuery
     */
    public function getCover()
    {
        return $this->hasOne(Covers::className(), ['id' => 'cover_id']);
    }

    public function setCover($cover)
    {
        $this->populateRelation('cover', $cover);
    }

    public function loadCover($cover)
    {
        if(!$this->nocover){
            if($cover->image->name){
                $fname=Helper::randomString().'.'.$cover->image->getExtension();
                $cover->filename=$fname;
                if($cover->validate()){
                    //$cover->save();
                    //$this->cover_id=$cover->id;
                    $this->setCover($cover);
                    return true;
                }else{
                    return false;
                }
            }
          return true;
        }
        return true;
    }


    public function beforeSave($insert){
        if($this->isNewRecord){
            if(!$this->publishto){
                $this->publishto=date('Y-m-d H:i:s',time());
            }

                $relatedRecords = $this->getRelatedRecords();
                Yii::info(Helper::dumps($relatedRecords),'spec');

                if (isset($relatedRecords['cover'])) {
                    $this->link('cover', $relatedRecords['cover']);
                }

        }
        return parent::beforeSave($insert);
    }
 
Получаю ошибку "Unable to link models: the primary key of insolita\content\models\Covers is null."
in /home/insolita/www/data/vg/vendor/yiisoft/yii2/db/BaseActiveRecord.php at line 1400

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


1391139213931394139513961397139813991400140114021403140414051406140714081409

     * @param ActiveRecordInterface $foreignModel
     * @param ActiveRecordInterface $primaryModel
     * @throws InvalidCallException
     */
    private function bindModels($link, $foreignModel, $primaryModel)
    {
        foreach ($link as $fk => $pk) {
            $value = $primaryModel->$pk;
            if ($value === null) {
                throw new InvalidCallException('Unable to link models: the primary key of ' . get_class($primaryModel) . ' is null.');
            }
            if (is_array($foreignModel->$fk)) { // relation via array valued attribute
                $foreignModel->$fk = array_merge($foreignModel->$fk, [$value]);
            } else {
                $foreignModel->$fk = $value;
            }
        }
        $foreignModel->save(false);
    }
 
in /home/insolita/www/data/vg/vendor/yiisoft/yii2/db/BaseActiveRecord.php – yii\db\BaseActiveRecord::bindModels(['cover_id' => 'id'], insolita\content\models\News, insolita\content\models\Covers) at line 1202
in /home/insolita/www/data/vg/modules/content/models/News.php – yii\db\BaseActiveRecord::link('cover', insolita\content\models\Covers) at line 173

короче на link этот самый ругается

(если делать без всяких линков а просто тупо сохранять cover и в эту модель подставлять id то все норм.) но в Дампе $relatedRecords действительно аттрибут id отсутствует - только 'filename' и 'image'

Модель Covers

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

class Covers extends \common\SActiveRecord
{ 
    public $image;
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return '{{%covers}}';
    }
 
    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['filename'], 'string'],
            ['image','image'],
            //['image','image','extensions'=>['jpg','jpeg','gif','png'], 'mimeTypes'=>['image/jpeg','image/gif','image/png']]
        ];
    }

    public function scenarios(){
        return [
        'default'=>['filename','image'],
        'create'=>['filename','image'],
        'update'=>['filename','image'], 
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'id' => 'ID',
            'filename' => 'Файл',
            'image' => 'Титульное изображение',
        ];
    }
    /**
     * @return \yii\db\ActiveQuery
     */
    public function getNews()
    {
        return $this->hasMany(News::className(), ['cover_id' => 'id']);
    }

    public function beforeSave($insert){ 
        return parent::beforeSave($insert);
    }

    public function afterSave($insert, $changedAttributes){
        $this->image->saveAs(Yii::getAlias(ContentModule::COVERS_ORIG_PATH).'/'.$this->filename);
        Yii::$app->image->load(Yii::getAlias(ContentModule::COVERS_ORIG_PATH).'/'.$this->filename)
                        ->resize(Yii::$app->params['thumb_article'],Yii::$app->params['thumb_article'],Yii\image\drivers\Image::AUTO)
                        ->save(Yii::getAlias(ContentModule::COVERS_PATH).'/'.$this->filename);
        return parent::afterSave($insert, $changedAttributes);
    }
}
 
ЧЯДНТ????????
Аватара пользователя
Insolita
Сообщения: 788
Зарегистрирован: 2011.06.06, 01:39
Контактная информация:

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

Сообщение Insolita »

:-( ничего не понимаю.. пытаюсь делать с тэгами как в примере, link в afterSave() ругается Unable to link models: both models must NOT be newly created.
Есть у кого еще можно рабочие примеры посмотреть... :-(( третий день уже бьюсь
Аватара пользователя
Insolita
Сообщения: 788
Зарегистрирован: 2011.06.06, 01:39
Контактная информация:

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

Сообщение Insolita »

ну понятно что isNewRecord true возвращает и оно считает модели новыми но вот в статье с примером http://habrahabr.ru/post/226103/ в afterSave теги сохраняют как-то... или это уже что-то новое с того поста в yii обновилось
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

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

Сообщение zelenin »

В примере такой код:

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

$post = new Post();
$post->link('category', new Category()); 
он работать не будет, т.к. линкуется, используя $post->id, а, как видно, сохранение еще даже не произошло.
Аватара пользователя
Insolita
Сообщения: 788
Зарегистрирован: 2011.06.06, 01:39
Контактная информация:

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

Сообщение Insolita »

не, там ниже пример модель и контроллер
Аватара пользователя
Insolita
Сообщения: 788
Зарегистрирован: 2011.06.06, 01:39
Контактная информация:

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

Сообщение Insolita »

Посмотрела issue явно поторопились его закрывать....
Я уже даже не в afterSave а в контроллере вынесла после сохранения модели прилинковку тегов -
там по-моему в логике не то чё-то
yii2/db/BaseActiveRecord.php

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

public function link($name, $model, $extraColumns = [])
    {
        $relation = $this->getRelation($name); 
        if ($relation->via !== null) {
            if ($this->getIsNewRecord() || $model->getIsNewRecord()) {
                throw new InvalidCallException('Unable to link models: both models must NOT be newly created.');
            } 
...............
}
 
оно выдаёт ошибку если либо сама модель новая либо связанные новые - так а связанные-то само собой новые - link их и должен сохранять по его предназначению свыше
короче какие-то сырые эти плюшки, либо совсем у меня руки не из того места растут, пока старыми проверенными костылями работать.
Аватара пользователя
Insolita
Сообщения: 788
Зарегистрирован: 2011.06.06, 01:39
Контактная информация:

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

Сообщение Insolita »

уфф ладно, теги таки победила, линкует оно только в pivot черновой пример модели такой

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

class News extends \common\SActiveRecord
{ 
    public $taglist;
    public $nocover=0;
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return '{{%news}}';
    }
    public function transactions()
    {
        return [ 
            'create' => self::OP_INSERT | self::OP_UPDATE,
            'update' => self::OP_INSERT | self::OP_UPDATE,
        ];
    } 
    .....
    /**
     * @return \yii\db\ActiveQuery
     */
    public function getTaggeds()
    {
        return $this->hasMany(Tagged::className(), ['contid' => 'id']);
    }

    /**
     * @return \yii\db\ActiveQuery
     */
    public function getTags()
    {
        return $this->hasMany(Tags::className(), ['tag_id'=>'tagid'])->via('taggeds');
    }

    public function setTags($tags)
    {
        $this->populateRelation('tags', $tags); 
    }
    public function prepareTags()
    {
            $tags=[];
            if(count($this->tags)){
                Tagged::deleteAll(['contid'=>$this->id]);

              /*foreach($this->tags as $oldtag){
                       $this->unlink('tags',$oldtag,true);
               }*/

            }
            if(!empty($this->taglist)){
                $taglist=explode(',',$this->taglist);
                foreach ($taglist as $name) {
                    $tag=Tags::findTag($name);
                    if($tag){
                        $tags[] = $tag;
                    }else{
                        $tag = new Tags();
                        $tag->scenario='create';
                        $tag->tagname = $name;
                        if($tag->save()){
                            $tags[] = $tag;
                        }
                    }
                }
            }

            $this->setTags($tags);
    }
 
    public function afterSave($insert, $changedAttributes){
        if($this->scenario=='update' or $this->scenario=='create'){
            $this->prepareTags();
        }
        $relatedRecords = $this->getRelatedRecords();
        if (isset($relatedRecords['tags'])) {
            foreach ($relatedRecords['tags'] as $tag) {
                $this->link('tags', $tag);
            }
        }
         parent::afterSave($insert, $changedAttributes);
    }

    public function afterFind(){
        $this->taglist=(isset($this->tags) && count($this->tags))?implode(',',\yii\helpers\ArrayHelper::getColumn($this->tags,'tagname',false)):'';
    }
    ....

}
В контроллере никаких телодвижений не требуется, добавляется и сохраняется норм... осталось теперь только с предварительным сохранением разобраться
Последний раз редактировалось Insolita 2014.07.30, 07:25, всего редактировалось 1 раз.
Аватара пользователя
Faenir
Сообщения: 292
Зарегистрирован: 2010.01.06, 01:46
Откуда: Симферополь

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

Сообщение Faenir »

Аватара пользователя
Insolita
Сообщения: 788
Зарегистрирован: 2011.06.06, 01:39
Контактная информация:

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

Сообщение Insolita »

Видела. Мне принципиально разобраться с новыми плюшками, с фактической реализацией проблем нет.
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

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

Сообщение slavcodev »

С тегами на хабре я пропустил сохранение тегов.
Правило при работе с link такое - Primary Key должны быть не пустыми, т.е. первичная модель должна быть заранее сохранена, т.к. линк занимается только линковкой (проставляет нужные FK и сохраняет этот FK)

Insolita, в твоем примере с cover, ты делаешь в beforeSave, когда news еще не сохранена.
В примере на хабре с тегами, т.е. с пивотной таблицей, оба и теги и пост должен быть сохранен до линковки, чтоб ИД обоих моделей уже были.
Жду Yii 3!
Аватара пользователя
Insolita
Сообщения: 788
Зарегистрирован: 2011.06.06, 01:39
Контактная информация:

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

Сообщение Insolita »

Да я поняла уже, что он только связи проставляет.(Нелохо бы подробнее этот нюанс на хабре осветить) Но штука такая в моём примере с cover сейчас такой код

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

public function setCover($cover)
    {
        $this->populateRelation('cover', $cover);
    }

    /**
     * @var \insolita\content\models\Covers $cover
     **/
    public function loadCover($cover)
    { 
        if($this->nocover){
            if($this->cover){
                /**Вот кстати еще затык - как зануллить значение - по отдельности не работает - работает такая связка**/
                $this->unlink('cover',$this->cover);
                $this->cover_id=new Expression('NULL');
            }
        }else{
            if(isset($cover->image->name)){
                Yii::info($cover->image->name,'spec');
                $fname=Helper::randomString().'.'.$cover->image->getExtension();
                $cover->filename=$fname;
                $cover->conttype='news';
                if($cover->save()){
                    $this->setCover($cover);
                }
                Yii::info('cover assigned'.Helper::dumps($this->cover),'spec');
                return true;
            }else{
                Yii::info('Нет загруженного изображения','spec');
                return true;
            }
        }
        return true;
    }


    public function beforeSave($insert){
        if($this->isNewRecord){
                if(!$this->publishto){
                    $this->publishto=date('Y-m-d H:i:s',time());
                }
            }
            $relatedRecords = $this->getRelatedRecords();
            Yii::info('beforasave','spec');
            if (isset($relatedRecords['cover'])) {
                $this->link('cover', $relatedRecords['cover']);
                //$this->cover_id=$relatedRecords['cover']->id;
            }
        return parent::beforeSave($insert);
    }
 
У меня метод beforeSave в бесконечный цикл уходит до лимитов php- вот там вот фразочка логируется - если не тормознуть - в логах появляется куча метров 'beforesave'.
Если напрямую id присвоить - закомментированную строчку использовать - то всё норм
Аватара пользователя
slavcodev
Сообщения: 3134
Зарегистрирован: 2009.04.02, 21:42
Откуда: Valencia
Контактная информация:

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

Сообщение slavcodev »

С unlink надо проверить, похоже на баг. Скинь плиз весь класс на pastebin.com, с подсветкой, а то сложно разобраться. Завтра попробуй еще раз c unlink, на версии из мастера.
Жду Yii 3!
Ответить