Eloquent ORM Events
Laravel 的 Eloquent ORM 有几种不同的事件:
retrieved, creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored
根据这些事件,可以快速在特定情景下,执行一些对应的逻辑操作。比如:在订单的保存事件中,减掉相应的库存。
需求一:快速对入库数据格式化处理
在日程的创建事件中格式化时间戳,项目中包含 web 服务 和 api 服务,之前的开发人员将代码写的比较杂乱,并且未对时间进行统一的格式化。添加日历视图后,日程未按照预期进行显示,在此情况下优先考虑通过事件进行统一处理通过 events 快速处理。
创建观察器 php artisan make:observer CalendarObserver
<?php
namespace App\Observers;
use App\Models\Calendar;
class CalendarObserver
{
public function saved(Calendar $calendar)
{
if ($calendar->start_at != strtotime($calendar->start_at) || $calendar->stop_at != strtotime($calendar->stop_at)) {
$calendar->start_at = strtotime($calendar->start_at);
$calendar->stop_at = strtotime($calendar->stop_at);
// 处理后进行保存
$calendar->save();
}
}
}
需求二:对模型添加操作日志
后续对系统添加操作日志,又监听了 created,updated,deleted 事件
<?php
namespace App\Observers;
use App\Models\Calendar;
class CalendarObserver
{
public function created(Calendar $calendar)
{
if (count($calendar->getDirty()) > 0) {
// 记录新增日志
}
}
public function updated(Calendar $calendar)
{
if (count($calendar->getDirty()) > 0) {
// 记录修改日志
}
}
public function deleted(Calendar $calendar)
{
// 记录修改日志
}
public function saved(Calendar $calendar)
{
if ($calendar->start_at != strtotime($calendar->start_at) || $calendar->stop_at != strtotime($calendar->stop_at)) {
$calendar->start_at = strtotime($calendar->start_at);
$calendar->stop_at = strtotime($calendar->stop_at);
// 处理后进行保存
$calendar->save();
}
}
}
预期应该触发一次新增日志,而后触发一次编辑日志:
- 新增日志记录所有的保存字段值
- 编辑日志应该只记录
start_at和stop_at两个字段值的变动
实际执行时,新增日志记录为预期效果,但是编辑日志却将新增日志的结果完整的重写了一遍,同时修改了 start_at 和 stop_at 两个字段的值。
对观察器进行分析:
- 新增依次执行了
created->saved - 在
saved事件中又调用了save()方法, save()时又触发了updated->saved事件,
updated 中获得到的 getDirty() 与 created 中的 getDirty() 仅有 saved 中修改的两个字段不同,所以产生与预期不同的结果。
对事件进行调整,首先明确事件的依次执行顺序:
新增时:
saving -> creating -> created -> saved
编辑时:
saving -> updating -> updated -> saved
其中的任何一个事件中 return false; 后续的事件都不会再执行。
最初使用 saved 的事件,是因为在 created 和 udpated 的时候都会执行,所以这里将事件考虑放到同样都会执行的 saving 事件中。
因为 saving 事件是最先调用的,created 和 updated 均在其后,所以只需在其中正常处理逻辑即可,不需要像 saved 中的实现一样,需要再次调用 save() 才能保存。
<?php
namespace App\Observers;
use App\Models\Calendar;
class CalendarObserver
{
public function created(Calendar $calendar)
{
if (count($calendar->getDirty()) > 0) {
// 记录新增日志
}
}
public function updated(Calendar $calendar)
{
if (count($calendar->getDirty()) > 0) {
// 记录修改日志
}
}
public function deleted(Calendar $calendar)
{
// 记录删除日志
}
public function saving(Calendar $calendar)
{
if ($calendar->start_at != strtotime($calendar->start_at) || $calendar->stop_at != strtotime($calendar->stop_at)) {
$calendar->start_at = strtotime($calendar->start_at);
$calendar->stop_at = strtotime($calendar->stop_at);
}
}
}
最后记录的日志,新增只会触发一次 created 日志,不再是最初预期的一个新增日志,同时跟一个编辑的日志,更加完美。