企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## **简介** Eloquent 是一个 ActiveRecord ORM 框架,ORM 全称是 Object Relational Mapping,意为对象关系映射,用于实现面向对象编程语言里不同类型系统的数据之间的转换,简单来说,它会构建类与数据表之间的映射关系,从而建立起一个可在编程语言里使用的「虚拟对象数据库」。「ActiveRecord」是 ORM 的一种实现模式,Eloquent 则是 Laravel 版的「ActiveRecord」。 一个 Eloquent 模型类映射一张数据表,通过模型类提供的方法,你可以获取其映射的数据表的所有记录,也可以获取单条记录,还可以创建、更新和删除对应数据表记录,而这一切都不需要你编写任何 SQL 语句、或者构建查询构建器即可完成。 ## **模型类定义** 使用模型类之前,需要在数据库有对应的数据表,因为模型类就是数据表在面向对象编程语言中的映射。比如我们前面几篇教程中用到的`User`模型和`Post`模型都是这样,创建一个模型类,需要使用`make:model`命令: ~~~ php artisan make:model Post ~~~ > 注:如果对应的数据表尚未创建,你还可以在创建模型类的同时创建对应的数据库迁移文件,通过`php artisan make:model Post -m`即可。如果你想将模型类创建到`app/Models`目录下,可以这么运行上述命令`php artisan make:model Models/Post`。 新生成的`Post`模型类里面什么东西都没有,但是我们就可以通过它完成数据表记录的增删改查操作了,怎么做到的?这就是「约定优于配置」的功劳了。下面我们就来看看这些默认的约定。 - 表名:Eloquent 约定模型类映射表名是将类名由驼峰格式转化为小写+下划线(含多个单词的话),最后将其转化为复数形式,比如`Post`对应表名是`posts`、`PostTag`对应表名是`post_tags`等等。当然,如果你不想遵循这个系统约定的规则,也可以通过手动设置模型类属性的方式进行自定义:`protected $table= 'articles';`; - 主键:Eloquent 默认假设每张数据表都有一个整型的自增主键,其字段名为`id`,如果你的数据表主键名不是`id`,可以通过`$primaryKey`属性来指定——`protected $primaryKey = 'post_id';`;如果主键不是自增的,还可以设置`$incrementing`属性为`false`——`public $incrementing = false;`;如果主键不是整型,还可以设置`$keyType`属性为`string`——`protected $keyType = 'string';`; - 时间戳:Eloquent 默认约定每张表都有`created_at`和`updated_at`字段(迁移类中`$table->timestamps()`会生成这两个字段),并且在保存模型类时会自动维护这两个字段。如果你的数据表里面不包含这两个字段,或者只包含一个,都需要设置`$timestamps`属性为`false`——`public $timstamps=false;`;或者通过`CREATED_AT`和`UPDATED_AT`常量来设置自定义的创建和更新时间字段: ~~~ public const CREATED_AT = 'create_time'; public const UPDATED_AT = 'update_time'; ~~~ - 数据库连接:Eloquent 模型类默认约定的数据库连接是`config/database.php`中配置的默认连接,如果应用配置了多个数据库连接,可以通过`$connection`属性为模型类指定使用哪个连接: ~~~ protected $connection = 'connection_name'; ~~~ ## **查询数据** 日常开发中,大部分操作都是数据库中查询数据,Eloquent 模型了为我们提供了很多方法帮助我们从数据库中获取数据。 ### **获取所有记录** 我们可以通过模型类提供的`all`方法获取一张表的所有记录: ~~~ $posts = Post::all(); ~~~ 和查询构建器一样,该方法返回的也是集合,只不过是模型类集合: ![](https://img.kancloud.cn/1c/39/1c395f5b6672170f88dea9705b813df4_1844x1388.jpg) 如果结果集很大的话,类似于查询构造器,模型类也支持通过`chunk`方法分块获取查询结果:: ~~~ Post::chunk(10, function ($posts) { foreach ($posts as $post) { if ($post->views == 0) { continue; } else { dump($post->title . ':' . $post->views); } } }); ~~~ 除此之外,在 Eloquent 模型中还可以通过`cursor`方法每次只获取一条查询结果,从而最大限度减少内存消耗: ~~~ foreach (Post::cursor() as $post) { dump($post->title . ':' . $post->content); } ~~~ ### **获取指定查询结果** 如果想要指定查询条件和查询字段,可以通过`where`方法和`select`方法来实现: ~~~ $posts = Post::where('views', '>', 0)->select('id', 'title', 'content')->get(); ~~~ 实际上,Eloquent 模型类底层的查询也是基于[查询构建器]('')来实现的,你可以在模型类上调用所有查询构建器的[Where 查询方法](''),同样是以流接口的模式构建方法链调用即可。前面提到的`chunk`和`cursor`方法也适用于这种指定查询条件的查询操作。 因为是查询构建器,所以我们还可以在模型查询操作中对查询结果进行排序和分页: ~~~ $posts = Post::where('views', '>', 0)->orderBy('id', 'desc')->offset(10)->limit(5)->get(); ~~~ ### **获取单条记录** 你也可以通过查询构建器的方式在模型类查询中获取单条记录: ~~~ $user = User::where('name', '学院君')->first(); //返回一个模型类实例 ~~~ 如果查询的条件是主键 ID 的话,还可以将上述调用简化为通过`find`方法来实现: ~~~ $user = User::find(1); ~~~ 模型类查询结果为空会返回`null`。如果你想要在单条记录返回结果为空时返回 404 响应(在控制器方法中可能需要用到类似操作),可以通过`firstOrFail`或者`findOrFail`方法在找不到对应记录时抛出 404 异常,从而简化代码编写。 ### **获取聚合结果** Eloquent 模型类同样支持`count`、`sum`、`avg`、`max`、`min`等聚合函数查询: ~~~ $num = User::whereNotNull('email_verified_at')->count(); # 计数 $sum = User::whereNotNull('email_verified_at')->sum('id'); # 求和 $avg = User::whereNotNull('email_verified_at')->avg('id'); # 平均值 $min = User::whereNotNull('email_verified_at')->min('id'); # 最小值 $max = User::whereNotNull('email_verified_at')->max('id'); # 最大值 ~~~ 你会发现,如果你掌握了查询构建器,就等同于掌握了 Laravel 中的所有数据库查询操作。只不过将`DB::table`换成对应的模型类而已。 > 注:除获取单条记录之外,ELoquent 模型类查询返回的结果都是集合类,因此你可以在查询结果上调用集合类的所有方法,还可以自定义模型对应集合类,详情请查看对应[官方文档](https://xueyuanjun.com/post/9585.html)。 ## **插入数据** 通过 Eloquent 模型类插入记录到数据库也比较简单: ~~~ $post = new App\Post; $post->title = '测试文章标题'; $post->content = '测试文章内容'; $post->user_id = 1; $post->save(); ~~~ 创建时间和更新时间字段由 Eloquent 底层自动帮我们维护(遵循默认约定的话)。执行上面的代码就会在数据库新增一条记录(我们在[Tinker]('')代码): ![](https://img.kancloud.cn/54/3d/543d8895e14babd1547b475e024a38b6_1234x782.jpg) 此外,Eloquent 还为我们提供了一些快捷的插入方法,比如`firstOrCreate`和`firstOrNew`,这两个方法都会先尝试通过指定查询条件在数据库中查找对应记录,如果没有找到的话,会创建对应模型类的实例,并将查询条件作为对应字段值设置到模型属性上。 ### **批量赋值** 回看上例,我们新增Eloquent模型时是通过依次设置每个属性来实现的(`$post->title, $post->content.......`),如果模型类就那么三五个属性还好,如果是十几个甚至几十个呢?到时候我们的控制器类里面可能会遍布这种设置代码,Laravel 号称的优雅就是打脸了。So,批量赋值就是为我们来解决这个问题的。 批量赋值允许我们以数组的方式将待设置属性以关联数组的方式传递构造函数: ~~~ $post = new Post([ 'title' => '测试文章标题', 'content' => '测试文章内容' ]); ~~~ 直观来看,好像跟之前的写法没有什么大的优势,依然需要指定每个属性,但是这为我们提供了一个很好的基础,如果和用户请求数据结合起来使用,就能焕发它的光彩了。比如,如果我们的请求数据是一个文章发布表单提交过来的数据,包含`title`、`content`等字段信息,就可以通过下面这种方式进行批量赋值了: ~~~ $post = new Post($request->all()); ~~~ 这样一来,不管多少字段,一条语句就搞定了全部属性的赋值。但是,细心的同学可能会发现,这里有一个安全隐患,如果用户发布的时候,包含了用户字段`user_id`,并且设置的不是自己的用户 ID,而是其它用户的 ID,发布出来的文章就变成其他人发布的了;又或者文章需要审核后才能发布,但用户在表单中传递了状态字段将文章状态设置为审核通过,这样文章保存后就直接是已发布状态了。诸如此类的问题还有很多,总而言之,批量赋值给我们带来便利的同时,也给我们带来了烦恼。 作为一个成熟的 ORM 框架,Eloquent 在设计之初肯定不会没有考虑到这样的问题,实际上,我们可以借助模型类中的白名单属性或黑名单属性来解决这个困扰。 所谓白名单属性就是该属性中指定的字段才能应用批量赋值,不在白名单中的属性会被忽略;与之相对的,黑名单属性指定的字段不会应用批量赋值,不在黑名单中的属性则会应用批量赋值。可以看到,这两个属性是互斥的,只要设置一个属性就可以解决所有问题了,不要同时设置两个属性。 Eloquent 模型类默认白名单属性为空,黑名单属性为`*`,即所有字段都不会应用批量赋值: ~~~ /** * 使用批量赋值的属性(白名单) * * @var array */ protected $fillable = []; /** * 不使用批量赋值的字段(黑名单) * * @var array */ protected $guarded = ['*']; ~~~ 我们在实际开发中,对于频繁变动的数据表,建议使用白名单,这样安全性更好,因为哪些字段应用批量赋值始终是可控的,黑名单则会在后续新增字段的时候容易遗漏。而对于相对稳定或者字段很多的数据表,建议使用黑名单,免去设置字段之苦,但是对于这样的模型类,每次修改数据表结构的时候都要记得维护这个黑名单,看看是否需要变动。 ## **更新数据** 通过模型类更新数据表记录也很简单: ~~~ $post = Post::find(31); $post->title = '测试文章标题更新'; $post->save(); //更新时间 Eloquent 底层会自动帮我们维护 ~~~ 同样,Eloquent 也为我们提供了快捷的更新方法`updateOrCreate`,该方法首先会根据传入参数对模型对应记录进行更新,如果发现对应记录不存在,则会将更新数据作为初始数据插入数据库,并保存(不建议这么做,除非你的场景特别适合): ~~~ $user = user::updateOrCreate( ['name' => '学院君'], ['email' => 'admin@laravelacademy.org'] ); ~~~ 有的时候我们可能需要批量更新模型对应数据表的多条记录,这可以借助查询构建器来实现: ~~~ Post::where('views', '>', 0)->update(['views' => 100] ~~~ >对于更新模型类,也可以通过批量赋值的方式实现,只需在获取模型类后使用`fill`方法批量填充属性即可: ~~~ $post = Post::findOrFail(11); $post->fill($request->all()); $post->save(); ~~~ ## **删除数据** 通过模型类删除对应数据表记录和更新记录类似,都要先获取对应操作模型实例,删除对应记录更简单,获取到模型实例后,直接调用其删除方法即可: ~~~ $post = Post::find(31); $post->delete(); ~~~ 这样,就完成了`id = 31`对应数据表记录的删除,你还可以通过 Eloquent 提供的`destroy`方法一次删除多条记录,通过数组传递多个主键 ID 即可: ~~~ Post::destroy([1,2,3]); ~~~ 当然,你也可以通过查询构建器的方式删除指定记录: ~~~ $user = User::where('name', '学院君')->fisrt(); $user->delete(); ~~~ ### **软删除** 在日常开发过程中,删除数据库记录在所难免,但是我们多数时候并不想从数据库中物理删除记录而只是想从业务角度逻辑删除。这样既保证了不出现在查询结果中的实际需求,又满足了统计或查看历史数据的隐形需求。 Eloquent 模型类为我们提供了「软删除」功能的支持。在 Laravel 中,我们只需要在支持软删除的数据表中添加一个`deleted_at`字段——通过数据库迁移来实现——就可以实现对数据库记录的「软删除」。比如我们想要让`posts`表支持软删除,需要为其创建一个数据库迁移: ~~~ php artisan make:migration alter_posts_add_deleted_at --table=posts ~~~ 然后在新生成的迁移文件中编写代码如下: ~~~ <?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class AlterPostsAddDeletedAt extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('posts', function (Blueprint $table) { $table->softDeletes(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('posts', function (Blueprint $table) { $table->dropColumn('deleted_at'); }); } } ~~~ 这样运行`php artisan migrate`命令即可在`posts`表中新增一个`deleted_at`字段,该字段默认值为NULL,表示没有被软删除。如果要在模型类中支持软删除,需要在对应模型类(在本例中是`Post`模型)中添加支持软删除的 SoftDeletes`Trait: ~~~ <?php namespace App; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; class Post extends Model { use SoftDeletes; protected $guarded = ['user_id']; } ~~~ > 注:你也可以修改这个默认约定的`deleted_at`字段,但何必费这个劲呢,除非你是从其它系统迁移过来的,原来的表结构已经存在了,这时候可以通过再模型类中设置静态属性`DELETED_AT`来自定义软删除字段。 要软删除一条记录,在对应模型类实例上调用`delete`方法即可,底层会自动将数据表的`deleted_at`字段设置为当前时间——表示该记录已经被「删除」,而在模型类上做所有常规查询操作的时候就会过滤掉被软删除的记录。 如果是误删除的话,你可以`restore`方法来恢复软删除记录: ~~~ $post->restore(); // 恢复单条记录 Post::onlyTrashed()->where('views', 0)->restore(); // 通过onlyTrashed获取被删除记录,通过restore可恢复多条记录 ~~~ 如果你确实是想物理删除数据表记录,通过`forceDelete`方法删除即可: ~~~ $post->forceDelete(); ~~~ ## **访问器** 先来看一个例子,在带有用户功能的系统中,除了用户注册时使用的用户名之外,我们有时候还允许用户设置昵称(可选的),当在页面上显示用户名时,会优先展示用户昵称,如果该用户没有设置昵称,则使用注册时的用户名。 对于这样的需求,我们可以在每次获取用户信息后通过这段代码进行设置: ~~~ if ($user->nickname) { $user->display_name = $user->nickname; // 显示用户名为用户昵称 } else { $user->display_name = $user->name; // 显示用户名为注册时的用户名 } ~~~ 我们的系统中有大量显示用户名的地方,如果每次都这么设置,是件很让人崩溃的事情,而且可维护性很差,万一要修改用户名显示逻辑,每个地方都要修改。So,是时候祭出“访问器”了。 访问器用于从数据库获取对应字段值后进行一定处理满足指定需求再返回给调用方。 要定义访问器很简单,在相应模型类中设置对应方法即可。以上面的`$user->display_name`为例,我们可以在`User`模型类中添加相应的方法`getDisplayNameAttribute`(注意这里的转化方式,将小写字母+短划线格式属性转化为驼峰格式方法,后面的修改器也是这样): ~~~ public function getDisplayNameAttribute(){ return $this->nickname ? $this->nickname : $this->name; } ~~~ 这样,我们就可以在代码中直接通过`$user->display_name`访问期望的用户名了,以后如果你想要修改用户名显示逻辑,直接改这个方法里的代码就好了。 >注:访问器方法名中包含的字段尽量不要和数据库字段名同名,否则会覆盖数据库字段,导致通过模型属性将永远无法访问该数据库字段;另外,如果访问器内部访问了某个数据库字段,则不能将访问器和该数据库字段同名,否则会导致循环引用而报错。比如此例中,就不能将访问器方法名设置为`getNameAttribute`或`getNickNameAttribute`。 ## **修改器** 有了访问器,相对的,就有修改器,修改器用于在字段值保存到数据库之前进行一定处理满足需求后再存到数据库。比如做金融的同学可能比较熟悉,在保存用户银行卡号的时候需要加密后才能保存,显示时需要对银行卡号进行脱敏处理。 我们先定义一个加密银行卡号的修改器(在此之前可以通过[数据库迁移]('')为`users`表新增一个`card_no`字段): ~~~ public function setCardNoAttribute($value) { $value = str_replace(' ', '', $value); // 将所有空格去掉 $this->attributes['card_no'] = encrypt($value); } ~~~ 注意修改器传入形参`$value`不能漏掉,否则无法正常设置属性值。下面,我们通过模型类保存一个加密后的银行卡号到数据库: ~~~ $user = User::find(1); $user->card_no = '6222020903001483077'; $user->save(); ~~~ 在数据回显给用户时,我们还要定义一个访问器将加密数据解密并脱敏后显示给用户(脱敏【仅显示后四位】是为了安全考虑,避免银行卡号被爬取或劫持): ~~~ public function getCardNumAttribute() { if (!$this->card_no) { return ''; } $cardNo = decrypt($this->card_no); $lastFour = mb_substr($cardNo, -4); return '**** **** **** ' . $lastFour; } ~~~ > 注:由于我们在访问器内部访问了`card_no`属性,所以需要将访问器方法名调整为`getCardNumAttribute`。 这样,当我们查询并获取到对应模型实例后,访问`$user->card_num`属性,返回的就是脱敏后的银行卡号了。 ## **数组 & JSON转化** 你有一定有过这种经历,数据以 JSON 格式在数据库中存取时,每次存储时都要通过`json_encode`对数据进行编码,读取时都要通过`json_decode`对数据进行解码。我们当然可以通过上述访问器和修改器完成这种操作,但是 Laravel 提供了更加快捷的方法,对于一个在数据库中类型为`JSON`或`TEXT`的字段,我们可以在模型类中将字段对应属性类型转化设置为数组,这样在保存字段到数据库时,会自动将数组数据转化为 JSON 格式,在从数据库读取该字段时,会自动将 JSON 数据转化为数组格式,方便操作。 同以`users`表为例,我们为其新增一个类型为`JSON`格式的字段`settings`,用于保存用户设置信息(MySQL 5.7 以下版本设置字段类型为`TEXT`格式),然后在`users`表中设置`settings`类型转化格式为`array`: ~~~ protected $casts = [ 'settings' => 'array' ]; ~~~ 接下来,我们来测试下保存操作就能看到`settings`字段确实是以 JSON 格式保存到数据库了: ~~~ $user = User::find(1); $user->settings = ['city' => '杭州', 'hobby' => ['读书','撸码']]; $user->save(); ~~~ ## **Eloquent模型事件和监听方式** 在Eloquent模型类上进行查询、插入、更新、删除操作时,会触发相应的模型事件,不管你有没有监听它们,这些事件包括: * `retrieved`:获取到模型实例后触发 * `creating`:插入到数据库前触发 * `created`:插入到数据库后触发 * `updating`:更新到数据库前触发 * `updated`:更新到数据库后触发 * `saving`:保存到数据库前触发(插入/更新之前,无论插入还是更新都会触发) * `saved`:保存到数据库后触发(插入/更新之后,无论插入还是更新都会触发) * `deleting`:从数据库删除记录前触发 * `deleted`:从数据库删除记录后触发 * `restoring`:恢复软删除记录前触发 * `restored`:恢复软删除记录后触发 > 注:批量更新时不会触发相应事件,因为是直接走查询构建器完成的,绕过了模型方法。 通过监听这些事件,我们可以在 Eloquent 模型实例生命周期的特定阶段执行特定操作。在 Laravel 中我们有多种方式来监听模型事件。 ### **通过静态方法监听模型事件** 一般我们会在某个服务提供者的`boot`方法中完成这项工作,比如`EventServiceProvider`。举个例子,假设我们要监听每次获取模型实例的事件并在日志中记录查询到的用户信息,可以这么做: ~~~ // app/Providers/EventServiceProvider.php public function boot() { parent::boot(); //监听模型获取事件 User::retrieved(function ($user) { Log::info('从模型中获取用户[' . $user->id . ']:' . $user->name); }); } ~~~ 上面这段代码中表示我们在`User`模型上监听`retrieved`事件,然后通过一个闭包函数执行对应的处理逻辑,该闭包函数传入参数是模型实例,在处理逻辑中,我们通过`Log`门面记录日志信息。 如果我们要监听多个模型类的多个事件,像这样编写代码的话,就会导致服务提供者的臃肿,而且不便于维护,所以 Eloquent 底层还支持我们以普通事件注册与监听的方式来监听模型事件。 ### **通过订阅者监听模型事件** 如果要通过自定义监听器监听模型事件,需要先创建对应的事件类,然后将 Eloquent 支持的模型事件与自定义的事件类建立映射关系,最后将事件类注册到监听器类中,从而完成模型事件监听闭环。 我们先来创建自定义的事件类,这里我们以删除模型为例进行演示,分别定义一个删除前事件类和删除后事件类。我们通过 Artisan 命令来完成事件类初始化: ~~~ php artisan make:event UserDeleting php artisan make:event UserDeleted ~~~ 然后在这两个事件类中都添加`$user`属性并在构造函数中传入: ~~~ // app/Events/UserDeleted.php // app/Events/UserDeleting.php public $user; /** * Create a new event instance. * * @return void */ public function __construct(User $user) { $this->user = $user; } ~~~ 接下来,我们要在`User`模型类中建立模型事件与自定义事件类的映射,这可以通过`$dispatchesEvents`属性来完成: ~~~ protected $dispatchesEvents = [ 'deleting' => UserDeleting::class, 'deleted' => UserDeleted::class ]; ~~~ 这样当我们触发`deleting`和`deleted`事件时,底层会将其转化为触发`UserDeleting`和`UserDeleted`事件。 最后,我们还要监听上述自定义的事件类,我们可以通过在`EventServiceProvider`的`listen`属性中为每个事件绑定对应的监听器类,但通常情况下,我们会为某个模型类创建一个事件订阅者类来统一处理该模型中的所有事件。在`app/Listeners`目录下创建一个`UserEventSubscriber.php`文件作为订阅者类,编写代码如下: ~~~ <?php namespace App\Listeners; use App\Events\UserDeleted; use App\Events\UserDeleting; use Illuminate\Support\Facades\Log; class UserEventSubscriber { /** * 处理用户删除前事件 */ public function onUserDeleting($event) { Log::info('用户即将删除[' . $event->user->id . ']:' . $event->user->name); } /** * 处理用户删除后事件 */ public function onUserDeleted($event) { Log::info('用户已经删除[' . $event->user->id . ']:' . $event->user->name); } /** * 为订阅者注册监听器 * * @param Illuminate\Events\Dispatcher $events */ public function subscribe($events) { $events->listen( UserDeleting::class, UserEventSubscriber::class . '@onUserDeleting' ); $events->listen( UserDeleted::class, UserEventSubscriber::class . '@onUserDeleted' ); } } ~~~ 最后,我们在`EventServiceProvider`中注册这个订阅者,使其生效: ~~~ // app/Providers/EventServiceProvider.php protected $subscribe = [ UserEventSubscriber::class ]; ~~~ ### **通过观察者监听模型事件** 针对模型事件这种特殊的事件类型,Laravel还为我们提供了观察者类来处理模型事件的监听。观测可以看作是上述订阅者处理模型事件的简化版本,我们不需要自定义事件类,不需要建立映射关系,只需要在观察者类中将需要监听的事件定义为同名方法,并在相应方法中编写业务处理代码即可。当某个模型事件触发时,Eloquent 底层会去该模型上注册的观察者类中通过反射查找是否定义了对应的方法,如果定义了则执行相应的逻辑,否则忽略。 首先,我们通过 Artisan 命令初始化针对`User`模型的观察者: ~~~ php artisan make:observer UserObserver --model=User ~~~ 默认生成的`UserObserver`会为`created`、`updated`、`deleted`、`restored`、`forceDeleted`(强制删除) 事件定义一个空方法。你可以把前面定义的`retrived`、`deleting`、`deleted`事件监听代码迁移过来,也可以将不需监听的事件方法移除,这里我们将编写保存模型时涉及的模型事件,包括`saving`、`creating`、`updating`、`updated`、`created`、`saved`: ~~~ <?php namespace App\Observers; use App\User; use Illuminate\Support\Facades\Log; class UserObserver { public function saving(User $user) { Log::info('即将保存用户到数据库[' . $user->id . ']' . $user->name); } public function creating(User $user) { Log::info('即将插入用户到数据库[' . $user->id . ']' . $user->name); } public function updating(User $user) { Log::info('即将更新用户到数据库[' . $user->id . ']' . $user->name); } public function updated(User $user) { Log::info('已经更新用户到数据库[' . $user->id . ']' . $user->name); } public function created(User $user) { Log::info('已经插入用户到数据库[' . $user->id . ']' . $user->name); } public function saved(User $user) { Log::info('已经保存用户到数据库[' . $user->id . ']' . $user->name); } } ~~~ 编写好观察者后,需要将其注册到`User`模型上才能生效,我们可以在`EventServiceProvider`的`boot`方法中完成该工作: ~~~ public function boot() { parent::boot(); User::observe(UserObserver::class); ... } ~~~ >注:我们已经介绍完了三种监听 Eloquent 模型事件的方法和使用方式,如何选择,视情况而定。如果只是监听一两个模型事件,第一种方式比较合适;如果仅仅监听系统支持的模型事件,并且要监听多个模型的多个事件,观察者是最佳选择;如果还要在模型类上监听更多系统模型事件之外的自定义事件,则使用订阅者来监听比较合适。