Laravel中restore()恢复软删除记录时新增重复项的问题求助
修复软删除收藏记录重复创建问题
问题背景
实现用户收藏功能时,Favourite模型启用了软删除机制。控制器逻辑为:先检查是否存在对应user_id、content_model和content_id的记录(包含已软删除条目),若存在已删除记录则调用restore()恢复,但实际操作中每次都会新增重复记录,日志持续显示创建新条目。
相关代码
控制器原代码
public function setUserFavourite(Request $request): \Illuminate\Http\JsonResponse { $user = auth()->user(); $contentModel = $request->input('contentModel'); $contentId = $request->input('contentId'); $existingFavourite = $user->favourites() ->byContentAlias($contentModel) ->where('content_id', $contentId) ->first(); if ($existingFavourite) { if ($existingFavourite->trashed()) { $existingFavourite->restore(); \Log::info('Favourite restored:', ['favourite_id' => $existingFavourite->id]); return $this->success(new FavouriteResource($existingFavourite), 'Content added to favourites'); } return $this->generalError('This Content is already a favourite'); } $favourite = new Favourite(['user_id' => $user->id]); switch ($contentModel){ case 'Venue': $venue = Venue::find($contentId); $favourite->favourable()->associate($venue); break; case 'Organiser': $organiser = Organiser::find($contentId); $favourite->favourable()->associate($organiser); break; case 'Event': $event = Event::find($contentId); $favourite->favourable()->associate($event); break; default: return $this->generalError('Invalid Content Model'); } $favourite->save(); \Log::info('Favourite created:', ['favourite_id' => $favourite->id]); return $this->success(new FavouriteResource($favourite),'Content added to favourites'); }
Favourite模型原代码
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\SoftDeletes; class Favourite extends Model { use HasFactory; use SoftDeletes; protected $table = 'favourites'; protected $fillable = [ 'user_id','content_model','content_id' ]; public function favourable(): MorphTo { return $this->morphTo(__FUNCTION__,'content_model','content_id'); } public function scopeByContentAlias($query, $contentAlias) { $contentModelNamespace = "App\Models\\" . ucfirst($contentAlias); return $query->where('content_model', $contentModelNamespace); } }
User模型原代码
class User extends Authenticatable implements MustVerifyEmail { use HasRoles ,HasApiTokens, HasFactory, Notifiable; public function favourites(): HasMany { return $this->hasMany(Favourite::class,'user_id'); } }
问题原因
Laravel的hasMany关联默认会自动过滤deleted_at不为null的软删除记录,导致$user->favourites()查询无法找到已软删除的收藏条目,每次都进入创建新记录的分支。
解决方案
1. 修改查询逻辑,包含软删除记录
在查询已存在的收藏时,添加withTrashed()方法,让查询包含软删除的条目:
$existingFavourite = $user->favourites() ->withTrashed() // 新增此行,包含软删除记录 ->byContentAlias($contentModel) ->where('content_id', $contentId) ->first();
2. 可选:优化关联定义(按需选择)
如果需要频繁在用户收藏关联中包含软删除记录,可以修改User模型的关联方法,默认包含软删除:
public function favourites(): HasMany { return $this->hasMany(Favourite::class,'user_id')->withTrashed(); }
注意:此修改会让所有$user->favourites查询都包含软删除记录,若有其他场景不需要,建议还是在单独查询时添加withTrashed()更灵活。
3. 验证修复效果
修改后,再次操作已软删除的收藏内容时,系统会找到对应记录并执行restore(),日志会输出恢复的记录ID而非创建新ID,数据库中不会再出现重复条目。
额外优化建议
- 数据库层面添加唯一索引,防止
user_id、content_model、content_id组合重复,即使代码逻辑出错也能从底层避免重复:ALTER TABLE favourites ADD UNIQUE INDEX user_content_unique (user_id, content_model, content_id); - 简化模型关联的
associate逻辑,避免冗余的switch语句:$modelClass = "App\Models\\" . ucfirst($contentModel); if (!class_exists($modelClass)) { return $this->generalError('Invalid Content Model'); } $model = $modelClass::find($contentId); if (!$model) { return $this->generalError('Content not found'); } $favourite->favourable()->associate($model);
内容的提问来源于stack exchange,提问作者user23398056
相关产品推荐
相关产品推荐

