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:
- The database table name must be the pluralised version of the model name (e.g. "foobars").
- 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.