WithRelatedBehavior или работа со связью MANY_MANY

08.10.2012

Схема связей таблиц в базе данных

За последний месяц сразу несколько человек спрашивали как организовать работу со связью MANY_MANY, вот Вам ответочка ;)

Репозиторий на гитхабе - https://github.com/neo-classic/many_many

01. Создаем базу данных

Работу со связями MANY_MANY разберем на примере следующей БД: пускай у нас есть таблица пользователей и каждый пользователь может отсылать сообщение нескольким другим пользователям. Таким образом у нас получается 3 таблицы: user, message и user_message_link. Получаем что связь с таблицей user_message_link будет M_M.Дамп базы можно посмотреть в /protected/data/many_many.sql

02. Генерируем приложение Yii и подключаем Behavior

Создаем приложение, как делаем обычно (настраиваем подключение к БД) и качаем сам бехавиор отсюда: http://yiiext.github.com/extensions/with-related-behavior/readme.ru.html.

На станице есть хорошее описание возможностей расширения. Распаковываем в /protected/extensions/withRelated.

Теперь давайте генерируем наши модели: User и Message, а для третьей таблицы модель ненужна, подключаем бехавиор для этих моделей:

public function behaviors() {
    return array(
        'withRelated'=>array(
            'class'=>'ext.withRelated.WithRelatedBehavior',
        ),
    );
}

Прописываем отношения для модели Message:

public function relations() {
    return array(
        'users' => array(self::MANY_MANY, 'User', 'user_message_link(user_id, message_id)'),
    );
}

И для модели User:

public function relations() {
    return array(
        'messages' => array(self::MANY_MANY, 'Message', 'user_message_link(user_id, message_id)'),
    );
}

03. Сохраняем модели

Добавим несколько пользователей и создаем для них сообщение. Чтобы сохранить сообщение для выбранных пользователей, нам нужны их AR-объекты. Пускай мы хотим добавить сообщение для 1,3 и 4 пользователя, для этого выберем их:

public function actionCreateMessage() {
    // Выбираем нужных нам пользователей
    $criteria = new CDbCriteria();
    $criteria->addInCondition('id', array(1,3,4));
    $users = User::model()->findAll($criteria);
    // Создаем новый объект сообщение
    $message = new Message();
    $message->title = 'WithRelatedBehavior';
    $message->description = 'This behavior allows you to validate, insert, update and save a model along with models from its relations. It supports all relation types. All DB queries are wrapped into transactions automatically but there\'s a support for manual transaction handling. Composite keys are supported as well.';
    // И теперь определим пользователей, для которых сообщение
    $message->users = $users;
    // А теперь сохраняем
    if($message->withRelated->save(true, array('users'))) {
        echo "Models saved.";
    }
}

Проверим что получилось: Таблица message

Таблица Message

И user_message_link:

Таблица user_message_list

Как видим всё отлично сохранилось!

04. Удаление реляций

По моему мнению это отличный бехавиор, я им пользуюсь везде, но у него есть 1 недостаток — он не умеет удалять связанные данные. Но в правильно спроектированной базе это и ненужно:

  1. Правильно устанавливаем каскадное обновление и удаление для внешних ключей таблицы с которой у нас
  2. Если уж нельзя выставить ключи — используем afterDelete в модели из которой удаляем.

P.s.: Обратите внимание, что в таблице user_message_link заданы внешние ключи.

blog comments powered by Disqus
Наверх