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
相关产品推荐
相关产品推荐

