ActiveRecord Angst… ReActivated.
Surely the last thing PHP needs is yet another interpretation of Active Record right?
Maybe, but this stuff is such fun to code, and for me all of those valiant copycat attempts still fall short in comparison to the elegant Ruby on Rails version.
The thing I admire most about Rails' ActiveRecord is the total clarity with which sub-classes represent the database table, and instances of those classes represent a row in that table. For well documented reasons, PHP5 still can't pull off this representation, and so most attempts use workarounds that kind of get there in the end, but sacrifice all of the Ruby elegance along the way.
My own hack is to use static methods in the base class to retrieve instances of subclasses, so that you can use:
$person = MyActiveRecord::FindById('Person', 12);
to find an instance of the Person subclass, rather than the preferable, but impossible:
$person = Person::FindById(12);
You can consider the base MyActiveRecord class as a kind of static Object/Relational Mapper whilst instances of its subclasses represent specific table rows.
It's a kludge, but still better than something like:
$dummy = new Person(); $person = $dummy->find_by_id(12);
Which in my view is just plain wrong architecturally, and also more awkward in use.
Here's a proof-of-concept class you can download under a BSD license. It has some limitations but in fact these might be advantages if you're after a quick and convenient ORM class to do your bidding, rather than a full-stack regime to tell you what to do. The double-edged limitations in question are:
No Upfront Declarations
To represent a table as a class you need do the following:
define ('MYACTIVERECORD_CONNECTION_STR', 'mysql://un:pw@yourhost/yourdb');
include 'MyActiveRecord.php';
class Person extends MyActiveRecord {}
And that is all. Obviously this requires some strict schema rules in your database. Each database table is named identically to the class that will represent it (is that really so bad?). Each table must have an auto-incrementing id and foreign keys must be named in the form ForeignTable_id.
The point is that you don't need to make any declarations in a constructor or configuration file. This holds for relationships too. There's no need to declare a one_to_many relationship upfront if you're going to use methods like this:
$articles = $person->find_children('Article');
Of course you might want to add composite properties, validation rules, clean-up routines and so on, but these can be achieved very simply by overloading the methods
set_properties(), save() and destroy().
MySQL only
It's LAMP right? Not LAPP, LAOP, LASSP or LADP. Despite the accusations regularly levelled at MySQL, I think that's what most of us use and if you believe in getting everything through your ORM then there's a good argument that you really want your database to play dumb.
Apart from taking out the overhead of a database abstraction layer, knowing that it's MySQL we're connecting to enables us to leverage some vendor-specific retrieval methods, and offload type-conversion to the database server.
PHP 4 Compatible
If anything, the class runs better under PHP5, but PHP4 is still prevalent in most hosting environments including MADE's main web-server and this is intended to be a convenient, simple class rather than a paragon of PHP architecture. For that reason PHP4 compatibility is maintained, but that means
- triggering errors rather than exceptions
- 'StaticMethods' are implied by naming convention rather than enforced by the static keyword
- not leveraging
__get(),__set()or__call(), all magic methods that could be really handy in a class like this...
It's quite big
You might easily argue that the class is monolithic and attempts to take on too much of the problem. It might be nicer to abstract the database-connection, table, column, errors and validation-routines into their own classes, but then before you know it you have an ORM framework that requires a book of its own to understand and that's absolutely what I'm NOT trying to do.
Not transactional
It's really not ACID and I'm not sure how I would rework it to make it so. That's actually a really strong argument for looking at a separate Mapper Class/Data Object Class, as the Mapper can also double as a Unit Of Work. This is the approach we take in our own framework StageBlocks...
So if none of the above puts you off, download the class and give it a whirl. I'm not aware of anything that plain doesn't work, but would really welcome any bug-fixes or suggestions for improvement.

Thanks for this class. It’s pretty handy (compared to the other ActiveRecord implementations out there for PHP 4). I made a tweak to the populate method, to handle date and datetime fields, so I thought I’d share:
function populate($arrVals)
{
if( is_array($arrVals) )
{
$table = MyActiveRecord::Class2Table(get_class($this));
foreach($arrVals as $key=>$val)
{
if(MyActiveRecord::GetType($table, $key) 'datetime')
{
$this->set_datetime($key, MyActiveRecord::TimeStamp($val));
}
else if(MyActiveRecord::GetType($table, $key) ‘date’)
{
$this->set_date($key, MyActiveRecord::TimeStamp($val));
}
else
{
$this->$key=$val;
}
}
return true;
}
else
{
return false;
}
}