Fi1osof 09 июня 2013 1 0
Не буду сейчас расписывать про весь механизм сохранения связанных объектов, а приведу пару примеров и расскажу про тонкость, ради которой и пишется этот топик.

Возьмем два новых объекта (пользователя и его профиль), добавим профиль в пользователя, и сохраним пользователя. Результат: будет сохранен и профиль, плюс еще и в качестве internalKey будет присвоен id нового пользователя. Вот код:
$user = $modx->newObject('modUser', (array)$userdata);
$profile = $modx->newObject('modUserProfile', (array)$profiledata);
$user->addOne($profile);
$user->save();


Отличная штука, эти связи.

Так же можно работать и с имеющимися объектами, к примеру получим профиль пользователя, изменим его, и сохраним объект самого профиля. Результат: профиль будет сохранен.

$user = $modx->getObject('modUser', $id);
$profile = $user->getOne('Profile');
$profile->fromArray((array)$profiledata);
$user->save();

Собственно, здесь тоже все замечательно. xPDO сам отследит, что связанный с пользователем объект профиля был изменен, и сохранит изменения профиля в базу. Но как будут обстоять дела, если будет изменен профиль во втором уровне вложенности?

Для примера возьмем профиль пользователя, создавшего документ, изменим его, и сохраним документ.
$doc = $modx->getObject('modResource', $id);
$user = $doc->getOne('CreatedBy');
$profile = $user->getOne('Profile');
$profile->set('fullname', 'New name');
$doc->save();

И вот здесь как раз и есть загвоздка. Дело в том, что xPDO сохраняет объект только тогда, когда у него есть хоть одна dirty-колонка (измененная). То есть он вызывает метод xPDOObject::save(), в котором вызывается метод _saveRelatedObjects(), сохраняющий связанные объекты. А так как у нас объект пользователя не был изменен, то xPDO его не сохраняет (не вызывает метод save()), а значит и не сохраняет связанные с ним объекты. Следовательно и профиль пользователя не сохраняется.

Давайте еще раз рассмотрим последовательность действий:
  1. Мы сохраняем документ — вызываем метод $doc->save();
  2. В этом методе вызывается метод _saveRelatedObjects(), сохраняя связанные объекты. В нашем случае связанный объект ближайшего уровня — $user.
  3. Далее, каждый связанный объект проверяется на наличие измененных колонок ( if (!empty ($this->_dirty)) ), и если таковые имеются, то этот объект сохраняется (и смотрим опять все начиная с первого пункта, только уже для этого объекта).

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

В общем, как оказалось, здесь только или через явное сохранение предшествующего объекта (почему мне такой момент не подходит, написал выше), или через маркировку предшествующего объекта, будто бы он изменен. В нашем случае код будет выглядеть так:
$doc = $modx->getObject('modResource', $id);
$user = $doc->getOne('CreatedBy');
$profile = $user->getOne('Profile');
$profile->set('fullname', 'New name');
if($profile->_dirty){
    $user->setDirty('id');
}
$doc->save();

То есть проверяем, если колонки есть измененные в профиле, то помечаем колонку id юзера как измененную. В итоге, при сохранении документа, xPDO попытается и пользователя сохранить, и затем его профиль. Конечно это хак, но так, на заметку…
0 комментариев
Авторизуйтесь или зарегистрируйтесь (можно через соцсети ), чтобы оставлять комментарии.