Laravel模型replicate()复制时如何同步复制关联图片并重命名
问题说明
在Store存储方法中,调用replicate()复制表单数据的原始代码如下:
public function store(StoreNewsRequest $request) { $news = News::create($request->all()); if ($request->input('image', false)) { $news->addMedia(storage_path('tmp/uploads/' . $request->input('image')))->toMediaCollection('image'); } if ($media = $request->input('ck-media', false)) { Media::whereIn('id', $media)->update(['model_id' => $news->id]); } $assigned_pack_ids = DB::table('content_pack_news')->where('news_id' , $news->id)->pluck('content_pack_id')->toArray(); foreach($assigned_pack_ids as $key => $pack_id) { $news_replica = $news->replicate(); $news_replica->save(); DB::table('content_pack_news')->insert(['content_pack_id' => $pack_id,'news_id' => $news_replica->id]); } return redirect()->route('admin.news.index'); }
当前代码运行后,foreach($assigned_pack_ids)循环可正常复制生成3条新闻条目,但仅最初创建的原始条目绑定了已上传的图片,副本条目无关联图片。
需要实现的效果:将原始条目绑定的图片复制到所有生成的副本条目,图片按{原图片文件名}_{assigned_pack_id}的规则重命名后,绑定到对应replicate()生成的副本数据上。
解决方案
代码中使用的是spatie/laravel-medialibrary处理媒体关联,replicate()方法默认不会复制模型关联的媒体文件,需要在循环生成副本时手动处理媒体复制,完整修改后的代码如下:
public function store(StoreNewsRequest $request) { $news = News::create($request->all()); if ($request->input('image', false)) { $news->addMedia(storage_path('tmp/uploads/' . $request->input('image')))->toMediaCollection('image'); } if ($media = $request->input('ck-media', false)) { Media::whereIn('id', $media)->update(['model_id' => $news->id]); } // 提前获取原始条目绑定的图片媒体实例,避免循环重复查询 $originalImage = $news->hasMedia('image') ? $news->getFirstMedia('image') : null; $assigned_pack_ids = DB::table('content_pack_news')->where('news_id' , $news->id)->pluck('content_pack_id')->toArray(); foreach($assigned_pack_ids as $key => $pack_id) { $news_replica = $news->replicate(); $news_replica->save(); DB::table('content_pack_news')->insert(['content_pack_id' => $pack_id,'news_id' => $news_replica->id]); // 存在原始图片时,复制并重命名绑定到副本 if ($originalImage) { // 拼接符合规则的新文件名 $newFileName = pathinfo($originalImage->file_name, PATHINFO_FILENAME) . '_' . $pack_id . '.' . $originalImage->extension; // 读取原始图片本地路径,重命名后存入副本的image媒体集合 $news_replica->addMedia($originalImage->getPath()) ->usingFileName($newFileName) ->toMediaCollection('image'); } } return redirect()->route('admin.news.index'); }
实现说明
- 提前在循环外获取原始条目的图片实例,减少不必要的数据库查询,提升执行效率
- 直接读取原始媒体文件的本地存储路径做复制,不需要重复读取临时上传目录的文件,减少磁盘IO
- 通过
usingFileName()方法直接指定复制后的文件名,完全匹配image_name_assigned_pack_id的命名规则 - 媒体文件复制完成后会自动生成
media表关联记录,和原始条目的媒体存储逻辑完全一致,不需要手动操作媒体表数据
内容的提问来源于stack exchange,提问作者Jessica Kimble
相关产品推荐
相关产品推荐

