Working with Model

Chyrp Lite's Model class provides a powerful data model for objects that are constructed using SQL data. The core object types of Post, Page, User, and Group all extend the Model class, as do the Category, View, Like, Pingback, and Comment objects provided by modules. Extending the Model class means that these object types present a standard interface to their data with transparent caching for improved performance, it allows the objects to easily define how they interrelate, and it allows them to be extended with additional attributes using trigger responders.

Let's take a look at what Model can do for a hypothetical FooBar class that extends Model.

Constructing an Object

FooBar's __construct() method can call parent::grab() with the arguments $this and $foobar_id to handle the SQL query and populate the new object's attributes using the results. The object will be cached so that instantiating the same object repeatedly will not cause duplicate SQL queries. The query option 'read_from' => allows an object to be populated from a supplied associative array of values (see placeholders below) instead of query results.

There are two requirements for grab() to work:

  1. The database table name must be the pluralised version of the model name (e.g. "foobars").
  2. The database table must have an "id" column that uniquely identifies each row.

If the query returns no results, the attribute FooBar->no_results will be set to true.

Searching for Objects

FooBar can provide a FooBar::find() method so that callers can request an array of results that match certain criteria without the need for custom SQL queries. All find() needs to do is accept an associative array of query options and to call parent::search() with the arguments self::class and $options. The query option 'placeholders' => true can be specified by callers to determine whether the results of the search will be returned as an array of simple arrays, or as an array of instantiated objects.

If the search returns no results, an empty array will be returned.

Editing and Deletion

FooBar inherits the methods editable() and deletable() from Model. These methods can be used by callers to determine if a FooBar can be edited or deleted by a user. In the case of FooBar, the user must have the privileges "edit_foobar" and "delete_foobar" in order for these methods to return true.

FooBar also inherits the methods edit_link() and delete_link() that can be used to generate a hyperlink to corresponding edit/delete pages in the administration console. In the case of FooBar, the URLs will have route actions of "edit_foobar" and "delete_foobar", to which our extension must respond.

Destroying an Object

FooBar can provide a delete() method that simply calls parent::destroy() with the arguments self::class and $foobar_id. Before deleting the specified row from the table, the trigger "delete_foobar" will be called if it exists.

Deferred Attributes and Object Interrelations

Model implements the magic __isset() and __get() methods, enabling FooBar to have attributes that are added at runtime by trigger responders, or attributes that are based on its relationship to other data models. When these magic methods are called, they will first check for the existence of a trigger that can provide the value for the requested attribute.

For example, our extension can provide a method named foobar_baz_attr() that will be called when the value of the object attribute FooBar->baz is requested. The Categorize and Likes modules use this functionality to provide Category->post_count and Post->like_count as deferred attributes, thus avoiding the need for wasteful SQL queries if the attributes are never requested.

Attributes can also be populated based on FooBar's relationship to other models. FooBar can specify either "belongs_to", "has_one", or "has_many" attributes containing the names of other data models with which it has a relationship.

For example, FooBar can specify FooBar->belongs_to = "post" and when the attribute name FooBar->post is requested, the magic getter will return the Post object to which this instance of FooBar belongs. In order for this to work, FooBar->post_id must exist and contain the unique Post ID.