Laravel Eloquent 作用域与模型事件:软删除与审计字段的工程化

test12026-09-140 次阅读

作用域:把查询条件收敛到模型

查询条件如果散落在控制器各处,既重复又难维护。Eloquent 的局部作用域让你把“已支付”“有效”这类语义封装成可读的方法,全局作用域则能默认给所有查询加上过滤(例如排除已取消订单),从根源减少误查。

局部与全局作用域

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;

class Order extends Model
{
    protected $fillable = ["user_id", "amount", "status"];

    // 局部作用域:只查已支付
    public function scopePaid(Builder $query): Builder
    {
        return $query->where("status", "paid");
    }

    // 全局作用域:默认排除已取消订单
    protected static function boot()
    {
        parent::boot();
        static::addGlobalScope("notCancelled", function (Builder $query) {
            $query->where("status", "<>", "cancelled");
        });
    }
}

软删除与全局作用域

SoftDeletes 本身就是一个全局作用域:它让所有查询默认忽略被删除的记录,需要时再用 withTrashed 显式拉回。和业务全局作用域叠加时互不冲突。

use Illuminate\Database\Eloquent\SoftDeletes;

class Order extends Model
{
    use SoftDeletes;
    protected $dates = ["deleted_at"];
}

// 恢复一条被软删除的订单
Order::withTrashed()->where("id", $id)->first()->restore();

模型事件与观察者:自动填充审计字段

created_by / updated_by 这类审计字段没必要在每个控制器里手填。放到模型的 creating / updating 事件里,配合全局登录态自动写入,既一致又防漏。

use Illuminate\Support\Facades\Auth;

class Order extends Model
{
    protected static function boot()
    {
        parent::boot();

        static::creating(function ($order) {
            $order->created_by = Auth::id();
        });

        static::updating(function ($order) {
            $order->updated_by = Auth::id();
        });
    }
}

在业务里组合使用

// 只返回“未取消 且 已支付 且 金额大于 100”的订单
$orders = Order::paid()
    ->where("amount", ">", 100)
    ->get();

经验清单

  • 高频查询条件优先做成局部作用域,提升可读性也避免写错。
  • 全局作用域要克制,只放“几乎总是成立”的约束,否则会悄悄改变查询结果。
  • 审计字段、时间戳等横切逻辑放在模型事件里,比在控制器重复写更稳。
  • 使用全局作用域后,记得在需要“看到被过滤数据”的地方用 withoutGlobalScope / withTrashed。
T

test1

文章作者

作用域:把查询条件收敛到模型 查询条件如果散落在控制器各处,既重复又难维护。Eloquent 的局部作用域让你把“已支...

分类
技术
发布时间
2026-09-14
字数
约 1871 字
阅读
0 次