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

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


limb3_2007_2:ru:usage:active_record:more_on_relations

Дополнительная информация по отношениям

Поддержка отношений в методе lmbActiveRecord :: import()

Метод lmbActiveRecord :: import() используется для заполнения/обновления объектов. Если метод import() встречает в списке переданных ему полей имя, сответствующее названию одного из отношений, он пытается заполнить полученные данные в соответствие с описанием отношения.

Для отношений один-к-одному

Возьмем пример, описанный в разделе "Поддержка отношений вида один-к-одному":

class Person extends lmbActiveRecord
{
  protected $_has_one = array('social_security' => array('field' => 'social_security_id',
                                                         'class' => 'SocialSecurity',
                                                         'can_be_null' => true));
}
 
class SocialSecurity extends lmbActiveRecord
{
  protected $_belongs_to = array('person' => array('field' => 'social_security_id',
                                                   'class' => 'Person'));
}

Используем import для того, чтобы связать SocialSecurity с Person.

 $person = new Person(); 
 $person->setName('Jim');
 $person->save();
 [[...]]
 $data = array('code' => '099123', 'person' => $person->getId());
 $number = new SocialSecurity();
 $number->import($data);
 $number->save();

Обратите внимание, что поле называется именно person - по названию связи, а не person_id.

Где может использоваться такая возможность? Обычно она используется в панелях управления, где можно передавать идентификатор связанных объектов прямо в запросе и передавать данные в объекты посредством вызовов вида:

 $number = new SocialSecurity();
 $number->import($request->export());
 $number->save();

Отметим, что это такой способ заполнения объекта данными подходит не всегда: например, если значение поля, соответствующее связи, в пришедшем в import() массиве не указывает на реальный объект. В этом случае может произойти исключение, так как при импорте lmbActiveRecord постарается загрузить указанный объект через findById() метод (см. "Создание, сохранение, загрузка и удаление объектов").

Для отношений один-ко-многим и много-ко-многим

Если в import в соответствующем поле пришел массив идентификаторов, эти объекты будут сохранены, как если бы мы вызвали метод setRelationName($objects).

Возьмем пример, описанный в разделе "Поддержка отношений вида много-ко-многим":

class Group extends lmbActiveRecord
{
  protected $_db_table_name = 'user_group';
  protected $_has_many_to_many = array('users' => array('field' => 'group_id',
                                                        'foreign_field' => 'user_id',
                                                        'table' => 'user2group',
                                                        'class' => 'User'));
}
 
class User extends lmbActiveRecord
{
  protected $_has_many_to_many = array('groups' => array('field' => 'user_id',
                                                         'foreign_field' => 'group_id',
                                                         'table' => 'user2group',
                                                         'class' => 'Group'));
}

Используем import для того, чтобы связать User с Group.

 $group1 = new Group(); 
 $group1->setTitle('First group');
 $group1->save();
 
 $group2 = new Group(); 
 $group2->setTitle('Second group');
 $group2->save();
 [[...]]
 $data = array('first_name' => 'Vasa', 'groups' => array($group1->getId(), $group2->getId()));
 $user = new User();
 $user->import($data);
 $user->save(); // Теперь пользователь Vasa входит в 2 группы.
 // или
 $data = array('first_name' => 'Vasa', 'groups' => array($group1, $group2));
 $user = new User();
 $user->import($data);
 $user->save(); // Теперь пользователь Vasa входит в 2 группы.

Аналогично для отношений один-ко-многим

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

Рассмотрим отношение один-ко-многим, которое мы приводили чуть выше:

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

Наша задача - рассмотреть подробнее коллекции связанных объектов, то, какими методами можно пользоваться, чтобы изменять состояние этих коллекций.

Изменение содержимого коллекции

Итак, коллекцию, которую возвращает метод getLectures() можно изменять, например, добавлять в нее новые элементы:

  $course = new Course();
  $course->setTitle('Super course');
 
  $l1 = new Lecture();
  $l1->setTitle('Physics');
 
  $lectures = $course->getLectures();
  $lectures->add($l1); // Эквивалентно $course->addToLectures($l1);

Также из коллекции можно удалить все объекты, используя метод removeAll().

Доступ к элементам внутри коллекции

Коллекция связанных объектов - это обычный итератор:

  $lectures = $course->getLectures();
  for($lectures->rewind(); $lectures->valid(); $lectures->next())
  {
    $lecture = $lectures->current();
    echo $lecture->getTitle();
  }
 
  // Возможна более компактная запись
  foreach($course->getLectures() as $lecture)
    echo $lecture->getTitle();

Можно получать доступ к определенным объектам по индексу при помощи метода at($pos), это полезно при тестировании:

  $lectures = $course->getLectures();
  echo $lectures->at(2)->getTitle();

Обратите внимание, что каждый вызов at() приводит к отдельному запросу к базе данных.

Также можно получить массив объектов при помощи метода getArray() и обращаться к объектами по порядковому номеру:

  $lectures = $course->getLectures()->getArray();
  echo $lectures[1]->getTitle();

Также можно получить только список идентификаторов связанных объектов при помощи метода getIds():

  $ids = $course->getLectures()->getIds();
  echo implode(',', $ids); // Выведет что-то вроде 2,10,11,20...

Поиск элементов в коллекциях

Для поиска элементов внутри коллекция существуют find-методы, подобные тем, которые реализованы для класса lmbActiveRecord.

Метод find($params = array()) - осуществляет поиск элементов внутри коллекции. Аргумент $params действует аналогично тому, что используется в lmbActiveRecord :: find(). То есть вместо $params, можно передать строку или объект Criteria, а может передать массив с некоторыми полями, которые будут содержать информацию по сортировке, например:

  $course = new Course();
  $short_lectures = $course->getLectures->find('duration < 30');
  $criteria = new lmbSQLFieldCriteria('duration ', 60, '>');
  $sort = array('title' => 'DESC', 'duration' => 'ASC');
  $course->getLectures->find(array('criteria' => $criteria, 'sort' => $sort));

Также существует метод findFirst($params = array()), который возвращает первый найденный элемент из коллекции.

Сортировка

Если find-методы ничего не получают про то, как нужно сортировать элементы при итерации, тогда они используют параметры сортировки по-умолчанию.

Параметры сортировки по-умолчанию также применяются при обычном итерировании по элементам коллекции.

Параметры сортировки по-умолчанию формируются следующим образом:

  • они могут быть указаны в описании отношений,
  • они берутся из свойств класса, объекты которого хранятся в коллекции.

В первом случае сортировка может быть указана в виде параметра sort_params при описании отношений, например:

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

Обратите внимание на sort_params. То есть теперь элементы коллекции lectures будут сортироваться по заголовку, если не будет указано иное:

$lecture1 = new Lecture(array('title' => 'Super lecture'));
$lecture2 = new Lecture(array('title' => 'Basic lecture'));
 
$course = new Course(array('title' => 'My lecture'));
$course->setLectures(array($lecture1, $lecture2));
$course->save();
 
$lectures = $course->getLectures()->find();
$lectures->rewind();
echo $lectures->current()->getTitle(); // Выведет Basic lecture
$lectures->next();
echo $lectures->current()->getTitle(); // Выведет Super lecture

Если же параметр sort не будет указан, тогда элементы коллекции (пусть будет lectures), будут сортироваться так, как это определено для класса Lecture (см. сортировку в классе lmbActiveRecord).

Различия в поведении коллекций для новых и уже сохраненных объектов

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

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

Длу коллекции несохраненного владельца метод add() и removeAll() не приводят к обращению к базе данных. Они лишь изменяют состояние набора объектов в памяти. То есть в «новой» коллекции объекты «копятся», и сохраняются только при сохранении владельца. Обратите внимание, что метод find() и findFirst() для «новых» коллекций не работают и генерируют исключение.

Изменение объектов в коллекциях

Если вы получили коллекцию связи один-ко-многим или много-ко-многим через родительский объект (или просто объект с одной из строн), и изменяете их, итерируя по коллекции, вы должны сохранять объекты явно. Сохранение только родительского объекта в текущей версии не достаточно:

$course = new Course($course_id);
foreach($course->getLectures() as $lecture)
  $lecture->setTitle($lecture->getTitle() . ' appended');
 
$course->save(); // Не приведет к сохранению изменений в lectures.

Необходимо писать:

$course = new Course($course_id);
foreach($course->getLectures() as $lecture)
{
  $lecture->setTitle($lecture->getTitle() . ' appended');
  $lecture->save();
}

В будущих версиях это поведение планируется поправить, однако сейчас - будьте внимательны!

Обсуждение

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