In this post, two possible implementations of the ActiveRecord design pattern will be discussed. With the raise of Ruby on Rails, ActiveRecord became very popular to the IT crowd.
To put the concepts of this pattern in a nutshell:
"ActiceRecord = Data + Business Logic."
An ActiveRecord object maps a row in a database table to an object in your webapplication (Data). This record is flavoured with object logic and even some domain logic (Business Logic). I won't repeat too much details as the ActiveRecord pattern is exhaustive discussed elsewhere.
As introduced in Rails, all the object relational mapping code is encapsulated in a very powerful ActiveRecord base class. Following the convention over configuration paradigm, you derive your model from this base class - having all the power of a database at your fingertips.
Implementing the ActiveRecord design pattern in PHP is not very easy. There have been many attempts by various frameworks but none of it has an elegant implementation like in Rails. Flinn Mueller pointet out that because of a lack of PHPs language core features it is impossible to create an such an elegant implementation.
Using the AR pattern typically looks like this:
-
class Blogentry extends ActiveRecord {
-
}
-
-
$blogentry = new Blogentry;
-
$blogentry->title = "Pac Man eats my Panties!";
-
$blogentry->save();
-
-
$otherentry = Blogentry::find_by_id(3);
Usually the save or find_by_id method is implemented in the ActiveRecord base class. Before we had Late Static Binding (arrived in PHP 5.3) it was almost impossible to have a nice implementation of this pattern. E.g. when you call Blogentry::find_by_id(3), which is implemented in the ActiveRecord base class, it is impossible to derive that the method is statically invoked by the derived Blogentry class.
First I'm going to show you the LSB based implementation:
-
class ActiveRecord {
-
$class = get_called_class(); // LSI magic comes from here
-
$sql = 'SELECT FROM `' . $class . '` WHERE `id`=\'' . $id . "';";
-
echo "Generate sql for select by id\n";
-
return(new $class);
-
}
-
-
function save() {
-
-
echo "Generate sql for new Record:\n";
-
-
$sql = 'INSERT INTO `' . $class . '` ';
-
$sql_fields = '(';
-
$sql_values = '(';
-
foreach($this->fields as $field => $type) {
-
if($field != 'id') {
-
$sql_fields .= '`' . $field . '`,';
-
$sql_values .= "'" . $this->{$field} . "',";
-
}
-
}
-
$this->id = 1;
-
} else {
-
echo "Generate sql for Update:\n";
-
-
$sql = 'UPDATE `' . $class . '` SET ';
-
foreach($this->fields as $field => $type) {
-
if($field != 'id') {
-
$sql .= '`' . $field . '`=\'' . $this->{$field} . '\',';
-
}
-
}
-
}
-
-
}
-
}
-
?>
The magic lies in the get_called_class method - this function returns the class where the function was statically called from.
This is quite elegant but it brings up some problems. As mentioned in Kores rant "Why Active Record Sucks", deriving your model from an ActiveRecord base class will disturb your code semantics.
To solve this problem I used the very promising Traits patch from Stefan Marr. Traits are like interfaces with code - by using them, you can simulate multiple inheritance (Ruby has a similar concept called mixins). And here comes the beef:
-
trait ActiveRecord {
-
$sql = 'SELECT FROM `' . $class . '` WHERE `id`=\'' . $id . "';";
-
echo "Generate sql for select by id\n";
-
return(new $class);
-
}
-
-
function save() {
-
-
echo "Generate sql for new Record:\n";
-
-
$sql = 'INSERT INTO `' . $class . '` ';
-
$sql_fields = '(';
-
$sql_values = '(';
-
foreach($this->fields as $field => $type) {
-
if($field != 'id') {
-
$sql_fields .= '`' . $field . '`,';
-
$sql_values .= "'" . $this->{$field} . "',";
-
}
-
}
-
$this->id = 1;
-
} else {
-
echo "Generate sql for Update:\n";
-
-
$sql = 'UPDATE `' . $class . '` SET ';
-
foreach($this->fields as $field => $type) {
-
if($field != 'id') {
-
$sql .= '`' . $field . '`=\'' . $this->{$field} . '\',';
-
}
-
}
-
}
-
-
}
-
}
-
?>
You simply add your functionality to your class by using the trait:
-
class Blogentry uses ActiveRecord {
-
'id' => 'int',
-
'title' => 'varchar',
-
'text' => 'text',
-
'created_on' => 'datetime'
-
);
-
}
I think the implementation with traits is much better than with late static binding because you don't have to worry about your code semantics and (almost) no need for magic functions (if you dig deeper into AR - say hello to __callStatic).
I hope that the trait feature will soon be added to the PHP Core.
Disclaimer: These examples are just Proof-Of-Concepts there is no database driver implemented yet. Currently we are working with PHP bleeding edge to have a tight implementation of a state of the art web framework. You can check it out at google code - but the current state is far from beeing stable.
Nice example illustrating the benefits of LSB and traits. However I think it is not very useful to bind the concrete classes against a specific way of persistence. In my point of view the BlogEntry class should not know anything about how it may be persisted, neither by extending a specific class nor by using a trait.
Different people – different flavours. I think it always depends on your case – I’ve just chosen ActiveRecord because it’s something you could not implement with PHP 5.2 and you can do now with LSB and/or traits. But why is mixing in persistance using Traits so bad?
I don’t say it is necessarily bad, I just think it is not a good idea. This way your container is still coupled to the persistence layer, while I prefer to not have it coupled with any persistence layer. Maybe I want to reuse the container, but with another persistence layer (XML, Application Server, whatsoever…) – with your implementation I still require the trait even if I don’t use a database.
I admit that this is a very pure architectural point of view and not always required for every application. :)
Maybe that’s an interesting point for the Traits RFC. What if we want a “Persistance Trait” which uses (by configuration) an XML Trait, Application Server, whatsoever Trait. How could we realize this and still keep the code DRY. If this problem could be solved you would have your persistance kept very tight to your model which is good (IMHO).
Nice introduction, traits is very interesting. And thank you for mailing this page. :)
One more point, I am not sure how was implemented, but in the initial version of LSB there was a logical hole (IMHO): you can’t overwrite a method and call in it the parent’s method.
Look at the code, with the LSB the methods won’t be overwritten.
Checkout the last example in my post: http://blog.felho.hu/what-is-new-in-php-53-part-2-late-static-binding.html , it show what is the problem with the current implementation.
I see… calling the static method through parent:: will break the binding. Let’s take traits ;-)
Hi! I was surfing and found your blog post… nice! I love your blog. :) Cheers! Sandra. R.