在 ORM 模型中处理脏状态的最佳方法

时间:2023-02-28
本文介绍了在 ORM 模型中处理脏状态的最佳方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我正在做一些支持缓存的 ORM.即使不支持缓存,我还是需要这个功能,以了解何时将对象写入存储.模式是 DataMapper.

这是我的方法:

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

我有一个名为 AbstractModel 的抽象类,所有模型都继承了它.它有一个名为 isDirty() 的公共方法,带有一个名为 is_dirty 的私有属性(如果需要,也可以被保护).它必须返回 true 或 false,具体取决于对象数据自加载后是否发生更改.

问题是:有没有办法在每个 setter $this->is_dirty = true<中不用编码就提升内部标志 "is_dirty"/代码>?我的意思是:大多数情况下,我希望将 setter 设置为 $this->attr = $value,除非业务逻辑需要更改代码.

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

有什么想法吗?接受来自其他 ORM 的代码示例.

我的一个想法是修改 NetBeans setter 模板,但我认为应该有一种不依赖于 IDE 的方法.

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

解决方案

注意!
我对这个主题的看法在过去一个月有所改变.虽然答案 where 仍然有效,但在处理大型对象图时,我建议改用工作单元模式.您可以在这个答案

中找到对其的简要说明

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

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

 $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().并且您在映射器存储 what-you-call-Model 的状态并调用 cleanState() 之前调用 isDirty() - 当映射器拉数据到what-you-call-Model.

<块引用>

顺便说一句,如果您有更好的建议从数据映射器中获取值而不是 setParameters()getParameters(),请分享,因为我一直在努力想出更好的东西.在我看来,这就像封装泄漏.

这将使数据映射器方法看起来像:

 公共函数 pull( 参数化 $object ){如果 ( !$object->isDirty() ){//没有在干净的对象上设置任何条件//或者自上次拉取后值没有改变返回假;//或者可能抛出异常}$data =//执行从存储中读取信息的操作$object->setParameters($data);$object->cleanState();返回 $true;//或省略,如果替代作为例外}公共静态函数推送(参数化 $object ){如果 ( !$object->isDirty() ){//没有什么可以保存的,走开返回假;//或者可能抛出异常}$data = $object->getParameters();//将值保存在存储中$object->cleanState();返回 $true;//或省略,如果替代作为例外}

<块引用>

在代码片段中 Parametrized 是接口的名称,该对象应该实现.在这种情况下,方法 getParameters()setParameters().它有一个如此奇怪的名字,因为在 OOP 中,implements 词的意思是 has-abilities-of ,而 extends 表示 is-a.

到这部分为止,您应该已经拥有所有类似的东西...

<小时>

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

 公共函数 cleanState(){$this->is_dirty = false;$temp = get_object_vars($this);取消设置( $temp['variableChecksum'] );//校验和不应该是它自身的一部分$this->variableChecksum = md5( serialize( $temp ) );}公共函数 isDirty(){if ( $this->is_dirty === true ){返回真;}$previous = $this->variableChecksum;$temp = get_object_vars($this);取消设置( $temp['variableChecksum'] );//校验和不应该是它自身的一部分$this->variableChecksum = md5( serialize( $temp ) );返回 $previous !== $this->variableChecksum;}

I don't want anyone saying "you should not reinvent the wheel, use an open source ORM"; I have an immediate requirement and cannot switch.

I'm doing a little ORM, that supports caching. Even not supporting caching, I would need this feature anyways, to know when to write an object to storage or not. The pattern is DataMapper.

Here is my approach:

  • I want to avoid runtime introspection (i.e. guessing attributes).
  • I don't want to use a CLI code generator to generate getters and setters (really I use the NetBeans one, using ALT+INSERT).
  • I want the model to be the closest to a POPO (plain old PHP object). I mean: private attributes, "hardcoded" getters and setters for each attribute.

I have an Abstract class called AbstractModel that all the models inherit. It has a public method called isDirty() with a private (can be protected too, if needed) attribute called is_dirty. It must return true or false depending if there is a change on the object data or not since it was loaded.

The issue is: is there a way to raise the internal flag "is_dirty" without coding in each setter $this->is_dirty = true? I mean: I want to have the setters as $this->attr = $value most of the time, except a code change is needed for business logic.

Other limitation is that I cannot rely on __set because on the concrete model class the attributes already exists as private, so __set is never called on the setters.

Any ideas? Code examples from others ORMs are accepted.

One of my ideas was to modify the NetBeans setters template, but I think there should be a way of doing this without relying on the IDE.

Another thought I had was creating the setters and then change the private attribute's name with an underscore or something. This way the setter would call to __set and have some code there to deal with the "is_dirty" flag, but this breaks the POPO concept a little, and it's ugly.

解决方案

Attention!
My opinion on the subject has somewhat changed in the past month. While the answer where is still valid, when dealing with large object graphs, I would recommend using Unit-of-Work pattern instead. You can find a brief explanation of it in this answer

I'm kinda confused how what-you-call-Model is related to ORM. It's kinda confusing. Especially since in MVC the Model is a layer (at least, thats how I understand it, and your "Models" seem to be more like Domain Objects).

I will assume that what you have is a code that looks like this:

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

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

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

  $mapper->push( $model );

And, i will assume that what-you-call-Model has two methods, designer to be used by data mappers: getParameters() and setParameters(). And that you call isDirty() before mapper stores what-you-call-Model's state and call cleanState() - when mapper pull data into what-you-call-Model.

BTW, if you have a better suggestion for getting values from-and-to data mappers instead of setParameters() and getParameters(), please share, because I have been struggling to come up with something better. This seems to me like encapsulation leak.

This would make the data mapper methods look like:

  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
  }

In the code snippet Parametrized is a name of interface, which object should be implementing. In this case the methods getParameters() and setParameters(). And it has such a strange name, because in OOP, the implements word means has-abilities-of , while the extends means is-a.

Up to this part you should already have everything similar...


Now here is what the isDirty() and cleanState() methods should do:

  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;
  }

这篇关于在 ORM 模型中处理脏状态的最佳方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持html5模板网!

上一篇:调用布尔成员函数是什么意思以及如何修复 下一篇:如何在 PHP 中从头实现 MVC?

相关文章

最新文章