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();
        }
    }
}

需求二:对模型添加操作日志

后续对系统添加操作日志,又监听了 createdupdateddeleted 事件

<?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_atstop_at 两个字段值的变动

实际执行时,新增日志记录为预期效果,但是编辑日志却将新增日志的结果完整的重写了一遍,同时修改了 start_atstop_at 两个字段的值

对观察器进行分析

  1. 新增依次执行了 created -> saved
  2. saved 事件中又调用了 save() 方法,
  3. save() 时又触发了 updated -> saved 事件,

updated 中获得到的 getDirty()created 中的 getDirty() 仅有 saved 中修改的两个字段不同,所以产生与预期不同的结果。

对事件进行调整,首先明确事件的依次执行顺序

新增时:
saving -> creating -> created -> saved

编辑时:
saving -> updating -> updated -> saved

其中的任何一个事件中 return false; 后续的事件都不会再执行。

最初使用 saved 的事件,是因为在 createdudpated 的时候都会执行,所以这里将事件考虑放到同样都会执行的 saving 事件中。

因为 saving 事件是最先调用的,createdupdated 均在其后,所以只需在其中正常处理逻辑即可,不需要像 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 日志,不再是最初预期的一个新增日志,同时跟一个编辑的日志,更加完美。

发表评论

您的电子邮箱地址不会被公开。