5

我不希望有人说“你不应该重新发明轮子,使用开源 ORM”;我有即时需求,无法切换。

我正在做一个支持缓存的小 ORM。即使不支持缓存,我仍然需要这个功能,以知道何时将对象写入存储。模式是 DataMapper。

这是我的方法:

  • 我想避免运行时自省(即猜测属性)。
  • 我不想使用 CLI 代码生成器来生成 getter 和 setter(实际上我使用的是 NetBeans,使用 ALT+INSERT)。
  • 我希望模型最接近 POPO(普通的旧 PHP 对象)。我的意思是:私有属性,每个属性的“硬编码”getter 和 setter。

我有一个抽象类,称为AbstractModel所有模型都继承。它有一个isDirty()名为 is_dirty 的私有(如果需要,也可以受到保护)属性调用的公共方法。它必须返回真或假,具体取决于自加载以来对象数据是否发生变化。

问题是:"is_dirty"有没有办法在每个 setter 中不编码就可以提高内部标志$this->is_dirty = true$this->attr = $value我的意思是:除了业务逻辑需要更改代码外,我大多数时候都希望使用 setter 。

其他限制是我不能依赖,__set因为在具体模型类上,属性已经作为私有存在,所以__set永远不会在 setter 上调用。

有任何想法吗?接受来自其他 ORM 的代码示例。

我的一个想法是修改 NetBeans 设置器模板,但我认为应该有一种方法可以在不依赖 IDE 的情况下做到这一点。

我的另一个想法是创建设置器,然后用下划线或其他内容更改私有属性的名称。这样,setter 会调用__set并在那里有一些代码来处理"is_dirty"标志,但这有点打破了 POPO 的概念,而且很丑陋。

4

3 回答 3

8

注意力!
在过去的一个月里,我对这个主题的看法发生了一些变化。虽然答案 where 仍然有效,但在处理大型对象图时,我建议改用 Unit-of-Work 模式。您可以在此答案中找到它的简要说明

我有点困惑what-you-call-Model与ORM 的关系。这有点令人困惑。特别是因为在 MVC 中,模型是一个层(至少,我是这么理解的,而且你的“模型”似乎更像是 域对象)。

我将假设您拥有的是如下所示的代码:

  $model = new SomeModel;
  $mapper = $ormFactory->build('something');

  $model->setId( 1337 );
  $mapper->pull( $model );

  $model->setPayload('cogito ergo sum');

  $mapper->push( $model );

而且,我将假设what-you-call-Model有两种方法,设计器供数据映射器使用:getParameters()setParameters(). 并且您isDirty()在 mapper 存储what-you-call-Model的状态和调用之前调用cleanState()- 当 mapper 将数据拉入what-you-call-Model时。

顺便说一句,如果您有更好的建议来获取数据映射器的值而不是setParameters()and 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()。它有一个奇怪的名字,因为在 OOP 中,这个implements词的意思是has-abilities-of,而extends意思是 is-a

到目前为止,您应该已经拥有类似的所有内容...


现在这里是isDirty()andcleanState()方法应该做的:

  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;
  }
于 2012-06-11T23:57:57.427 回答
1

我会做一个代理来设置,例如:

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。我想这不是很流行。

于 2012-06-07T21:51:32.933 回答
0

虽然这篇文章很旧,但是当 isDirty() 发生时如何使用事件通知侦听器?我会用事件来解决这个问题。

于 2013-12-18T16:56:28.130 回答