Содержание
Транзакция - lmbUnitOfWork
Краткое описание
lmbUnitOfWork - хранит список объектов, затронутых одной транзакцией, а также координирует процесс записи или удаления объектов в (из) базу данных (persistence layer). Основное назначение данного класса - развязать по времени изменение объекта в памяти и отражение этого изменения в базе данных.
Старт и завершение транзакции
Транзакция в lmbUnitOfWork начинается с метода reset() и заканчивается commit().
Обычно вызовы этих методов находятся в одном из фильтров глобальной цепочки фильтров под названием UnitOfWorkFilter, так что при создании приложений волноваться насчет транзакции не стоит. Обычно стартовать или сбрасывать транзакцию требуется во время тестов или при возникновении какой-либо экстремальной ситуации, когда работу приложения нельзя считать успешной.
Код фильтра UnitOfWorkFilter очень простой:
class UOWFilter//implements lmbInterceptingFilter { function run($filter_chain, $request, $response) { $uow = Limb :: toolkit()->getUOW(); $uow->reset(); $filter_chain->next(); $uow->commit(); } }
Регистрация объекта в lmbUnitOfWork
Новые (вновь созданные) объекты обычно необходимо регистрировать в lmbUnitOfWork. Для регистрации объекта в lmbUnitOfWork используется метод lmbUnitOfWork :: register($object), например, так:
$unit = new Unit(); $unit->setName('litres'); $uow = Limb :: toolkit()->getUOW(); $uow->register($unit);
На самом деле регистрировать в lmbUnitOfWork нужно не все объекты, а только те объекты, на которые в текущий момент никто не ссылается. Например, возьмем такой код:
$author = new Author(); $author->setName('Robert Stivenson'); $book = new Book(); $book->setTitle('Treasure Island'); $book->setAuthor($author); $author->addBook($book); $uow = Limb :: toolkit()->getUOW(); $uow->register($book);
Здесь не обязательно регистрировать объект класса Author, так как Book уже зарегистрирован. lmbUnitOfWork «найдет» объект класса Author самостоятельно. Правда ничего страшного в том, чтобы зарегистрировать $author вручную, нет.
Те, объекты, которые получаются из мапперов, регистрируются ими в lmbUnitOfWork автоматически (в случае, если они отнаследованы от AbtractDataMapper).
Удаление объектов
Для того, чтобы пометить доменный объект в транзакции как удаленный, используется метод lmbUnitOfWork :: delete($object), например, так:
$toolkit = Limb :: toolkit(); $unit = $toolkit->createDataMapper('UnitMapper')->findById($id); $uow = $toolkit->getUOW(); $uow->delete($unit);
Объект будет удален из базы данных после завершения транзакции.
Отмена изменений, удаление объекта из транзакции
В некоторых случаях изменения над каким-либо конкретным объектом необходимо отменить. Для этого используется метод lmbUnitOfWork :: evict($object), например, так:
$uow = $toolkit->getUOW(); $unit = $toolkit->createDataMapper('UnitMapper')->findById($id); $uow->delete($unit); if($this->_someCondition()) $uow->evict($unit);
Как lmbUnitOfWork отслеживает изменения объектов и как она находит вложенные объекты?
При первоначальной регистрации или же после коммита транзакции lmbUnitOfWork запоминает начальное состояние объекта, а также ссылку на него. Вернее она сохраняет в качестве начального состояния объекта результат выполнения метода getHash(), который она вызывает у регистрируемого объекта. Вот, к примеру, код метода lmbObject :: getHash():
function getHash() { return md5(serialize($this)); }
Соответственно, если хоть один атрибут класса изменится, то изменится результат getHash и транзакция узнает, что объект изменился и нужно попросить маппер обновить состояние объекта в базе данных.
Теперь перейдем к тому, как транзакция находит другие объекты. При регистрации объектов у них спрашиваются все атрибуты. Те атрибуты, которые являются объектами и поддерживают интерфейс lmbPersistable, также регистрируются в транзакции. Отметим также, что регистрации подлежат также все lmbPersistable объекты, которые лежат в коллекциях. lmbUnitOfWork ведет себя достаточно деликатно по отношению к коллекциям, что позволяет очень эффективно использовать прокси-коллекции. lmbUnitOfWork не будет проходить по коллекции, которая является неотрезолвленным прокси-объектом, то есть который еще не загружал реальные данные. В остальном работа прокси-объектов является прозрачной даже для lmbUnitOfWork, поэтому можно особо не вникать в детали того, как proxy-объекты работают с транзакцией.
Внимание! На настоящий момент есть один негативный момент с транзакцией. Внутри нее используется метод get_object_vars(), однако эта функция возвращает только те атрибуты, которые являются публичными. Из-за этого транзакция не находит защищенные или приватные атрибуты регистрируемого объекта. Мы обязательно исправим этот баг в следующих релизах.

Обсуждение