Laravel:booted函数中save触发死循环,求slug正确存储方案
解决Laravel中saved事件调用save()导致无限循环的Slug生成问题
你遇到的问题核心是无限循环:saved事件会在模型创建/更新完成后触发,而你在回调里调用$discussion->save(),会再次触发saved事件,反复执行下去最终导致服务器资源耗尽卡死。
下面是几种可行的解决方案:
方案1:使用updateQuietly()(推荐,Laravel 8.42+)
updateQuietly()方法会直接更新数据库,不会触发任何模型事件,完美避免循环。同时增加判断逻辑,只有当slug确实需要更新时才执行操作,减少不必要的数据库请求:
protected static function booted(): void { static::saved(function (Discussion $discussion) { $newSlug = Str::slug($discussion->subject, '-') . '-' . $discussion->id; // 仅当slug发生变化时才更新 if ($discussion->slug !== $newSlug) { $discussion->updateQuietly(['slug' => $newSlug]); } }); static::deleting(function (Discussion $discussion) { $discussion->groups()->detach(); $discussion->registrations()->delete(); }); }
方案2:使用saveQuietly()或临时禁用事件(兼容低版本Laravel)
如果你的Laravel版本低于8.42,可以用saveQuietly()(Laravel 8+支持),或者用withoutEvents()临时禁用事件:
static::saved(function (Discussion $discussion) { $newSlug = Str::slug($discussion->subject, '-') . '-' . $discussion->id; if ($discussion->slug !== $newSlug) { $discussion->slug = $newSlug; // 方法A:使用saveQuietly $discussion->saveQuietly(); // 方法B:临时禁用事件包裹操作 // $discussion->withoutEvents(function () use ($discussion) { // $discussion->save(); // }); } });
方案3:拆分事件为created和updated(更精准控制)
既然创建时需要ID,created事件是创建完成后触发(此时已有ID),更新时监听updated事件,分开处理逻辑:
protected static function booted(): void { static::created(function (Discussion $discussion) { $discussion->slug = Str::slug($discussion->subject, '-') . '-' . $discussion->id; $discussion->saveQuietly(); }); static::updated(function (Discussion $discussion) { // 仅当subject字段修改时,才更新slug if ($discussion->isDirty('subject')) { $newSlug = Str::slug($discussion->subject, '-') . '-' . $discussion->id; if ($discussion->slug !== $newSlug) { $discussion->updateQuietly(['slug' => $newSlug]); } } }); static::deleting(function (Discussion $discussion) { $discussion->groups()->detach(); $discussion->registrations()->delete(); }); }
内容的提问来源于stack exchange,提问作者Duddy67
相关产品推荐
相关产品推荐

