You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel中save()/update()字段变更检测及自动化记录方法咨询

解答你的Laravel模型更新相关疑问与需求

首先来确认你的第一个疑问:

1. ->save() 和 ->update() 是否仅在数据变更时执行更新?

是的,完全正确!从你贴出的Illuminate\Database\Eloquent\Model@performUpdate代码就能看出来:

protected function performUpdate(Builder $query, array $options = []) {
$dirty = $this->getDirty();
if (count($dirty) > 0) {
// runs update query
}
return true;
}

不管是调用save()(当模型是已存在的实例时)还是update(),最终都会走到这个performUpdate方法。它会先通过getDirty()获取所有和原始数据不一致的字段(也就是"脏数据"),只有当脏数据数组不为空时,才会触发实际的数据库更新查询。如果数据没有任何变化,这两个方法都不会执行SQL更新操作,直接返回true。

2. 关于你的常规更新写法

你当前的更新写法是Laravel中非常标准且推荐的方式:

public function update(Requests\UpdatePetRequest $request, Pet $pet) {
    $pet->update($request->all());
    return $pet;
}

这种写法简洁安全(前提是你的UpdatePetRequest已经做了字段验证和授权),完全没问题。

3. 自动获取更新字段、旧值与新值的自动化方案

你不需要手动判断每个字段的变化,Laravel Eloquent已经提供了内置的方法来获取这些信息,结合模型事件或观察者就能实现完全自动化的追踪:

方案一:利用模型的updating事件

在你的Pet模型中,通过boot方法注册updating事件,这个事件会在模型更新操作执行之前触发,此时可以轻松拿到所有变更信息:

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Pet extends Model
{
    // ... 你的模型属性和其他代码

    protected static function boot()
    {
        parent::boot();

        static::updating(function (Pet $pet) {
            // 获取所有变更的字段(键是字段名,值是新值)
            $dirtyFields = $pet->getDirty();
            
            if (!empty($dirtyFields)) {
                foreach ($dirtyFields as $field => $newValue) {
                    // 获取该字段的原始旧值
                    $oldValue = $pet->getOriginal($field);
                    
                    // 这里可以根据需求处理这些信息:比如写入日志、触发通知等
                    // 示例:记录到日志文件
                    logger()->info("宠物[ID: {$pet->id}]的字段{$field}已更新", [
                        '旧值' => $oldValue,
                        '新值' => $newValue
                    ]);
                }
            }
        });
    }
}

方案二:使用模型观察者(适合多模型复用场景)

如果你的多个模型都需要追踪字段变更,使用观察者会更优雅,便于统一管理:

  1. 生成观察者:
php artisan make:observer PetObserver --model=Pet
  1. 在观察者中实现updating方法:
namespace App\Observers;

use App\Models\Pet;
use Illuminate\Support\Facades\Log;

class PetObserver
{
    public function updating(Pet $pet)
    {
        $dirty = $pet->getDirty();
        
        if (count($dirty) === 0) {
            return; // 没有变更直接返回
        }

        foreach ($dirty as $field => $newValue) {
            $oldValue = $pet->getOriginal($field);
            // 这里可以将变更信息存入专门的日志表,或者做其他业务处理
            Log::channel('model_changes')->info("Pet模型更新", [
                'model_id' => $pet->id,
                'field' => $field,
                'old_value' => $oldValue,
                'new_value' => $newValue
            ]);
        }
    }
}
  1. 在AppServiceProvider中注册观察者:
namespace App\Providers;

use App\Models\Pet;
use App\Observers\PetObserver;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Pet::observe(PetObserver::class);
    }
}

方案三:自定义Trait封装变更获取逻辑

如果只是需要在控制器中手动获取变更信息(而不是自动触发处理),可以封装一个Trait:

namespace App\Traits;

trait TracksChanges
{
    /**
     * 获取模型的变更日志(字段名、旧值、新值)
     * @return array
     */
    public function getChangeLog(): array
    {
        $changeLog = [];
        $dirtyFields = $this->getDirty();

        foreach ($dirtyFields as $field => $newValue) {
            $changeLog[] = [
                'field' => $field,
                'old_value' => $this->getOriginal($field),
                'new_value' => $newValue
            ];
        }

        return $changeLog;
    }
}

然后在Pet模型中引入这个Trait:

namespace App\Models;

use App\Traits\TracksChanges;
use Illuminate\Database\Eloquent\Model;

class Pet extends Model
{
    use TracksChanges;
    // ...
}

之后在控制器中就能直接调用:

public function update(Requests\UpdatePetRequest $request, Pet $pet) {
    $pet->update($request->all());
    
    // 获取变更信息
    $changes = $pet->getChangeLog();
    
    // 处理变更信息,比如返回给前端或者存入数据库
    return response()->json([
        'pet' => $pet,
        'changes' => $changes
    ]);
}

以上三种方案都能完美替代你的手动判断实现,完全利用Laravel的内置能力,不需要额外开发复杂逻辑。

内容的提问来源于stack exchange,提问作者Blagoh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:11:04