Лучший способ обработки грязного состояния в модели ORM

Я не хочу, чтобы кто-то говорил: «Вы не должны изобретать велосипед, использовать ORM с открытым исходным кодом» ; У меня есть неотложное требование и я не могу переключиться.

Я занимаюсь небольшим ORM, который поддерживает кеширование. Даже не поддерживая кеширование, мне нужна эта функция в любом случае, чтобы знать, когда писать объект на хранение или нет. Шаблон – DataMapper.

Вот мой подход:

  • Я хочу избежать интроспекции времени выполнения (т. Е. Угадывать атрибуты).
  • Я не хочу использовать генератор кода CLI для генерации getters и seters (действительно, я использую NetBeans один, используя ALT + INSERT).
  • Я хочу, чтобы модель была самой близкой к POPO (простой старый объект PHP). Я имею в виду: частные атрибуты, «жестко закодированные» геттеры и сеттеры для каждого атрибута.

У меня есть абстрактный класс под названием AbstractModel который наследует все модели. Он имеет открытый метод isDirty() с частным (может быть защищен, если необходимо) атрибутом is_dirty. Он должен возвращать true или false в зависимости от изменения данных объекта или нет, поскольку он был загружен.

Проблема заключается в следующем: существует ли способ повысить внутренний флаг "is_dirty" без кодирования в каждом $this->is_dirty = true ? Я имею в виду: я хочу, чтобы сеттеры были $this->attr = $value большую часть времени, за исключением необходимости изменения кода для бизнес-логики.

Другим ограничением является то, что я не могу полагаться на __set потому что в конкретном классе модели атрибуты уже существуют как частные, поэтому __set никогда не __set .

Есть идеи? Приводятся примеры кода из других ORM.

Одна из моих идей заключалась в том, чтобы изменить шаблон сеттеров NetBeans, но я думаю, что должен быть способ сделать это, не полагаясь на среду IDE.

Еще одна моя мысль заключалась в создании сеттеров, а затем изменении имени частного атрибута с подчеркиванием или чем-то еще. Таким образом, сеттер будет звонить в __set и иметь некоторый код для работы с флагом "is_dirty" , но это немного нарушает концепцию POPO, и это уродливо.

Attantion!
Мое мнение по этому вопросу несколько изменилось в прошлом месяце. Хотя ответ, который по-прежнему действителен, при работе с крупными объектными графами, я бы рекомендовал вместо этого использовать шаблон Unit-of-Work. Вы можете найти краткое объяснение этого в этом ansewer

Я немного смущен, как то, что вы называете-Model связано с ORM. Это смущает. Тем более, что в MVC модель представляет собой слой (по крайней мере, вот как я его понимаю , а ваши «Модели» мне больше похожи на объекты домена ).

Я буду считать, что у вас есть код, который выглядит так:

  $model = new SomeModel; $mapper = $ormFactory->build('something'); $model->setId( 1337 ); $mapper->pull( $model ); $model->setPayload('cogito ergo sum'); $mapper->push( $model ); 

И, я буду предполагать, что у того , что вы вызывают-модель, есть два метода: конструктор, который будет использоваться агентами данных: getParameters() и setParameters() . И что вы вызываете isDirty() прежде чем mapper isDirty() вы» – «Модель » и вызовет cleanState() – когда данные cleanState() данные в « что вы вызывать» .

Кстати, если у вас есть лучшее предложение для получения значений от-и-к карте данных вместо setParameters() и getParameters() , пожалуйста, поделитесь, потому что я изо всех сил пытался придумать что-то лучше. Это кажется мне утечкой инкапсуляции.

Это приведет к тому, что методы сопоставления данных будут выглядеть так:

  public function pull( Parametrized $object ) { if ( !$object->isDirty() ) { // there were NO conditions set on clean object // or the values have not changed since last pull return false; // or maybe throw exception } $data = // do stuff which read information from storage $object->setParameters( $data ); $object->cleanState(); return $true; // or leave out ,if alternative as exception } public static function push( Parametrized $object ) { if ( !$object->isDirty() ) { // there is nothing to save, go away return false; // or maybe throw exception } $data = $object->getParameters(); // save values in storage $object->cleanState(); return $true; // or leave out ,if alternative as exception } 

В фрагменте кода Parametrized – это имя интерфейса, объект которого должен быть реализован. В этом случае методы getParameters() и setParameters() . И у этого есть такое странное имя, потому что в ООП implements слово означает has-ability-of , в то время как средство extends означает -a .

До этой части у вас должно быть уже все похожее …


Теперь вот что должны делать методы isDirty() и cleanState() :

  public function cleanState() { $this->is_dirty = false; $temp = get_object_vars($this); unset( $temp['variableChecksum'] ); // checksum should not be part of itself $this->variableChecksum = md5( serialize( $temp ) ); } public function isDirty() { if ( $this->is_dirty === true ) { return true; } $previous = $this->variableChecksum; $temp = get_object_vars($this); unset( $temp['variableChecksum'] ); // checksum should not be part of itself $this->variableChecksum = md5( serialize( $temp ) ); return $previous !== $this->variableChecksum; } 

Например, я бы сделал прокси-сервер:

 class BaseModel { protected function _set($attr, $value) { $current = $this->_get($attr); if($value !== $current) { $this->is_dirty = true; } $this->$attr = $value; } } 

Затем каждый дочерний класс будет реализовывать свой сеттер, вызывая _set() и никогда не устанавливая свойство напрямую. Кроме того, вы всегда можете вводить более классный код в каждый _set и просто вызывать parent::set($attr, $processedValue) _set parent::set($attr, $processedValue) при необходимости. Затем, если вы хотите использовать магические методы, вы создаете прокси-сервер для метода свойств, который _set на _set . Я полагаю, что это не очень POPO.

хотя этот пост старый, но как об использовании событий для уведомления слушателей, когда isDirty () происходит? Я бы подошел к решению с событиями.