С версии 1.1.7 в ActiveRecord Yii доступен новый параметр для настройки отношений в моделях. Имя этого параметра — through. В этой заметке хочу показать один из примеров для чего этот параметр можно использовать. Сразу скажу, что я сам еще не до конца разобрался с этим, так что могу что-то напутать и где-то ошибаться.
Предположим, что у нас есть модель «Сотрудник» — Employee, модель «Проект» — Project. Сотрудник может участвовать во многих проектах и в одном проекте может участвовать много сотрудников, следовательно между проектом и сотрудиком отношение «многие ко многим» (MANY_MANY в терминах Yii). Для простоты предположим, что сотрудник, как и проект имеет всего два поля: id и name.
Для связки сотрудников и проектов введем третью таблицу «Employee_to_Project», в которой будем хранить id сотрудника (emp_id) и id проекта (proj_id). Такой простой таблички достаточно для установки простой связи. Теперь предположим, что каждый сотрудник в рамках каждого проекта должен иметь какую-то должность (для простоты это будет просто текстовое поле). Для хранения должности, в таблицу-связку добавим еще одно поле «position». Таким образом таблица-связка будет состоять из 3-х полей: emp_id, proj_id, position. С таблицами разобрались, переходим к моделям. Для краткости буду показывать только необходимые для примера методы моделей.
Модель Employee:
<?php class Employee extends CActiveRecord { public function relations() { return array( 'projects' => array(self::MANY_MANY, 'Project','Employee_to_Project("emp_id","proj_id")'), ); } } ?>
В этой моделе мы определили стандартное отношение MANY_MANY и теперь для получения пользователя со всеми его проектами необходимо выполнить вот такой код:
<?php $user = Employee::model()->with('projects')->findByPk($id); ?>
"$user->projects" — будет содержать массив моделей Project (проектов, в которых участвует пользователь)
Казалось бы все хорошо, но возникает вопрос: как получить роль сотрудника в каждом из проектов? Роль хранится в таблице-связке, а массив «projects» содержит массив моделей Project — следовательно через модель Project эту самую роль получить не получится. Вот тут нам на помощь и приходит новый параметр through.
Изменим нашу модель следующим образом:
<?php class Employee extends CActiveRecord { public function relations() { return array( 'projectsRell' => array(self::HAS_MANY, 'Project','EmployeeToProject'), 'projects' => array(self::HAS_MANY, 'Project','proj_id','through' => 'projectsRell'), ); } } ?>
Что мы тут сделали:
1 Добавили отношение «projectsRell» (EmployeeToProject — модель для табличики Employee_to_Project)
2 В отношении projects тип отношения изменили с MANY_MANY на HAS_MANY
3 В отношении projects указали параметр «through» со значением «projectsRell» (projectsRell — отношение, определенное в пункте 1)
Обратите внимание на:
1 Вместо отношения MANY_MANY везде используется HAS_MANY.
2 В отношении projects, которое указывает на таблицу/модель Project в качестве ключа использовано поле «proj_id». Это поле в таблице Project отсутствует, но оно есть в таблице/моделе EmployeeToProject, на которую мы и указываем параметром «through» (через запись 'through' => 'projectsRell')
Как этим пользоваться?
<?php $user = Employee::model()->with('projectsRell','projects')->findByPk($id); ?> <?php foreach($user->projectsRell as $rel):?> <?php echo $rel->position; // получаем роли в проектах?> <?php endforeach;?> <?php foreach($user->projects as $proj):?> <?php echo $proj->name; // получаем названия проектов ?> <?php endforeach;?>
Ссылки по теме:
www.yiiframework.com/doc/guide/1.1/en/database.arr#relational-query-with-through