Laravel关联创建时Trait重写create/createMany不生效问题
旧系统数据迁移归档实现的问题
项目背景
正在将旧软件数据迁移到全新自定义Web应用中:旧软件里大量字段不会在新系统日常业务中使用,但未来可能具备参考价值,需要将这些字段迁移到独立、可检索的archives表存储。
当前已完成的基础实现:
- 创建了
Archive模型,用于存储归档字段的键、值、值类型,通过多态关联绑定到对应业务模型 - 封装了
ArchiveTrait,核心逻辑为:模型创建时自动提取所有带_archive前缀的属性,拆分出归档字段与正式业务字段,先创建对应业务模型,再将提取到的归档字段存入archives表 - 挂载方式:在
Account、Location、Appliance等需要归档功能的模型上引入该Trait
待迁移的数据为树状结构JSON,简化示例:
{ "id": 12345, "first_name": "Bob", "last_name": "Johnson", "_archive_previous_account_holder": "Randy Johnson", "locations": [ { "street": "123 Main Street", "city": "Bloomington", "_archive_house_color": "red", "appliances": [ { "make": "GE", "model": "GSS25G" } ] } ] }
ArchiveTrait的核心实现代码:
trait ArchiveTrait { public static function create(array $attributes = []) { $archives = []; foreach ($attributes as $key => $value ) { if (preg_match('/^_archive/', $key) ) { if( ! is_null($value) and $value != "" ) { $archives[$key] = $value; } unset( $attributes[$key] ); } } $model = static::query()->create($attributes); foreach ($archives as $key => $value ) { $model->archives()->create([ 'key' => $key, 'value' => $value, 'cast' => gettype($value) ]); } return $model; } public static function createMany(iterable $records) { return new Collection( array_map(function ($attributes) { $archives = []; foreach ($attributes as $key => $value ) { if (preg_match('/^_archive/', $key) ) { if( ! is_null($value) and $value != "" ) { $archives[$key] = $value; } unset( $attributes[$key] ); } } $model = static::query()->create($attributes); foreach ($archives as $key => $value ) { $model->archives()->create([ 'key' => $key, 'value' => $value, 'cast' => gettype($value) ]); } }, $records) ); } public function archives() { return $this->morphMany(Archive::class, 'archivable'); } }
存在的问题
当前这套实现在直接静态调用模型create方法的场景下运行完全正常,但如果通过关联关系创建模型,归档逻辑就不会触发,示例代码:
$a = Account::create($account); $l = $a->locations()->create($location); $x = $l->appliances()->create($appliance);
原因是关联对象调用的create方法属于关联类的内部实现,并不会触发模型本身通过Trait定义的静态create、createMany方法。
已知备选方案与顾虑
- 方案1:逐层级拆分嵌套JSON,依次调用每个模型的静态create方法完成迁移。缺点是写法繁琐,破坏了嵌套数据的整体处理流畅度。
- 方案2:实现一个带归档逻辑的
ArchivedModel父类,让所有需要归档的业务模型继承该父类。缺点是相比Trait的灵活按需挂载特性,继承方式的耦合度更高,不符合设计初衷。
约束条件
- 待迁移JSON文件为已导出的固定格式,总量约5000个待迁移账户,每个账户平均4层嵌套,每层包含2-20个归档字段,在可行前提下不希望大规模修改原始数据结构。
- 核心诉求是让基于Trait实现的归档逻辑,在通过关联关系创建模型时也能正常生效,尽可能保留Trait的灵活挂载优势。
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

