Object-Oriented PHP5 Data Mapper ORM

Implemented Relationship Types

HasOne

One-to-one table relationships. Row ‘has one’ related row in another table. Each post ‘has one’ author.

HasMany

One-to-Many table relationships. Row ‘has many’ related rows in another table. Each post ‘has many’ comments.

Define The Relationship

Relationships must be defined in the individual models that require them. In the example below, we define a relationship in the PostsModel so that each post row object ‘HasMany’ comments. The mapper used to load the comments is ‘CommentsModel’, and the post ‘id’ field will match up with the comment ‘post_id’ field.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Post
class PostsModel extends phpDataMapper_Model {
	// Specify the database table
	protected $table = "posts";
 
	// Define your fields
	protected $fields = array(
		'id' => array('type' => 'int', 'primary' => true),
		'title' => array('type' => 'string', 'required' => true),
		'body' => array('type' => 'text', 'required' => true),
		'status' => array('type' => 'string', 'default' => 'draft'),
		'date_created' => array('type' => 'datetime')
	);
 
	// Define table relations
	protected $relations = array(
		// Comments
		'comments' => array(
			'relation' => 'HasMany',
			'mapper' => 'CommentsModel',
			'foreign_keys' => array('id' => 'post_id'),
		)
	);
}
 
// Post Comments
class CommentsModel extends phpDataMapper_Model {
	// Specify the database table
	protected $table = "post_comments";
 
	// Define your fields
	protected $fields = array(
		'id' => array('type' => 'int', 'primary' => true),
		'post_id' => array('type' => 'int', 'key' => true, 'required' => true),
		'body' => array('type' => 'text', 'required' => true),
		'date_created' => array('type' => 'datetime')
	);
}

Usage Example

We can now use the relationship like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// DataMapper base model
require '../phpDataMapper/Model.php';
 
// Setup database connection
require '../phpDataMapper/Database/Adapter/Mysql.php';
try {
	$adapter = new phpDataMapper_Database_Adapter_Mysql('localhost', 'blog', 'root', '');
} catch(Exception $e) {
	echo $e->getMessage();
	exit();
}
 
// Instantiate the Mapper
$postMapper = new PostMapper($adapter);
 
// Find all posts with a status of 'published'
$posts = $postMapper->all(array('status' => 'published'));
 
// Loop and print posts and comments
if($posts && count($posts) > 0) {
	// Posts
	foreach($posts as $post) {
		echo "<h2>" . $post->title . "</h2>";
		echo "<p>" . $post->body . "</p>";
		echo "<hr />";
		// Comments
		if($post->comments && count($post->comments) > 0) {
			foreach($post->comments as $comment) {
				echo "<p>" . $comment->body . "</p>";
			}
		}
	}
}

Notes

Currently, the above ‘HasMany’ example will incur N+1 queries. That is, one query will be executed for each set of comments requested for each post. So if 20 posts are returned and you print the comments for all posts, 20+1 queries will be executed.

Solving the N+1 query problem is one of the primary goals of the phpDataMapper project, and will be addressed before the first official release of phpDataMapper in the very near future so that in the above situation, the maximum number of queries executed will be 2. One for all the posts, and one for all the comments related to all the posts in the result set.