在Laravel项目开发中,我们常常需要在模型字段发生变更时执行特定逻辑,比如当订单的status字段从待支付变为已支付时,触发通知或者更新关联数据。Laravel的模型本身提供了完善的属性变更检测能力,不需要我们手动对比新旧数据就能实现需求。

Laravel模型的变更检测基础
Laravel的Eloquent模型在加载数据后,会记录原始属性值,当我们修改模型的属性时,可以通过内置方法判断字段是否发生了变化。核心的两个方法是isDirty()和getDirty()。
isDirty()方法可以检测模型是否有属性被修改,也可以传入指定字段名检测单个字段是否变更。getDirty()方法会返回所有被修改的字段及其新值。原始属性值可以通过getOriginal()方法获取。
基础检测示例
<?php
namespace AppModels;
use IlluminateDatabaseEloquentModel;
class Order extends Model
{
protected $fillable = ['order_no', 'status', 'amount'];
// 判断status字段是否被修改
public function checkStatusChange()
{
// 检测单个字段是否变更
if ($this->isDirty('status')) {
$oldStatus = $this->getOriginal('status');
$newStatus = $this->status;
echo "status字段从{$oldStatus}变更为{$newStatus}";
}
// 获取所有变更的字段
$dirtyAttributes = $this->getDirty();
if (isset($dirtyAttributes['status'])) {
echo "变更的字段包含status";
}
}
}
在模型事件中自动检测指定字段变更
如果需要在字段变更时自动触发逻辑,不需要每次手动调用检测方法,可以结合模型事件来实现。Laravel模型支持多种事件,比如saving、saved、updating、updated等,我们可以在事件中编写字段变更的检测逻辑。
在模型boot方法中注册事件
我们可以在模型的boot静态方法中注册updating或者updated事件,在事件回调中检测指定字段的变更情况。
<?php
namespace AppModels;
use IlluminateDatabaseEloquentModel;
class Order extends Model
{
protected $fillable = ['order_no', 'status', 'amount'];
// 模型启动方法,注册事件
protected static function boot()
{
parent::boot();
// 监听updating事件,在保存前检测变更
static::updating(function ($order) {
// 检测status字段是否变更
if ($order->isDirty('status')) {
$oldStatus = $order->getOriginal('status');
$newStatus = $order->status;
// 这里可以编写变更后的业务逻辑,比如记录日志、发送通知
Log::info("订单{$order->order_no}的status字段从{$oldStatus}变更为{$newStatus}");
}
});
// 如果需要保存后触发逻辑,可以使用updated事件
static::updated(function ($order) {
if ($order->isDirty('status')) {
// 保存后的业务逻辑,比如更新关联统计数据
}
});
}
}
使用观察者实现更清晰的逻辑分离
如果事件逻辑比较复杂,推荐使用观察者来分离代码,避免模型的boot方法过于臃肿。
首先创建观察者类:
<?php
namespace AppObservers;
use AppModelsOrder;
class OrderObserver
{
/**
* 监听订单更新前事件
*
* @param Order $order
* @return void
*/
public function updating(Order $order)
{
if ($order->isDirty('status')) {
$oldStatus = $order->getOriginal('status');
$newStatus = $order->status;
Log::info("观察者检测到订单{$order->order_no}的status变更:{$oldStatus} -> {$newStatus}");
}
}
/**
* 监听订单更新后事件
*
* @param Order $order
* @return void
*/
public function updated(Order $order)
{
if ($order->isDirty('status')) {
// 更新后的业务逻辑
}
}
}
然后在服务提供者中注册观察者:
<?php
namespace AppProviders;
use AppModelsOrder;
use AppObserversOrderObserver;
use IlluminateSupportServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Order::observe(OrderObserver::class);
}
}
常见问题与注意事项
- 只有调用save方法或者update方法时,模型的变更检测才会生效,如果是通过查询构造器直接更新数据,不会触发模型的事件和变更检测。
isDirty()方法检测的是当前模型实例的属性是否和原始加载的属性有差异,如果是新创建的模型,调用isDirty()会返回true,因为所有属性都是新设置的。- 如果需要检测字段是否真的被修改(包括修改后的值和原来相同的情况),可以先获取原始值再对比,因为
isDirty()会在设置的值和原始值相同时返回false。
总结
通过Laravel模型内置的isDirty()、getOriginal()等方法,结合模型事件或者观察者,我们可以很方便地实现指定字段的自动变更检测。这种方式不需要手动对比新旧数据,减少了重复代码,也能让业务逻辑更加集中和清晰。在实际开发中,可以根据业务复杂度选择直接在模型boot中写逻辑,还是使用观察者分离代码。
Laravel模型字段变更检测status字段监听模型事件属性变更跟踪修改时间:2026-07-07 14:51:21