updated as of: March 30, 2015
last author: Andy Theuninck
The latest documentation can be found on the Project Wiki. The information below may be out of date.
BasicModel is the base class of a very light weight ORM (object relational mapping). Most subclasses are PHP classes that correspond to tables or views in the database. One advantage of having a table definition in code is it's possible to do runtime analysis comparing the class definition to the underlying and table and cope with differences intelligently. It can also generate SQL for simple queries which typically saves on boilerplate code.

Creating Models

The BasicModel class has some command line functionality assist in writing and maintaining other model classes. To generate a new model, run:
php BasicModel.php --new TableName
OR
php BasicModel.php --new-view TableName
This will create a skeleton class named TableNameModel.php. The chief difference with using --new-view is some functionality that doesn't make sense in a read-only context is automatically disabled. You can use a regular model with a view, but attempting to add/change/delete a record may not work depending on the underlying database.

There are two important properties to define in the new skeleton class. The first is $name. By default this would be TableName in the example. In theory per coding standards both tables should use StudlyCaps, but there's a long ways to go in fully implementing that. When retrofitting new model classes to old tables, the new class should use StudlyCaps but the underlying $name does not have to match - i.e., ProductsModel corresponds to the table products.

The other property that must be defined is $columns. This is a keyed array where the column keys are the column names and the array values are the column definitions. A column definition may include the following attributes:

Example:
class TableNameModel extends BasicModel
{
    protected $name = 'TableName';
    
    protected $columns = array(
    'tableNameID' => array('type'=>'INT', 'increment'=>true, 'primary_key'=>true),
    'columnOne' => array('type'=>'VARCHAR(50)', 'index'=>true),
    'columnTwo' => array('type'=>'TINYINT', 'default'=>0), 
    );    
}
A third property that may be defined for the class is $unique. Some operations must be able to isolate a single, unique record in the table. By default, the primary key column(s) are used for the uniqueness constraint. You may override this by defining $unique with array of column names. This can useful if a table has an integer ID column but is most often referenced using another logically unique column.

Lastly, after defining all the $columns, you need to run BasicModel's generator to create the methods for each column.

php BasicModel.php TableNameModel.php

Using Models

The constructor takes one argument, an SQLManager connection object. An object has a method for each column to get or set that column's value. Continuing with the example:
$tableName = new TableNameModel($sqlInstance);
// get column value
$value = $tableName->columnOne();
// set column value
$tableName->columnOne($newValue);
When a model object is first instantiated, it does not contain any data. The get methods will simply return the default value for the column. Similarly, the set methods are just updated the object. Data is loaded from the database and written to the database using the load() and save() methods, respectively.

To load() a record from the database, you assign values to the unique column(s) of the object. This specifies which record will be loaded from the database. The load method requires a primary key or unqiueness constraint. A successful load will set all columns in the object to the values in the underlying record. Load fails and returns false is the unique column(s) do not have values, if no record in the database matches the unique column value(s), or if the table itself does not exist. Example:

$tableName->tableNameID(1);
$tableName->load();
The reverse of load() is save(). The save method writes the object's current column values to the database. If the object's unique columns are defined, save() will perform either an INSERT or an UPDATE depending on whether the corresponding record exists. If the unique columns do not have values (or no uniqueness constraint has been defined), save() will always perform an INSERT. Note that save will only write columns that are set in the current instance. For instance, this would only update products.cost and leave the remainder of the record unchanged:
$product = new ProductsModel($sqlInstance);
$product->upc('0000000004011');
$product->cost(0.99);
$product->save();
Finally, model objects have a find() method for looking up multiple records. Simply calling find() on a new object will return all records in the table. Specifically, find returns an array of model objects whose columns are fully populated with data from the underlying records. These are new objects; find() does not modify the object that it is called on. Filters can be applied by adding a second argument containing an SQL operator such as <, <=, >, >=, =, !=, or <>. Filters are cummulative and repeated calls will add more conditions to the query. Some examples:
// WHERE tableNameID < 5
$tableName->tableNameID(5, '<');

// For backwards compatibility purposes, these are identical filters
// The first one does update the object's columns while the
// second one does not
$tableName->columnTwo(0);
$tableName->columnTwo(0, '=');

// There is an optional third argument for rare situations
// where the value matches one of the column names. 
// WHERE TableName.columnOne <> TableName.columnTwo
$tableName->columnOne('columnTwo', '<>');
// WHERE TableName.columnOne <> 'columnTwo'
$tableName->columnOne('columnTwo', '<>', true);

Updating Schemas

Because the model classes contain an extensive table definition, they can analyze the actual database table for discrepancies and suggest updates. Models can create tables, add columns to tables, and rename columns. They will not drop columns from tables although they will notify the user of the discrepancy between the definition and the actual table.

Updates from the command line require a database name to open the correct connection. There is a confirmation prompt before applying any updates. CLI synatx:

php BasicModel.php --update  TableNameModel.php
Two extra class properties are relevant to updates. The $preferred_db can be set to "op" or "trans". This is strictly for the web GUI to apply updates correctly. CLI updates will always respect the database value provided. The other property is $normalize_lanes. If this property is true, updates will be applied to the server's table as well as the corresponding table on any configured lanes.

To add a new column, simply define it in the array of columns then re-run the command to generate getters and setters. To rename a column, specify the name of the column it replaces in the definition. Changing properties of a column such as type or increment may work depending on the underlying database.

If a method exists named 'hookAddColumnNAME' (where NAME is the name of the new column), that method will be called after creating the column. This can be used to populate data into that column.

Every effort should be made to avoid creating SQL errors in code by changing the database. INSERTs are the most common problem. Using model classes to write records can simplify this otherwise manually checking whether a column exists can work too. Put a dated comment near this kind of code to give an indication when the schema changed.