October CMS 提供了一个优美而简单的 Active Record 实现,用于操作你的数据库, 基于 Laravel 的 Eloquent。 每个数据库表都有一个对应的“模型” ,用于与该表进行交互。 模型允许你查询表中的数据, 以及向表中插入新记录。
模型类位于插件目录的模型子目录中。模型目录结构的示例。
├── 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 方法中,因为这将在每个请求上运行,确保你对模型所做的扩展在任何地方都可用。