记录laravel orm trait加载小技巧

因为某些时候需要在很多model里面加入一些保存前保存后的事件
比如

  • created_by updated_by 字段自动维护

这种情况头一次想的是用 observer
或者直接监听 model 保存新增事件

机缘巧合下看到一篇文章 关于laravel orm加载trait的技巧 原文

实现方案

// app/Model/Traits/UserStamps
trait Usertamps{
        public static function bootUserStamps()
        {
            static::creating(function (Model $model) {
                // 设置 created_by 字段的值为当前登录的用户ID
                $model->created_by = auth()->id();
            });

            static::updating(function (Model $model) {
                // 设置 updated_by 字段的值为当前登录的用户ID
                $model->updated_by = auth()->id();
            });
        }
}

这样只需要在用到的model直接use这个类即可。

大概语法是

  • initializeUserStamps 每次实例化新模型的时候都会调用
  • bootUserStamps 这个应该是类似与 model::booted()里面执行
本作品采用《CC 协议》,转载必须注明作者和本文链接
终不似少年游
zds
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 19
sanders

实际业务场景里面不建议这样用,原因在于 auth() 是根据请求鉴权结果获取的数据,以下3种情况无法通过 auth() 获取授准确的权信息:

  1. 未授权请求;
  2. 队列任务中;
  3. 定时任务调度或命令行执行;

所以这样做并不能稳定获取授权信息,我们这样处理以上问题:

  1. 业务逻辑层单独封装;
  2. 控制器将授权用户ID传入业务逻辑层方法;
  3. 队列任务负载数据中保存发起用户的ID;
  4. 命令行或任务调度有关联的用户数据,或默认执行的系统用户;

至于 trait 在模型方面的用法,确实有很多可发挥的余地,比如公共的模型关系:

// 存在创建用户的都可以通过这个特性获取该模型关系
trait HasCreatedBy {
    // 只封装这一个方法,因为有些模型可能没有更新用户,这样可以增加适配性
    public function createdBy (): BelongsTo
    {
        return $this->belongsTo(User::class, 'created_by');
    }
    // 增加一个本地作用域 byCreatorId($userId) 方法用来根据创建用户ID查询本模型数据
    public function scopeByCreatorId (Builder $query, int $userId)
    {
        $query->whereHas('createdBy', fn ($query) => $query->where('id',$userId));
    }
}

类似的场景还比如有,是否关联品类 或 三级行政区 等等。

3个月前 评论
zds (楼主) 3个月前
zds (楼主) 3个月前
sanders

实际业务场景里面不建议这样用,原因在于 auth() 是根据请求鉴权结果获取的数据,以下3种情况无法通过 auth() 获取授准确的权信息:

  1. 未授权请求;
  2. 队列任务中;
  3. 定时任务调度或命令行执行;

所以这样做并不能稳定获取授权信息,我们这样处理以上问题:

  1. 业务逻辑层单独封装;
  2. 控制器将授权用户ID传入业务逻辑层方法;
  3. 队列任务负载数据中保存发起用户的ID;
  4. 命令行或任务调度有关联的用户数据,或默认执行的系统用户;

至于 trait 在模型方面的用法,确实有很多可发挥的余地,比如公共的模型关系:

// 存在创建用户的都可以通过这个特性获取该模型关系
trait HasCreatedBy {
    // 只封装这一个方法,因为有些模型可能没有更新用户,这样可以增加适配性
    public function createdBy (): BelongsTo
    {
        return $this->belongsTo(User::class, 'created_by');
    }
    // 增加一个本地作用域 byCreatorId($userId) 方法用来根据创建用户ID查询本模型数据
    public function scopeByCreatorId (Builder $query, int $userId)
    {
        $query->whereHas('createdBy', fn ($query) => $query->where('id',$userId));
    }
}

类似的场景还比如有,是否关联品类 或 三级行政区 等等。

3个月前 评论
zds (楼主) 3个月前
zds (楼主) 3个月前
Tomo11111

其实创建时间和更新时间框架会自动维护的, 如果是字段和框架默认的 created_at 不同,那只需要在模型中修改 CREATED_AT 和 UPDATED_AT 的值即可。

3个月前 评论
Cooper 3个月前

并不实用,也不推荐这种做法trait 的作用是实现代码复用,而不是逻辑复用,created_by 字段还是推荐在控制器注入,observer也不适合这个例子,observer适合一些事件,比如创建用户同步创建用户扩展,修改用户同步修改用户扩展

3个月前 评论
zds (楼主) 3个月前
zds (楼主) 3个月前
zds (楼主) 3个月前
sanders 3个月前
zds (楼主) 3个月前
zds (楼主) 3个月前
sanders 3个月前
zds (楼主) 3个月前
Imuyu (作者) 3个月前
Imuyu (作者) 3个月前
zds (楼主) 3个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!