4

I have a dilemma. I've used DI (read: factory) to provide core components for a homebrew ORM. The container provides database connections, DAO's,Mappers and their resultant Domain Objects on request.

Here's a basic outline of the Mappers and Domain Object classes

class Mapper{
    public function __constructor($DAO){
        $this->DAO = $DAO;
        }

    public function load($id){
        if(isset(Monitor::members[$id]){
        return Monitor::members[$id];

        $values = $this->DAO->selectStmt($id);
        //field mapping process omitted for brevity
        $Object = new Object($values);
        return $Object;
        }
    }

 class User(){
     public function setName($string){
        $this->name = $string;
        //mark modified by means fair or foul
     }
 }

The ORM also contains a class (Monitor) based on the Unit of Work pattern i.e.

class Monitor(){
    private static array modified;
    private static array dirty;
    public function markClean($class);
    public function markModified($class);
}

The ORM class itself simply co-ordinates resources extracted from the DI container. So, to instantiate a new User object:

$Container = new DI_Container;
$ORM = new ORM($Container);
$User = $ORM->load('user',1);
//at this point the container instantiates a mapper class
//and passes a database connection to it via the constructor
//the mapper then takes the second argument and loads the user with that id
$User->setName('Rumpelstiltskin');//at this point, User must mark itself as "modified"

My question is this. At the point when a user sets values on a Domain Object class, I need to mark the class as "dirty" in the Monitor class. I have one of three options as I can see it

1: Pass an instance of the Monitor class to the Domain Object. I noticed this gets marked as recursive in FirePHP - i.e. $this->Monitor->markModified($this)

2: Instantiate the Monitor directly in the Domain Object - does this break DI? 3: Make the Monitor methods static, and call them from inside the Domain Object - this breaks DI too doesn't it?

What would be your recommended course of action (other than use an existing ORM, I'm doing this for fun...)

sunwukung
  • 2,815
  • 3
  • 41
  • 56
  • Does the ORM keep a reference of the object it returns? If not, why is the Monitor in the ORM? Should the entity have it's own Monitor, then when it's all passed back to the ORM for CRUD operations, the ORM loops the Monitors for changes? – Nathan Loding Jun 11 '10 at 13:19
  • No, the Monitor keeps a record of all loaded classes - so $User1 and $User2 point to same object in Monitor if they're the same record. The ORM itself is just a facade really to handle the interaction of Mappers etc. ORM loops Monitor for changes on save – sunwukung Jun 11 '10 at 16:08
  • I'm confused on your use of the patterns, mostly because it's not how I would have set this up. Putting the DI *within* the ORM breaks Dependency Injection, doesn't it? The ORM is then a factory for domain objects, rather than a more traditional ORM. I wouldn't place the Monitor in the domain object factory. In this scenario, I'd make it static. If I'm misunderstanding what your patterns are accomplishing, I apologize. – Nathan Loding Jun 11 '10 at 20:35
  • Ah, I see. Sorry, I'm abusing the term ORM for the sake of a catchall class name - it is indeed just a factory. The Container is also just a factory, in that it invokes datamappers (ORM's?), DAO's, and returns Domain Objects. You seem to have some ideas about how I should go about this, can you post some reference material? The Monitor sits inside the "ORM" for the sake of wrapping the lot up in a single point of contact. The "ORM" word is a bit of a red herring – sunwukung Jun 11 '10 at 21:07
  • JFTR - most examples of PHP "ORM's" I've encountered use Active Record rather than Data Mapper, so this is a bit of an experiment for me, pulled together from various sources. – sunwukung Jun 11 '10 at 21:43
  • I found this the other day, it looks like a well-done pattern. Have you looked at something like this? http://components.symfony-project.org/event-dispatcher/ – Nathan Loding Jul 13 '10 at 13:40

2 Answers2

1

Okay, I see. What about this:

 $user = new User;
 $monitoredUser = new Monitor( $user );

 //at update in class Monitor:
 Monitor::markDirty( $this );

Now you use the Monitor as a decorator; it adds a shell around the user object or any other object that handles incoming requests. If an object gets updated it will be marked dirty by the monitor, however because the Monitor uses a static method for this it remains within the current class scope thus avoiding recursion.

Gabor de Mooij
  • 2,997
  • 17
  • 22
  • That's an interesting angle, although it might make the API a bit more difficult as a result - having to update Monitor manually. Ideally you just want to get a User from the ORM, change it's properties and it automatically gets monitored. The implementations of UoW I've seen use static (external) calls - but that's bad right? – sunwukung Jun 12 '10 at 12:18
  • You can also use reflection for this; implement a __call() method that routes the request to User in case an accessor has been invoked. – Gabor de Mooij Jun 12 '10 at 13:39
  • Maybe for inspiration: I have proposed an alternative UoW pattern: http://www.gabordemooij.com/unit_of_work, and I have my own ORM as well: http://www.redbeanphp.com – Gabor de Mooij Jun 12 '10 at 13:41
  • I'm accepting this answer - it's shown me the alternative I was looking for. I think for simplicity it might serve my personal objectives to use a static call - but I've got a better idea of the implications of each approach now. Thanks for your help – sunwukung Jun 12 '10 at 19:38
0

Thought i never did it, your Monitor class seems to correspond to an "Observer" design pattern. This way you don't have to ask the Monitor class to mark the class as dirty, but it should do it alone.

As i said i never did it in PHP but i'm pretty confident that you can look on Google a way to implement this design pattern in PHP.

MaxouMask
  • 985
  • 1
  • 12
  • 33
  • Sure, this is basically the same as using a setter. My only issue is that it sets up a recursion loop (since a reference to the Entity resides in the Monitor, and a reference to the Monitor resides in the Entity). – sunwukung Jun 10 '10 at 14:58