Инструменты пользователя

Инструменты сайта


limb3_2007_2:ru:usage:active_record:one_to_many

Поддержка отношений вида один-ко-многим

Определение отношения

Рассмотрим связь вида один-ко-многим на примере двух классов - курс (Course ) и набор лекций, из которых он состоит (Lecture). В итоге имеем отношение Course has many Lectures.

Объекты этих классов хранятся в таблицах соответственно course и lecture:

CREATE TABLE `course` (
 `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
 `title` VARCHAR(255) DEFAULT NULL,
 PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
CREATE TABLE `lecture` (
  `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
  `title` VARCHAR(255) DEFAULT NULL,
  `course_id` BIGINT(20) DEFAULT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Обратите внимание на поле course_id в таблице lecture. Это внешний ключ на поле id таблицы course. Получается, что поле, которые хранит связь, находится в таблице подчиненного объекта. В этом заключается отличие от связи "Один-к-одному", где связь хранится в поле таблицы главного (родительского) объекта.

Связь один-ко-многим в классах lmbActiveRecord описывается атрибутами $_has_many того класса, который является главным в связи и $_many_belongs_to того класса, который является подчиненным. В нашем случае классы Course и Lecture будут выглядеть следующим образом:

class Course extends lmbActiveRecord
{
  protected $_has_many = array('lectures' => array('field' => 'course_id',
                                                   'class' => 'Lecture'));
}
 
class Lecture extends lmbActiveRecord
{
  protected $_many_belongs_to = array('course' => array('field' => 'course_id',
                                                        'class' => 'Course'));
}

Одно отношение $_has_many описывается следующими полями:

  • field - указывает на поле в таблице подчиненного класса (в нашем случае таблица lecture), в котором хранится значение идентификатора родительского объекта.
  • class - указывает на название класса подчиненного объекта.

Одно отношение $_many_belongs_to описывается следующими полями:

  • field - указывает на поле в таблице текущего класса (подчиненный), в котором хранится значение идентификатора родительского объекта.
  • class - указывает на название класса родительского объекта.

Обратите внимание, что course_id упоминается как в $_has_many, так и в $_many_belongs_to.

Работа со связанными объектами

Связывание объектов

Для добавления новых зависимых объектов по одному в родительский объект (содержащий $_has_many) используется метод addToRelationName($object), в нашем случае addToLectures($lecture):

  $course = new Course();
  $course->setTitle('Super course');
 
  $l1 = new Lecture();
  $l1->setTitle('Physics');
  $l2 = new Lecture();
  $l2->setTitle('Math');
 
  $course->addToLectures($l1);
  $course->addToLectures($l2);

Можно также добавить сразу несколько объектов, используя метод setRelationName($array_or_iterator), в нашем случае setLectures($lectures). В качестве параметра передается массив или итератор с объектами.

  $course = new Course();
  $course->setTitle('Super course');
 
  $l1 = new Lecture();
  $l1->setTitle('Physics');
  $l2 = new Lecture();
  $l2->setTitle('Math');
 
  $course->setLectures(array($l1, $l2));
 
  $course->save();  // Сохранит курс и связанные с ним лекции

Обратите внимание, что при использовании метода setRelationName() набор связанных объектов полностью заменяется на новый, старые связанные объекты - удаляются.

Навигация по связанных объектам

Метод getRelationName() родительского класса применительно к связанным один-ко-многим объектам, в нашем случае это будет Course :: getLectures(), возвращает итератор (коллекцию) с дочерними объектами:

  $course = lmbActiveRecord :: findById('Course', $course_id);
 
  $lectures = $course->getLectures();
  foreach($lectures as $lecture)
    echo $lecture->getTitle() . "\n";

Получив коллекцию связанных объектов можно добавлять в нее элементы посредством метода add($object), что эквивалентно вызову addToRelationName($object) у родительского объекта:

  $course = new Course();
  $course->setTitle('Super course');
 
  $l1 = new Lecture();
  $l1->setTitle('Physics');
  $l2 = new Lecture();
  $l2->setTitle('Math');
 
  $lectures = $course->getLectures();
  $lectures->add($l1);
  $lectures->add($l2);

При помощи метода getRelationName() у подчиненного объекта (в нашем случае это метод Lecture :: getCourse()) можно получить родительский объект:

  $lectures = lmbActiveRecord :: find('Lecture');
 
  foreach($lectures as $lecture)
    echo "Lecture " . $lecture->getTitle() . " of " . $lecture->getCourse()->getTitle() . " course. \n";

В последнем примере мы сталкиваемся с проблемой n+1 выборок, то есть для каждой лекции будет сделан запрос на курс. Как решить эту проблему, мы расскажем позже.

Удаление связанных объектов

При помощи метода removeAll() применительно к коллекции связанных объектов можно удалять связанные объекты, например:

  $course = lmbActiveRecord :: findById('Course', $course_id);
  $course->getLectures()->removeAll();

Если вам нужно удалить все лишь один объект из коллекции, можете удалить его явно через метод lmbActiveRecord :: destroy().

Обратите внимание, что коллекции, в частности методы add() и removeAll() ведут себя по-разному, в зависимости от того, сохранен родительский объект в момент работы с коллекцией или еще нет. Подробнее об этом, а также дополнительная информация по работе с коллекциями связанных объектов можно получить в разделе "Дополнительная информация по отношениям".

Обсуждение

Ваш комментарий. Вики-синтаксис разрешён:
   ___    ___    ___    ____   ____
  / _ |  / _ )  / _ \  /  _/  / __/
 / __ | / _  | / ___/ _/ /   / _/  
/_/ |_|/____/ /_/    /___/  /_/
 
limb3_2007_2/ru/usage/active_record/one_to_many.txt · Последние изменения: 2010/11/10 10:02 (внешнее изменение)