New PHP Data Mapper Library

I had the same reservations in this topic. Czaries pointed me in the right direction :slight_smile: I won’t reiterate what he told me. His posts there are useful.

edit: By the way, the code I posted above was my solution to the problems I presented in that topic :slight_smile:

Enlighten a newbie Marcus, which part is a singleton particularly? Any suggestions on how I could improve that particular part to make it more DI friendly?

Also, I agree with TomB, I don’t know it’s so bad a thing that the object knows it CAN be persisted, it just shouldn’t know HOW it’s persisted.

I had hoped to use this library to replace an the ageing domain model layer on 99designs, so I do have some experience with large complex domain models. My experience on them has generally been the more complex they are, the less magic you are better of relying on. Hence, pheasant.

I’m not sure I wholly agree. I think Lachlan’s approach has far more benefits than drawbacks.

Consider this:


class DomainObject {
	protected $mapper;
	
	public function __construct(DataMapper $mapper) {
		$this->mapper = $mapper;	
	}
	
	public function save() {
		$this->mapper->save($this);	
	}
}



class DataMapper {
	public function findById($id) {
	//get data from the source	
		$result = array(); //query the database, read xml file, get the data from somewhere
		$obj = new DomainObject($this);
		foreach ($result as $key => $value) $obj->$key = $value;
		return $obj;	
	}
	
	public function save(DomainObject $obj) {
		//put $obj back into the database, or wherever it came from
	}
}

The domain object knows it can be persistent (is this a really a bad thing?). What it doesn’t know, is anything about how the mapper works.

Pointless? In this example yes… but introduce realations and it becomes incredibly powerful. By defining relations in the mapper (how a mapper relates to another mapper) it makes this:


$user = $userMapper->findById('123');
echo $user->orders[0]->items[0]->product->manufacturer->name;

Both possible and easy. This is how my own ORM works :slight_smile:

The alternative is messy and involves initiating a whole load of mappers manually with code that is clearly wrapping a relational schema or fetching the entire database into the $user object.

Any of these mappers could be database/xml/flat file/web service. It doesn’t matter.

Should the domain object know it might be persistent? It does not cause any problems. You mention testing, in this example you’d use a mock DataMapper.

Should the domain object know the method of persistence or any implementation details of it? No.

As I said previously the issue is relations. With your method I need to do:


$user = $userMapper->findById(1);
$orders = $orderMapper->findWhere('userId = '.  $user->id);
$orderItems = $orderItemMapper->findWhere('orderId = ' . $orders[0]->id);
$product = $productMapper->findById($items[0]->productId);
$manufacturer = $manufacturerMapper->findById($product->manufacturerId);
echo $manufacturer->name;

instead of


$user = $userMapper->findById(1);
echo $user->orders[0]->items[0]->product->manufacturer->name

The former is verbose, has reduced readability is not reusable and if the relations change, this code needs to be updated everywhere!

Hi…

It makes the domain code depend on the persistence layer. This is very common for small web projects, and works fine in these cases. If you are building a complex domain model you have enough problems to worry about without mixing in the DB behaviour.

A data mapper proper just reads the data out of a normal domain object. The domain object then knows nothing about the persistence layer, although it is obliged to make enough of itself public that the mapper can extract the data. Means you can test the model without the persistence at all.

Building a full on data mapper is a huge task. Usually smaller ones are build customised for a specific project. As these are often proprietary as well, you don’t see them in the open too much.

You may have a mapper internally, but I wouldn’t describe your library as such. Looks neat though.

Why do you have that Singleton in your code? Whether or not the main persistence class is a Singleton will be up to the application developer, no? Are you telling me how to do my job? Hmph. You come in here…just 'cos your better looking doesn’t give you the right…I mean I’ve been working on this project for years…young upstart, telling us what should be a Singleton when we know the code…I mean we know our dependencies and we can handle it…library coders telling us what to do when they no nothing about our problems…messing up our tests…I mean…

Ahem…sorry, Er, forgot what I was saying. Oh, yeah. No Singletons please. I might want to do something else like DI.

yours, Marcus

Unless the proxy extends a class that implements the User interface.

Here’s to circular conversations. :beer:

Yeah, It’s somewhat annoying. There’s generally never a “correct” solution and mostly it will vary based on individual goals and how people rank the relative pros/cons.

On a sort of related topic, php needs an __instanceof magic method. This would allow us to properly create wrapper classes since we already have __get, __set and __call.

This OOP business is like blumming “whack-a-mole”.

It’s what I love and hate about OOP - it exposes the “design” aspect of programming. A decision is required, it’s often not perfect and there are positive and negative consequences. To make skillful (and I guess you could say “beautiful”) choices is what we’re aiming for.

Cheers for the input, I’m off to buy some strong booze.

yes & no - the fact that you’ve called the calling variable $userMapper is kind of wrong,

I start with a wrapper for the whole system - let’s call it ORM

$User1 = $ORM->load('user',1);

this makes a call internally to the factory, which returns a proxy containing the entity and a reference to itself. So if the client calls

$Post = $User1->post[1] 

it would hit the proxy first, which would use it’s factory to generate the relevant objects, and return them wrapped in another proxy. All this does really is keep the dependencies out of the entities - for whatever that is worth.

How would that work in terms of the mapper/entity relationship? Do you pass the proxy to each entity instead of the mapper? Does each mapper have its own proxy?

edit: Are you saying

$userMapper->findById(123);

would return a Proxy object?

check post #34
It wraps the entity in an object which also contains the factory. The proxy grabs relations and properties keys from the entity, and intercepts access, either returning the relevant entity data, or generating new entities further down the chain.

It’s all about balance isn’t it?
On the one hand, we spend a lot of time here <ahem>debating</ahem> about best practices and the “correct” interpretation of design patterns - then we are confronted with the real world, throw the rule book out and assume that we’ll get away with it.

In that case, why not just use global variables for common system resources (like factories) rather than worry about all this dependency injection/orthogonality schtick?

:wink:

There are three issues:
1: how to handle the dependency (i.e. finding/generating the mapper for the relation)
2: Is it a corruption of the data mapper pattern - why not just use $user->save() if it has access to it’s own mapper?
3: The (albeit apparently negligible) circular references generated by passing factories or DI containers that cache previously instantiated objects.

Well that creates scalability, maintenance, reusability and code clarity issues. The cyclic reference thing is entirely performance. As far as I can tell it’s not bad in terms of software design. It doesn’t present any more design issues than any other dependency. I’m welcome to being proved wrong here.

2: Is it a corruption of the data mapper pattern - why not just use $user->save() if it has access to it’s own mapper?

Does $user->save(); present any real issues? In ActiveRecord it does because you can only save to a single data source and the $user object has knowledge of it.

It probably is outside the bounds of the pattern… but isn’t that just argumentum ad antiquitatem?

So, you could theoretically pass the whole ORM layer around then? OOF!! I digress -

How would/do you handle the new mapper issue? I noticed that you put a comment in your original code that suggested using a factory? How would you pass the factory round? I guess you pass it in at the top of the chain, and each step in the chain passes it’s factory (and database adapter) down the chain?

On all this circular references/memory usage I really have to ask: How much memory is actually being lost in a real world application. Is it hitting the memory limit or causing server issues? PHP scripts generally run in less than a second. The memory usage on the server isn’t going to be high unless it’s handling a hell of a lot of requests at once.

Doing the test script provided with a 1m memory limit:


<?php
ini_set('memory_limit', '1M');
class Foo {
    function __construct()
    {
        $this->bar = new Bar($this);
    }
}

class Bar {
    function __construct($foo = null)
    {
        $this->foo = $foo;
    }
}

$count = 0;
while (true) {
    $foo = new Foo();
    unset($foo);
    echo $count++ . '::' . number_format(memory_get_usage()) . "\\\
";
}
?>

On PHP 5.3 on windows this gets to the 1624th iteration. That’s with a 1mb limit. How many scripts are going to create anywhere near this many cyclic references?

To me, it seems like mountain/molehill. If you’re that worried about memory usage I’d argue you shouldn’t be using OOP in the first place :stuck_out_tongue:

In your defense Tom, I’ve not seen an example of Data Mapper (taken beyond the typical load->user example) that doesn’t run into this problem.

The question I asked was not how you would construct the factory itself, but how you would provide it to the entity? In your earlier example, getMapper was called when __get intercepted a key in the relations array. That method instantiated the required mapper there and then. If you were to replace this with a factory, would you pass the factory into the entity?

  • At construction(and thus down the chain),
  • Using static calls
  • or would you instantiate a new one (which would defeat the purpose).

At the moment, I’m toying with this proxy wrapper idea Mr.Sterling proposed earlier - so it intercepts the relations key, uses it’s instance of the factory object to generate the required objects, then wraps them up in another proxy/factory combo before returning it to the client.

Ok let me put a disclaimer on this, i’m not saying this is the right way of doing it, only how I’m handling it because the convenience of unlimited chaining on data objects is better than any downsides I’ve come across yet.



class MapperFactory {
	protected $dic;
	protected $mappers = array();
	
	public function __construct(Injection $dic) {
		$this->dic = $dic;	
	}
		
	public function getMapper($name) {
		if (!isset($this->mappers[$name])) $this->mappers[$name] = $this->dic->create($name);
		return $this->mappers[$name];
	}	
}

class DataMapper {
	
	public function __construct(MapperFactory $factory) {
		
	}
}

Yes it has the exact same issue as you had. It creates a cyclic reference because it maintains a single instance of any given mapper.

Well, “$userMapper” is retrieved using $mapperFactory->getMapper(‘User’); (or in actual fact I create a shorthand $this->mapper->user, I can even do
$this->mapper->user[123]; but this is not relevant :wink: )

I like the idea, but as with most things it creates new problems.

In this case you’ve broken any possible contracts with the data object.


public function placeOrder(Basket $basket, User $user) {
	
}

$user = $userMapper->findById(123);
placeOrder($this->session->basket, $user);

Because your $user is now an instance of Proxy this wont work.

In my datamapper. The mapper does something like:


$name = str_replace('Mapper', '', get_class($this);
if (class_exists($name)) return new $name;
else return new DataObject; 

Sometimes it’s useful for the returned object to be a type . This way I can either create a “User” class or
let it be handled by the generic DataObject class. For example an Order object may warrant a getTotal() function which loops through the order items/quantities and calculates the total.

Sorry I did update the code in a ninja-edit :stuck_out_tongue: I pass the factory down the chain via the constructor. I’m not sure i fully understand the proxy/wrapper idea. Can you show how you’d go about this?