Best way to handle dirty state in an ORM model

model-view-controller, oop, orm, php

Solution

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

Problem

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.

Original source

Related problems