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

Laravel:如何在Trait中获取创建与更新时的原始输入?

Laravel Eloquent 输入字段提升Trait的实现问题

我正在为Laravel Eloquent模型编写一个Trait,用于在创建和更新时实现输入元素的「提升」——将嵌套数据中的指定字段提取到顶层,存入数据库后更易排序和检索。

基础Trait代码

<?php

namespace App\Traits;

use Illuminate\Database\Eloquent\Builder;

trait PromotesInputsDuringCreation {

    public static function doPromotion($attrs) {
        if (isset(static::$promotions) && !empty(static::$promotions)){
            foreach (static::$promotions as $target => $path) {
                $pathParts = explode('.', $path);
                $targetData = $attrs;
                foreach ($pathParts as $part){
                    if (is_null($targetData)) {
                        break;
                    }
                    $targetData = is_object($targetData) ? $targetData->$part : $targetData[$part];
                }
                if(!is_null($targetData)) {
                    $attrs[$target] = $targetData;
                }
            }
        }
        return $attrs;
    }
}

模型使用示例

实现该Trait的模型需定义$promotions数组,示例如下:

public static $promotions = [
    'loan_status_end_date' => 'loan_status.end_date',
    'loan_status_type' => 'loan_status.type'
];

尝试的方案及问题

方案一:使用Boot方法

通过模型事件回调处理,但无法获取传入::create()/::updateOrCreate()等方法的原始数据,$model->getDirty()只能拿到与模型属性键完全匹配的字段,导致嵌套字段无法被处理。

代码示例:

public static function bootPromotesInputsDuringCreation()
{
    static::creating(function ($model) {
        // $model->getDirty() only gets attributes that exactly match data keys
        $model->attributes = array_intersect_key(self::doPromotion($model->getDirty()), $model->attributes);
    });
    static::updating(function ($model) {
        // $model->getDirty() only gets attributes that will change in the update
        $model->attributes = array_intersect_key(self::doPromotion($model->getDirty()), $model->attributes);
    });
}

方案二:重写创建/更新方法

提升功能可正常工作,但重写的方法会跳过模型的类型转换($casts)等Laravel原生特性;若尝试用static::create()或parent::create()处理提升后的数据,会导致无限循环。

代码示例:

public static function create($data) 
{
    return (new static)->newQuery()->create(self::doPromotion($data));
}

public static function updateOrCreate($where, $data)
{
    $data = self::doPromotion($data);

    if ((new static)->newQuery()->where($where)->exists()) {
        // this skips $casts and other model events :(
        return (new static)->newQuery()->where($where)->update($data);
    }

    return (new static)->newQuery()->create([...$where, ...$data]);
}

问题求解

我感觉离实现该Trait只差一步,请问我遗漏了什么?比如在Boot方法中获取原始创建/更新输入的方式,或在重写方法中调用create/update的正确方式以保留Laravel特性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 17:34:51