Models
Map the database table columns directly to a PHP object.
October CMS provides a beautiful and simple Active Record implementation for working with your database, based on Eloquent by Laravel (opens new window). Each database table has a corresponding "Model" which is used to interact with that table. Models allow you to query for data in your tables, as well as insert new records into the table.
Model classes reside in the models subdirectory of a plugin directory. An example of a model directory structure.
├── plugins
| └── acme
| └── blog
| ├── models
| | ├── post ← Config Directory
| | | ├── fields.yaml ← Config File
| | | └── columns.yaml ← Config File
| | └── Post.php ← Model Class
| └── Plugin.php
The model configuration directory could contain the model's form field definitions. The model configuration directory name matches the model class name written in lowercase.
# Defining Models
The create:model
command generates the files needed for a new model. The first argument specifies the author and plugin name. The second argument specifies the model class name.
php artisan create:model Acme.Blog Post
In most cases, you should create one model class for each database table. All model classes must extend the Model
class. The most basic representation of a model used inside a Plugin looks like this.
namespace Acme\Blog\Models;
use Model;
class Post extends Model
{
protected $table = 'acme_blog_posts';
}
The $table
protected field specifies the database table corresponding the model. The table name is a snake case name of the author, plugin and pluralized record type name.
# Supported Properties
There are some standard properties that can be found on models, in addition to those provided by model traits. For example:
class User extends Model
{
protected $primaryKey = 'id';
public $exists = false;
protected $dates = ['last_seen_at'];
public $timestamps = true;
protected $jsonable = ['permissions'];
protected $guarded = ['*'];
}
Property | Description |
---|---|
$primaryKey | primary key name used to identify the model. |
$incrementing | boolean that if false indicates that the primary key is not an incrementing integer value. |
$exists | boolean that if true indicates that the model exists. |
$dates | values are converted to an instance of Carbon/DateTime objects after fetching. |
$timestamps | boolean that if true will automatically set created_at and updated_at fields. |
$jsonable | values are encoded as JSON before saving and converted to arrays after fetching. |
$fillable | values are fields accessible to mass assignment. |
$guarded | values are fields guarded from mass assignment. |
$visible | values are fields made visible when serializing the model data. |
$hidden | values are fields made hidden when serializing the model data. |
$connection | string that contains the connection name that's utilised by the model by default. |
# Primary Key
Models will assume that each table has a primary key column named id
. You may define a $primaryKey
property to override this convention.
class Post extends Model
{
protected $primaryKey = 'id';
}
# Incrementing
Models will assume that the primary key is an incrementing integer value, which means that by default the primary key will be cast to an integer automatically. If you wish to use a non-incrementing or a non-numeric primary key you must set the public $incrementing
property to false.
class Message extends Model
{
public $incrementing = false;
}
# Timestamps
By default, a model will expect created_at
and updated_at
columns to exist on your tables. If you do not wish to have these columns managed automatically, set the $timestamps
property on your model to false
.
class Post extends Model
{
public $timestamps = false;
}
If you need to customize the format of your timestamps, set the $dateFormat
property on your model. This property determines how date attributes are stored in the database, as well as their format when the model is serialized to an array or JSON.
class Post extends Model
{
protected $dateFormat = 'U';
}
# Values stored as JSON
When attributes names are passed to the $jsonable
property, the values will be serialized and deserialized from the database as JSON.
class Post extends Model
{
protected $jsonable = ['data'];
}
# Model Events
Models fire several events, allowing you to hook into various points in the model's life cycle. Events allow you to easily execute code each time a specific model class is saved or updated in the database. Events are defined by overriding special methods in the class, the following method overrides are available:
Event | Description |
---|---|
beforeCreate | before the model is saved, when first created. |
afterCreate | after the model is saved, when first created. |
beforeSave | before the model is saved, either created or updated. |
afterSave | after the model is saved, either created or updated. |
beforeValidate | before the supplied model data is validated. |
afterValidate | after the supplied model data has been validated. |
beforeUpdate | before an existing model is saved. |
afterUpdate | after an existing model is saved. |
beforeDelete | before an existing model is deleted. |
afterDelete | after an existing model is deleted. |
beforeRestore | before a soft-deleted model is restored. |
afterRestore | after a soft-deleted model has been restored. |
beforeFetch | before an existing model is populated. |
afterFetch | after an existing model has been populated. |
The following is an example of using an event.
public function beforeCreate()
{
$this->slug = Str::slug($this->name);
}
# Basic Usage
Whenever a new model is saved for the first time, the beforeCreate
and afterCreate
events will fire. If a model already existed in the database and the save
method is called, the beforeUpdate
/ afterUpdate
events will fire. However, in both cases, the beforeSave
/ afterSave
events will fire.
For example, let's define an event listener that populates the slug
attribute when a model is first created.
public function beforeCreate()
{
$this->slug = Str::slug($this->name);
}
Returning false
from an event will cancel the save
/ update
operation.
public function beforeCreate()
{
if (!$user->isValid()) {
return false;
}
}
It's possible to access old values using the original
attribute. For example:
public function afterUpdate()
{
if ($this->title !== $this->original['title']) {
// Title has changed
}
}
You may find relationships created using deferred bindings (i.e: file attachments) are not available inside model events yet. To access them early, use the withDeferred
database query method on the relation.
public function beforeCreate()
{
$avatar = $this->avatar()->withDeferred()->first();
$gallery = $this->gallery()->withDeferred()->get();
}
You can externally bind to local events for a single instance of a model using the bindEvent
method. The event name should be the same as the method override name, prefixed with model.
.
$flight = new Flight;
$flight->bindEvent('model.beforeCreate', function() use ($model) {
$model->slug = Str::slug($model->name);
});
# Extending Models
Since models are equipped to use behaviors, they can be extended with the static extend
method. The method takes a closure and passes the model object into it.
Inside the closure you can add relations to the model. Here we extend the User
model to include a profile (has one) relationship referencing the Profile
model.
User::extend(function($model) {
$model->hasOne['profile'] = [Profile::class, 'key' => 'user_id'];
});
This approach can also be used to bind to local events, the following code listens for the model.beforeSave
event.
User::extend(function($model) {
$model->bindEvent('model.beforeSave', function() use ($model) {
// ...
});
});
Additionally, a few methods exist to extend protected model properties.
User::extend(function($model) {
// Add cast attributes
$model->addCasts([
'some_extended_field' => 'int',
]);
// Add a date attribute
$model->addDateAttribute('updated_at');
// Adds fillable or jsonable fields
$model->addFillable('first_name');
$model->addJsonable('some_data');
});
Typically the best place to place code is within your plugin registration file boot
method as this will be run on every request ensuring that the extensions you make to the model are available everywhere.
# See Also
← Plugins Controllers →