I’m trying to make my model layer more abstract and to do so I’ve introduced three layers, so for example a users db table would consist of;
Users - Object that contains attributes specific to user; id, firstname, email etc
UsersMapper - Object that maps user attributes to the fields in the db
UsersDbTable - contains sql queries CRUD commands etc
The mapper is responsible for taking a Users object, mapping the data to the related table fields and using the UsersDbTable object to run query
I think this is right??
But where I’m getting confused is how/where to implement table joins. Should the UsersMapper and UsersDbTable objects know about other tables I want to join?
If you’re creating an ORM, most of the time JOINs and other table or database operations will be performed with a Query Object. This essentially just holds commands and related data/models, and then converts them to an SQL string on execution.
The only thing the User object should be aware of are its relations - HasMany, HasOne, BelongsTo, etc. How those are actually implemented and executed through the mapper, etc. is totally up to the specific data store and implementation.
So as an example, if I wanted to fetch all users and their related images, the user model would reference it’s relationship with the images model and the user mapper would use both models to map data to the database
$users = $userMapper->fetchAll()
foreach($users as $user)
{
echo $user->getFirstName();
$image = $user->getImages();
}
Is that correct?
So going the other way, how would I save user information and any new images, something like this?
You’re really not that far off - that’s pretty much exactly how it works :).
The key is that the relationship definition can be used as a two-way street. It is used both to retrieve related images on request, and to save them to the datastore as well.
My code might look something more like this:
$user->setFirstName('Fred');
$user->images[] = new Image(array('name' => 'picture.jpg'));
$userMapper->save($user);
Upon save, the mapper looks to the relationships and the relationship aliases (related images are populated on the “images” property/pseudo-field, etc.), then loads the corresponding mapper to save the related data back according to how the relationship is defined. This is how it works with phpDataMapper Table Relationships.
phpDataMapper is a great resource thank you very much.
To keep things simple (for my own benefit really), would I then introduce a userDao in the example above that the mapper would utilize. The userDao would we responsible for the actual actions against the db.
With phpDataMapper I assume this is the Query object?
You can (obviously) structure your own code how you want, but the phpDataMapper design has one mapper per entity type that handles everything for that type - fetching, saving, inserting, etc. The way it does this is through a generic Query object that is built with the query criteria and is then passed to a corresponding adapter to execute. On fetch/select, executing the Query object through the database adapter returns a Collection that has populated Entity objects in it, or false if the result set was empty.