A Path Less Taken

Breaking with convention in a very conventional fashion. Powered by WordPress

"What would you attempt to do if you knew you could not fail?"
Dr. Robert Schuller

Monday, October 10, 2011

Category: PHP Development Tags: , Author: JJ 0 Comments

In a recent post I touted the coolness factor of the Spot ORM. Based on a response to that post I prepared this short tutorial on how to get started. This is culled from personal experience and guidance that I received from the software’s author. I hope this is helpful to you if you are interested in giving Spot a try.

First, download the code from github here and copy the included folder where it can be referenced by your project. I renamed my folder Spot and put it in the modules folder for my project. Your approach my vary.

Next we set up the Spot objects for use. Here is a MySql example.

1
2
3
4
require_once "modules/Spot/Config.php" ;
$cfg = new Spot\Config () ;
$adapter = $cfg->addConnection ('mysql', 'mysql://username:password@localhost/databasename') ;
$mapper = new Spot\Mapper ($cfg) ;

Right away you can see that Spot uses the namespace Spot. It’s a trivial observation, but it might save you a little frustration when you are just getting started. Next we need to define our entity. Spot is based on the Data Mapper design pattern so the Entity is sort of a dumb object, but we need to define it anyway. Here is a basic entity example.

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
/*
	Author Mapper
*/
class Authors extends Spot\Entity
{
    // Specify the data source (table for SQL adapters)
    protected static $_datasource = "authors";
 
    // Define your fields as public class properties
	public static function fields ()
	{
		return array
		(
			'id' => array ('type' => 'int', 'primary' => true, 'serial' => true),
			'firstname' => array ('type' => 'string', 'required' => true),
			'lastname' => array ('type' => 'string', 'required' => true),
			'sex' => array ('type' => 'string')
		) ;
	}
 
	public static function relations ()
	{
		// Authors Relationship
		return array
		(
			'books' => array 
			(
				'type' => 'HasMany', 
				'entity' => 'Books', 
				'where' => array ('author_id' => ':entity.id'),
				'order' => array ('title' => 'ASC')
			)
		) ;
	}
}

As you can see the Entity defines both the fields it cares about as well as foreign key related fields. The naming conventions are pretty standard. Note the $_datasource property which defines the actual data table where the entity is stored. I don’t think this is necessary in the above example, but I wanted to include it as a reference for how you might refer to an authors mapper where the table name was something other than authors. Also note the use of the singular author_id in the HasMany definition array. This is by convention. Now let’s use this Entity to do something useful. Let’s get a single author.

1
2
3
4
5
$author = $mapper->first ('Authors', array ('lastname' => $lastname)) ;
if ($author !== false)
{
	print $author->lastname . ", " . $author->firstname . "(" . $author->sex . ")</br>" ;
}

As you can see it is trivial to access the individual record retrieved from the DB. In this example there could be more than one authors with the same last name, but only the first one would be returned and acted upon. Now let’s create a new author entry.

1
2
3
4
5
$author = new Authors () ;
$author->firstname = "John" ;
$author->lastname = "Smith" ;
$author->sex = "Male" ;
$mapper->save ($author) ;

Again, pretty simple stuff. The Data Mapper pattern takes care of all the dirty translation into the underlying data store. You just work at the object level which is far more intuitive for a programmer. Updating the entity is just as easy. You simply retrieve the entity you want to update as shown above, assign new values to the properties you want to change and call the same save method on the mapper class. That’s it. It looks like this.

1
2
3
$author = $mapper-> first ("Authors", array ("firstname" => "John", "lastname" => "Smith")) ;
$author->sex = "Female" ;
$mapper->save ($author) ;

What is John up to anyway? Well, at least you get the idea. Deleting the record is just as easy. Here is an example.

1
2
$author = $mapper-> first ("Authors", array ("firstname" => "John", "lastname" => "Smith")) ;
$mapper->delete ($author) ;

That’s all there is to deleting. Now let’s look at some other useful commands. Here we select all entities matching a specific criteria and order the result.

1
2
3
4
5
6
7
8
$authors = $mapper->all ("Authors", array ("lastname" => $author_sirname, "sex" => "Male"))->order (array ("lastname" =>"desc")) ;
if (! $authors === false)
{
	foreach ($authors as $author)
	{
		// Do something
	}
}

Here is a more complicated example using where.

1
2
3
$authors = $mapper->all ("Authors", array ("id" => 15))
	->where (array ("lastname :like" => $sirname, "firstname :like" => $name), "OR", "AND")
	->order (array ("lastname" =>"desc")) ;

Shifting examples a bit here, below is an actual working example of a HasManyThrough relationship. In this example the Tags table is related to the Items table through the Items_Tags table.

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
/*
	Tags Mapper
*/
class Tags extends Spot\Entity
{
	// Specify the data source (table for SQL adapters)
	protected static $_datasource = "tags";
 
	// Define your fields as public class properties
	public static function fields ()
	{
		return array
		(
			'id' => array ('type' => 'int', 'primary' => true, 'serial' => true),
			'name' => array ('type' => 'string', 'required' => true),
			'user_id' => array ('type' => 'int', 'index' => true, 'required' => true)
		) ;
	}
 
	// Items Relationship
	public static function relations ()
	{
		return array
		(
			'items' => array
			(
				'type' => 'HasManyThrough',
				'entity' => 'Items',
				'where' => array ('id' => ':throughEntity.items_id'),
				'throughEntity' => 'Items_Tags',
				'throughWhere' => array ('tags_id' => ':entity.id'),
				'order' => array ('created_on' => 'desc')
			)
		) ;
	}
}

Based on this example we can select all Items associated with a group of tags. Two things to note about the example below. First, the user_id is another relation of a tag to a user which is defined in the tag’s entity. Second, the nested foreach behavior is (I think, but I can’t recall for certain) an implementation of lazy loading of the associated item records for the tags.

1
2
3
4
5
6
7
8
9
10
11
12
13
$tags = $mapper->all ('Tags', array ('id' => (int) $tag, 'user_id' => (int) config::$id)) ;
if (! $tags === false)
{
	$buffer = "" ;
	foreach ($tags as $tag)
	{
		foreach ($tag->items as $item)
		{
			$buffer .= $item->something_useful ;
		}
	}
	return $buffer ;
}

So that’s it. A quick and easy introduction to the basic use of Spot. If you are interested then let Vance Lucas (the author) know and hopefully he’ll put more time into developing the code and the documentation into an even more accessible ORM than it already is. Thanks for your interest and good luck with Spot.

No comments found. Please enter a comment if you have a question or contribution.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">