Laravel删除事件在递归树删除中显示错误的子节点标题
我有一个自引用的Laravel模型用于分类树。每个节点可以有子节点,我想删除一个节点及其所有后代。
我的关系是这样的:
public function children()
{
return $this->hasMany(self::class, 'parent_id')->orderBy('sort');
}
我还定义了一个获取器,用来构建完整的树路径:
// Full title
public function getFitleAttribute()
{
return implode(' > ', array_map(function ($category) {
return $category->title;
}, $this->getTreePath()));
}
以及递归路径方法是:
public function getTreePath($model = null)
{
$path = [];
if ($model === null) {
$model = $this;
}
$path = $this->getTreePathNode($model);
return array_reverse($path);
}
public function getTreePathNode($model)
{
$path[] = $model;
if ($model->parent) {
$path = array_merge($path, $this->getTreePathNode($model->parent));
}
return $path;
}
当我在控制器中删除模型时,结果符合预期:
public function destroy(Category $category)
{
foreach ($category->children as $child) {
Log::info('deleting child title: ' . $child->fitle); // Print correct children title
Category::deleteWithChildren($child);
}
$category->delete();
return redirect(route('category.index'))
->with('status', __('Category deleted'));
}
protected static function deleteWithChildren($category)
{
foreach ($category->children as $child) {
self::deleteWithChildren($child);
}
$category->delete();
}
日志打印出正确的子节点标题。
然而,当我将递归删除移到模型的 deleting 事件时,日志显示了错误的值。它打印的是同级节点的标题,而不是实际子节点的标题:
public static function booted(): void
{
static::deleting(function ($category) {
Log::info('deleted title: ' . $category->fitle);
foreach ($category->children as $child) {
Log::info('deleting child title: ' . $child->fitle);
static::deleteWithChildren($child);
}
});
}
我期望在 deleting 事件中的 $category->children 始终包含当前模型的直接子节点。
为什么在 deleting 事件中,$category->children 的行为会不同?在Laravel中递归删除自引用树的正确方法是什么,以避免得到错误的子数据?
环境:Laravel框架10.48.22
解决方案
更新你模型的 booted 方法,并移除手动的 deleteWithChildren 辅助函数:
protected static function booted(): void
{
static::deleting(function ($category) {
// 1. Log the current category being deleted
Log::info('Deleting category: ' . $category->fitle);
// 2. Trigger delete on children.
// Use children()->get() to ensure a fresh query/collection
// for this specific deletion cycle.
$category->children()->get()->each(function ($child) {
$child->delete(); // This triggers the 'deleting' event for the child
});
});
}
你的控制器只需要调用一个单独的 delete():
public function destroy(Category $category)
{
// The event takes care of everything recursively
$category->delete();
return redirect(route('category.index'))
->with('status', __('Category deleted'));
}
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。