views:

124

answers:

3

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...)

A: 

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.

Maskime
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
A: 

Big question is; what happens inside markModified()..? I copied to code and tried it, but I dont get the error you reported. So I guess one piece of puzzle is missing?

Gabor de Mooij
markModified simply stores a reference to $this to the $_modified array in Monitor. It's not an error, but a flag in FirePHP - if Monitor contains a ref of Entity, and Entity contains a ref of Monitor, it's a kind of recursion
sunwukung
+1  A: 

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