WithRelatedBehavior

Выкладываем свои наработки
Аватара пользователя
nizsheanez
Сообщения: 814
Зарегистрирован: 2011.04.29, 13:09
Откуда: Москва

Re: WithRelatedBehavior

Сообщение nizsheanez »

Решил вот так:

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

/**
     * @param $relations массив ($relation => $exclude), где $exclude - массив id объектов связи с которыми разрывать не нужно
     * @return bool
     */
    public function clear($relations)
    {
        $commands = array();
        foreach ($relations as $relation => $exclude)
        {
            list($table, $fk1, $fk2) = $this->parseManyMany($relation);
            $criteria = new CDbCriteria(array(
                'condition' => $fk1.'='.$this->owner->id,
            ));
            $criteria->addNotInCondition($fk2, $exclude);
            $commands[] = Yii::app()->db
                ->getCommandBuilder()
                ->createDeleteCommand($table, $criteria);
        }
        return $this->execInTransaction($commands);
    }

    public function execInTransaction($commands)
    {
        $flag = $transaction = Yii::app()->db->getCurrentTransaction();
        if ($transaction === null)
        {
            $transaction = Yii::app()->db->beginTransaction();
        }
        try
        {
            foreach ($commands as $c)
            {
                $c->execute();
            }
            //no commit, because withRelated save
        } catch (CException $e)
        {
            if ($flag !== null)
            {
                $transaction->rollback();
                return false;
            }
        }
        return true;
    }

    public function parseManyMany($relation)
    {
        preg_match('/^\s*(.*?)\((.*)\)\s*$/', $this->owner->getMetaData()->relations[$relation]->foreignKey, $matches);
        $fks = preg_split('/\s*,\s*/', $matches[2], -1, PREG_SPLIT_NO_EMPTY);
        return array(
            $matches[1],
            $fks[0],
            $fks[1]
        );
    }
 
использование

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

$this->withRelated->clear(array(
                    'categories' => $this->has_category,
                    'relevant_products' => $this->relevant_products_arr
                ));
 
Ну и вызываю clear где нужно, например в beforeSave
maschingan
Сообщения: 75
Зарегистрирован: 2011.01.20, 23:52

Re: WithRelatedBehavior

Сообщение maschingan »

+1. Было бы здорово, если бы удаление работало для HAS_MANY и MANY_MANY.
Аватара пользователя
resurtm
Сообщения: 299
Зарегистрирован: 2010.12.19, 09:13
Откуда: Казахстан, Алма-Ата
Контактная информация:

Re: WithRelatedBehavior

Сообщение resurtm »

Feature request поддерживаю. Ручками удалять как-то совсем некрасиво.
Аватара пользователя
creocoder
Сообщения: 138
Зарегистрирован: 2010.01.24, 05:29
Откуда: Тамбов

Re: WithRelatedBehavior

Сообщение creocoder »

Поведение предназначено для сохранения связанных моделей. Удаление связанных записей к сохранению связанных моделей никак не относится и является частным случаем применения стратегии обновления (которых может быть очень много и универсально эффективной для всех случаев не существует), поэтому должно быть описано строго явно. Это позволит избежать множества нежелательных побочных эффектов. Поэтому конструкция плана:

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

$post->tags=array();
$post->withRelated->save(true,array('tags'));
 
Не удалит записи.

В документации вскоре будет описана реализация собственных стратегий обновления (с удалением записей к примеру, с diff, etc). Вкратце: удаление должно быть реализовано в методе beforeSave() модели. Это наиболее оптимальный путь со всех точек зрения, при этом удаление будет происходить в рамках основной транзакции.

P.S. Также важно отметить, что сохранение связанных записей и линковка это 2 совершенно разных процесса. Да, второе может быть сделано в рамках первого. Но если нужна только линковка/делинковка, то делать это при помощи поведения для сохранения связанных не очень эффективно (выливается в большее, чем необходимо кол-во запросов). Этот функционал будет реализован в рамках другого поведения, либо войдет в это в качестве методов link()/unlink() (предварительно).
Аватара пользователя
nizsheanez
Сообщения: 814
Зарегистрирован: 2011.04.29, 13:09
Откуда: Москва

Re: WithRelatedBehavior

Сообщение nizsheanez »

Спасибо за развернутый ответ. Это и хотелось узнать - Что в планах, что не в планах. пока буду использовать самописный unlink(), а когда будет готовый вариант, перейдем на него "безболезненно".
Вообще, думаю, было бы здорово для столь полезного поведения завести страничку wiki с планами на будущее.
Аватара пользователя
nizsheanez
Сообщения: 814
Зарегистрирован: 2011.04.29, 13:09
Откуда: Москва

Re: WithRelatedBehavior

Сообщение nizsheanez »

Поведение всегда заново перелинковывает все объекты?
т.е. удаляет все связи, а потом заново сохраняет?
получается, что я не могу хранить ничего в связной таблице?
Аватара пользователя
creocoder
Сообщения: 138
Зарегистрирован: 2010.01.24, 05:29
Откуда: Тамбов

Re: WithRelatedBehavior

Сообщение creocoder »

Вот чтобы избежать этого и других побочных эффектов и будет link/unlink. Таким образом все будет под контролем, хотя в случае ассоциативных таблиц с доп. полями под такие таблицы следует иметь модель и пользоваться through.
Аватара пользователя
nizsheanez
Сообщения: 814
Зарегистрирован: 2011.04.29, 13:09
Откуда: Москва

Re: WithRelatedBehavior

Сообщение nizsheanez »

возможно вы правы.
Аватара пользователя
nizsheanez
Сообщения: 814
Зарегистрирован: 2011.04.29, 13:09
Откуда: Москва

Re: WithRelatedBehavior

Сообщение nizsheanez »

Еще одна вещь неприятная:
afterSave выполняется внутри транзакции, а значит там нельзя написать никакую логику зависящую от данных, которые должны были записаться в базу.
eax
Сообщения: 4
Зарегистрирован: 2012.02.16, 00:31

Re: WithRelatedBehavior

Сообщение eax »

крайне полезный экстеншен, но я так понял, что проблема с сохранением уже имеющихся записей - пока не решена ?
Аватара пользователя
creocoder
Сообщения: 138
Зарегистрирован: 2010.01.24, 05:29
Откуда: Тамбов

Re: WithRelatedBehavior

Сообщение creocoder »

eax писал(а):крайне полезный экстеншен, но я так понял, что проблема с сохранением уже имеющихся записей - пока не решена ?
Опишите более детально эту проблему тут https://github.com/yiiext/with-related-behavior/issues, если она имеет место быть.
eax
Сообщения: 4
Зарегистрирован: 2012.02.16, 00:31

Re: WithRelatedBehavior

Сообщение eax »

Прочитал все иссуии - и разобрался в своей проблеме, извеняюсь,что отнял время
Nafania
Сообщения: 1227
Зарегистрирован: 2011.01.31, 13:12

Re: WithRelatedBehavior

Сообщение Nafania »

Подскажите, из-за чего может быть ошибка при использовании этого поведения

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

Fatal error: Call to undefined method stdClass::save() in \extensions\yiiext\WithRelatedBehavior\WithRelatedBehavior.php on line 172
Nafania
Сообщения: 1227
Зарегистрирован: 2011.01.31, 13:12

Re: WithRelatedBehavior

Сообщение Nafania »

Все, нашел ошибку. Оказалось, что просто не была задана реляция, которую пытался сохранить.
Аватара пользователя
sequester
Сообщения: 95
Зарегистрирован: 2012.01.09, 16:20
Откуда: Донецк, Украина

Re: WithRelatedBehavior

Сообщение sequester »

А подскажите пожалуйста, почему тут

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

$post->tags=array($tag1,$tag2); 
у меня пишет

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

PHP warning

mb_strlen() expects parameter 1 to be string, array given
Вот весь код:

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

$image = new AImage();
$image->category_id = 1;
$image->owner_id = 3;
$image->width = 3;
$image->height = 3;
$needed_tags = array();

//добавим теги
foreach($needed_arr[0][1] as $tag)
{
    $t = new ATag();
    $t->name = $tag;
    $needed_tags[] = $t;
}

$image->tags = $needed_tags;

$color1=new AColor();
$color1->name='relation';
$color1->hex='relation';

$color2=new AColor();
$color2->name='save';
$color1->hex='relation';

$image->aColors = array($color1, $color2);
//$image->tags = '122';
//$image->colors = '122';


$image->withRelated->save(true, array('aTags', 'aColor'));

ec($image->getErrors());
 
Аватара пользователя
coder
Сообщения: 139
Зарегистрирован: 2010.04.09, 23:42
Откуда: Москва

Re: WithRelatedBehavior

Сообщение coder »

Простите, я всё же не понял как пользоваться расширением для связи MANY_MANY через ассоциативную таблицу. В примерах не нашел. Спасибо.
Аватара пользователя
creocoder
Сообщения: 138
Зарегистрирован: 2010.01.24, 05:29
Откуда: Тамбов

Re: WithRelatedBehavior

Сообщение creocoder »

sequester
Эта ошибка не связана с расширением.
coder
В примерах это есть. Раздел Usage, подраздел MANY_MANY.
Аватара пользователя
sequester
Сообщения: 95
Зарегистрирован: 2012.01.09, 16:20
Откуда: Донецк, Украина

Re: WithRelatedBehavior

Сообщение sequester »

creocoder писал(а):sequester
Эта ошибка не связана с расширением.
coder
В примерах это есть. Раздел Usage, подраздел MANY_MANY.
дада, я понял, спасибо за расширение.
Аватара пользователя
futbolim
Сообщения: 2051
Зарегистрирован: 2012.07.08, 19:28

Re: WithRelatedBehavior

Сообщение futbolim »

Здравствуйте, creocoder. Потрясающее расширение. Не подскажите на досуге ?
Есть 3 таблицы.
Untitled-1.jpg
Untitled-1.jpg (127.59 КБ) 7757 просмотров
Нужно добавить юзера (k_user) и линку на соц.сеть (k_link_user_social_net). Как мне быть, использовать отношение MANY_MANY или HAS_MANY.
Отношения User:

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

'kSocialNets' => array(self::MANY_MANY, 'SocialNet', '{{link_user_social_net}}(user_id, social_net_id)'),
'linkUserSocialNets'=>array(self::HAS_MANY, 'LinkUserSocialNet', 'user_id','joinType'=>'INNER JOIN'),
'socialNets'=>array(self::HAS_MANY, 'SocialNet', array('social_net_id'=>'id'),'through'=>'linkUserSocialNets','joinType'=>'INNER JOIN'),
 
Отношения LinkUserSocialNet:

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

'socialNetId' => array(self::BELONGS_TO, 'SocialNet', 'social_net_id'),
'userId' => array(self::BELONGS_TO, 'User', 'user_id'),
 
Заранее спасибо.
SpiLLeR
Сообщения: 350
Зарегистрирован: 2009.09.17, 16:47
Откуда: Санкт-Петербург
Контактная информация:

Re: WithRelatedBehavior

Сообщение SpiLLeR »

У вас юзер у которого много индектификаторов в соц. сетях = HAS_MANY. У вас же не может быть у одного индетификатора соц. сети куча юзеров?
Предупрежден - значит вооружен.
devKP.ru
Ответить