replicate() 是 Eloquent 浅拷贝方法,仅复制属性值(排除主键、时间戳等),不保存数据库、不触发事件、不复制关联;需手动 save() 且注意 fillable、mutator、软删除及关联外键处理。

replicate() 方法的基本用法和限制
replicate() 是 Eloquent 提供的浅拷贝方法,它会复制模型实例的属性值(不包括主键、时间戳、增量 ID),但不会保存到数据库。你必须手动调用 save() 才能真正插入新记录。
常见错误是直接 $new = $model->replicate(); 就以为完成了复制——其实 $new 还只是内存中的对象,id 为 null,也未触发任何事件或验证。
- 只复制
$model->getAttributes()中的字段,忽略关系、隐藏属性、casts转换后的值(如 JSON 字段保持原始字符串) - 自动清空
id、created_at、updated_at(如果模型启用了$timestamps = true) - 不复制关联数据(一对多、多对多等),也不触发
replicating或saved事件(Laravel 10+ 开始支持replicating事件,但需手动注册)
复制时需要重置的字段怎么处理
很多场景下,你希望新记录有不同值:比如把 status 改成 'draft',把 title 加上「副本」后缀,或者强制更新 user_id 为当前操作人。
最稳妥的做法是在 replicate() 后链式设置属性,再 save():
立即学习“PHP免费学习笔记(深入)”;
$original = Post::find(123);
$copy = $original->replicate()->fill([
'title' => $original->title . '(副本)',
'status' => 'draft',
'user_id' => auth()->id(),
'published_at' => null,
]);
$copy->save();
注意:fill() 不会跳过 guarded 或 fillable 检查,所以确保目标字段在 $fillable 中;否则要用 forceFill()。
- 不要在
replicate()前修改原模型属性(如$original->title = ...),这会影响原数据 - 如果字段有
mutator(如setSlugAttribute),fill()仍会触发它;而直接赋值$copy->slug = ...则不会 - 时间字段若设为
null,Eloquent 会在save()时按规则补上(如created_at写入当前时间),除非显式关闭$timestamps
带关联关系的深度复制怎么做
replicate() 本身不处理关联,要复制「文章 + 所有评论 + 标签」这类结构,得手动遍历处理。
一个常见且可控的做法是:先复制主模型,再用 replicate() 复制每个关联项,并绑定回新主模型 ID:
$post = Post::with('comments', 'tags')->find(123);
$newPost = $post->replicate()->fill(['title' => $post->title . '(复刻)'])->save();
// 复制评论
foreach ($post->comments as $comment) {
$newComment = $comment->replicate()->fill(['post_id' => $newPost->id]);
$newComment->save();
}
// 复制标签(多对多)
$newPost->tags()->sync($post->tags->pluck('id')->toArray());
更健壮的方案是封装成模型方法,比如在 Post 中加 replicateWithRelations(),并考虑事务包裹防止部分失败。
- 多对多同步用
sync()最安全,避免重复插入或漏掉 - 一对多复制时注意外键字段名(如
post_id),别写成post_id_id这类低级错误 - 深层嵌套(如评论的回复)容易引发 N+1,建议用集合预加载或批量插入优化
replicate() 在软删除模型中的行为
如果模型启用了软删除(SoftDeletes trait),replicate() 默认不会复制 deleted_at 字段,即新记录是「正常活跃」状态——这是合理设计,否则复制出一堆已删除的记录没意义。
但如果你明确需要保留删除状态(极少见),得手动恢复字段:
$trashed = Post::withTrashed()->find(123);
$copy = $trashed->replicate()->fill([
'deleted_at' => $trashed->deleted_at, // 显式复制
])->save();
注意:withTrashed() 是查询用的,不影响 replicate() 行为;replicate() 总是读取当前模型实例的属性,无论它是否已软删除。
- 软删除字段不在
$dates或$casts中时,replicate()可能把它当字符串复制,导致类型不一致 - 如果模型设置了
$dates = ['deleted_at'],Laravel 会自动转换为 Carbon 实例,replicate()也能正确处理 - 别依赖
replicate()来「恢复」软删除记录——它不改原记录,只是新建一条



















