2010-11-03

Models can be so Jaded

I am currently working on a PHP framework which I've been calling JadedPHP. It's purpose it to provide a lightweight MVC framework for some of my personal projects. Basically, I'm after a learning experience that provides useful output for me. If someone else gets use out of it as well, that's just icing on the cake.

My feelings on PHP frameworks in general are that they abstract way too much of the application's structure from the developer. I like to dig in and really know what's going on with the code that's running my code. Jaded is an attempt to build as little "automagic" into the framework as possible, and thus maximize the flexibility given to the developer to borrow bits and pieces as needed, and to override the rest if necessary.

The model layer is pretty solid so far. Models in Jaded are broken into 3 parts: a definition, a store, and a container.

The definition, oddly enough, defines the model. This means it lists the available fields of the model, which of those fields are key fields that uniquely identify the record held by the model, and any default values for those fields. Basically, the definition gives the model's structure, what it looks like.

A model store is the actual storage mechanism for the model. It implements basic CRUD operations, and is a place to define additional data manipulation tasks. The base class for model stores does not specify what the storage mechanism is. It could be a database, a CSV or XML file, or some volatile cache. It is up to concrete stores to actually implement the CRUD operations for a given model. (In reality, Jaded comes with basic database store that performs one model -> one row mapping using Jaded's PDO wrapper class.)

Finally, there is the model container itself. The container defines which definition and store the model will use. It is also responsible for holding the individual field values for a given record. It provides basic getter and setter functions, as well as the ability to fill a model from an array of values or spit out an array containing the values.

So how does this look? Let's pretend we have a database table that looks like this:
CREATE TABLE ducks (
    duckid int NOT NULL AUTO_INCREMENT,
    type int NOT NULL,
    name varchar(20) NULL,
    sound varchar(10) NULL,
    PRIMARY KEY duckid (duckid)
);

The definition for a Duck model would look something like this:
class DuckDefinition extends Jaded_Model_Definition
{
    const TypeMallard = 0;
    const TypeWood = 1;

    /**
     * Maps a name that calling code can use to an internal field name
     * Note that they do not have to match, and there can be multiple
     * aliases to a single internal name.
     */
    protected $aFieldMap = array(
        "duckid" => "duckid",
        "type"   => "type",
        "name"   => "name",
        "noise"  => "sound",
        "sound"  => "sound"
    );

    /**
     * The key fields for this model
     * Fields that uniquely identify it
     * A key of "auto" means the key is automatically set by the store,
     * else use "key"
     */
    protected $aKeyFields = array(
        "duckid" => "auto"
    );

    /**
     * Defaults for any fields
     */
    protected $aDefaultValues = array(
        "type"  => self::TypeMallard,
        "sound" => "quack"
    );
}

And now the store. In this case, I'm cheating and using the built-in basic database store:
class DuckStore extends Jaded_Model_Store_Database
{
    protected $sTable = "ducks";

    /**
     * This bit is simply the connection name used by Jaded's database wrapper
     */
    protected $sDbId = "duck_database";
}

And finally, a model container that wraps it all up:
class Duck extends Jaded_Model
{
    protected $sDefaultDefinition = "DuckDefinition";
    protected $sDefaultStore      = "DuckStore";
}

And now a bit of usage:

$oDuck = new Duck();
echo $oDuck->getType();    // prints "0"
echo $oDuck->getSound();   // prints "quack"

$oDuck->setName("Donald");
$oDuck->create();
$iDuckId = $oDuck->getDuckId();

$oDuck2 = new Duck();
$oDuck2->setDuckId($iDuckId);
$oDuck2->load();
echo $oDuck2->getName();   // prints "Donald"

// Now let's pretend we need to migrate all ducks to a CSV file, and we have a store for that
class DuckCSV extends Duck
{
    protected $sDefaultStore      = "DuckCSVStore";
}

$oDuck3 = new DuckCSV($oDuck2);
$oDuck->update();
Note how when we need to store the model in a different storage medium, we can just change the store type, and keep the definition and any methods that might have been built into the Duck class.

If the model's are stored in a database one row per model object, Jaded has a lot of functionality built in. But it also provides the flexibility to build model's that have the same definition, but have a store that pushes to/pulls from an RSS feed, or Twitter, or a stock ticker, or any other data source. For only a little extra setup, you get a lot of options.

There will probably be a GitHub repository soon, and another post or two as I start to use Jaded in more projects.

2 comments:

  1. Hi Josh. Good stuff, but, why not just use MVC with .NET ? :)

    ReplyDelete
  2. Good point. Or why not use one of the hundred other MVC frameworks in PHP? My real goal is to try and learn more about how those frameworks are built in general, and have something I can use in personal projects that I fully understand every line of code in the framework.

    ReplyDelete