October CMS 提供一个美观简洁的 Active Record 实现,用于处理您的数据库,基于 Laravel 的 Eloquent。每个数据库表都有一个对应的“模型”,用于与该表进行交互。模型允许您查询表中的数据,以及向表中插入新记录。
模型类位于插件目录的models子目录中。模型目录结构的一个示例。
├── plugins
| └── acme
| └── blog
| ├── models
| | ├── post ← 配置目录
| | | ├── fields.yaml ← 配置文件
| | | └── columns.yaml ← 配置文件
| | └── Post.php ← 模型类
| └── Plugin.php
模型配置目录可能包含模型的表单字段定义。 模型配置目录名称与小写的模型类名匹配。
create:model 命令生成新模型所需的文件。第一个参数指定作者和插件名称。第二个参数指定模型类名。
php artisan create:model Acme.Blog Post在大多数情况下,你应该为每个数据库表创建一个模型类。所有模型类都必须继承 Model 类。在插件内部使用的模型最基本的表示形式如下所示。
namespace Acme\Blog\Models;
use Model;
class Post extends Model
{
protected $table = 'acme_blog_posts';
}$table 受保护字段指定了模型对应的数据库表。表名是作者、插件和复数形式的记录类型名称的蛇形命名。
在模型上可以找到一些标准属性,除了模型特性提供的属性之外。例如:
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. |
模型将会假定每张表都有一个命名为 id 的主键列。您可以定义一个 $primaryKey 属性来覆盖此约定。
class Post extends Model
{
protected $primaryKey = 'id';
}模型将假定主键是一个自增的整型值,这意味着默认情况下主键将自动转换为整型。如果你希望使用非自增或非数字的主键,你必须将公共 $incrementing 属性设置为 false。
class Message extends Model
{
public $incrementing = false;
}默认情况下,模型会期望您的表上存在 created_at 和 updated_at 列。如果您不希望自动管理这些列,请将模型上的 $timestamps 属性设置为 false。
class Post extends Model
{
public $timestamps = false;
}如果你需要自定义时间戳的格式,请在你的模型上设置 $dateFormat 属性。此属性决定了日期属性在数据库中的存储方式,以及模型序列化为数组或 JSON 时的格式。
class Post extends Model
{
protected $dateFormat = 'U';
}当属性名传递给 $jsonable 属性时,其值将作为 JSON 从数据库中被序列化和反序列化。
class Post extends Model
{
protected $jsonable = ['data'];
}模型会触发多个事件,允许您介入模型生命周期中的各个点。事件允许您轻松执行代码,每当特定的模型类在数据库中被保存或更新时。事件通过覆盖类中的特殊方法来定义,以下方法覆盖可用:
| 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. |
以下是使用事件的一个示例。
public function beforeCreate()
{
$this->slug = Str::slug($this->name);
}每次新模型首次保存时,beforeCreate 和 afterCreate 事件都会触发。如果模型已存在于数据库中,并且 save 方法被调用,则 beforeUpdate / afterUpdate 事件都会触发。然而,在这两种情况下,beforeSave / afterSave 事件都会触发。
例如,让我们定义一个事件监听器,它会在模型首次创建时填充 slug 属性。
public function beforeCreate()
{
$this->slug = Str::slug($this->name);
}从事件返回 false 将取消 save / update 操作。
public function beforeCreate()
{
if (!$user->isValid()) {
return false;
}
}可以使用 original 属性访问旧值。 例如:
public function afterUpdate()
{
if ($this->title !== $this->original['title']) {
// Title has changed
}
}您可能会发现,使用 延迟绑定 创建的关系(即:文件附件)尚未在模型事件中可用。要尽早访问它们,请在该关系上使用 withDeferred 数据库查询方法。
public function beforeCreate()
{
$avatar = $this->avatar()->withDeferred()->first();
$gallery = $this->gallery()->withDeferred()->get();
}您可以外部绑定到 本地事件 用于模型的单个实例 使用 bindEvent 方法。事件名称应与方法覆盖名称相同,并以 model. 作为前缀。
$flight = new Flight;
$flight->bindEvent('model.beforeCreate', function() use ($model) {
$model->slug = Str::slug($model->name);
});由于模型能够使用行为,它们可以使用静态 extend 方法进行扩展。该方法接受一个闭包,并将模型对象传入其中。
在闭包内部,你可以向模型添加关系。这里我们扩展 User 模型,使其包含一个引用 Profile 模型的配置文件(一对一)关系。
User::extend(function($model) {
$model->hasOne['profile'] = [Profile::class, 'key' => 'user_id'];
});这种方法也可用于绑定本地事件,以下代码监听 model.beforeSave 事件。
User::extend(function($model) {
$model->bindEvent('model.beforeSave', function() use ($model) {
// ...
});
});此外,存在一些方法来扩展受保护的模型属性。
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');
});通常,放置代码的最佳位置是在您的插件注册文件boot方法中,因为这将在每个请求时运行,确保您对模型所做的扩展在任何地方都可用。