Laravel return 所有后代的id

Laravel return all the ids of descendants

如何 return AllSubSections 的所有 ID(所有级别)

class Section extends Model
{
    public function Ads()
    {
        return $this->hasMany(Ad::class);
    }

    public function AllSubSections()
    {
        return $this->SubSections()->with('AllSubSections');
    }

    public function SubSections()
    {
        return $this->hasMany(Section::class);
    }

    public function Parent()
    {
        return $this->belongsTo(Section::class);
    }
}

我现在正在做的是:

$section = Section::where('name', 'Properties')->first();
$subSections = $section->AllSubSections;
$subSections->pluck('id')

但它只是 return 第一级,而不是所有级别。

这是我带来的:

use Illuminate\Support\Collection;

class Section
{
    public function children ()
    {
        return $this->hasMany(Section::class,);
    }

    public function parent ()
    {
        return $this->belongsTo(Section::class);
    }

    public function getAllChildren ()
    {
        $sections = new Collection();

        foreach ($this->children as $section) {
            $sections->push($section);
            $sections = $sections->merge($section->getAllChildren());
        }

        return $sections;
    }
}

如您所见,getAllChildren 是一个递归函数。它包含一个遍历 children 部分的循环,将当前 child 添加到集合中并在此 child 上再次调用自身。

然后您可以使用:

$section->getAllChildren()->pluck('id');

您将获得所有 children id。

我希望我能回答这个问题!

根据上面@Hammerbot 的回答,对于那些导致性能问题的人,最可能的原因是你们的关系中存在无限循环(乱伦),例如

$child_id 3 has parent_id 2,
$child_id 2 has parent_id 3.

在这种情况下,使用上述递归函数 getAllChildren() 将陷入死循环并导致服务器抛出 500 错误。 @Community 和@BakerStreetSystems 对此进行了详细记录here

那怎么解决呢?

经过几个小时的搜索和研究,我发现通常有两种方法适用于解析递归关系集合(也称为 laravel 的 'tree' 集合)以获取指定键的值。

这里的一种方法是通过遍历每个子项来获取它们的任意属性,从而在一维中构建一个新集合。 仅在您的关系数据库不庞大且肯定没有乱伦关系的情况下使用@Hammerbot 的上述回答

另一种是使用预先加载的集合并尝试将其解析为 Json 字符串或数组,然后递归检索每个后代的任何属性,在这两种情况下都将通过深度嵌套进行。非常感谢 @Pedro's idea of combining Laravel pipe method and php array_walk_recursive

这是我的最终解决方案,希望对您也有帮助。

在您的类别模型中,定义父子关系和子子关系,

public function parent()
{
    return $this->belongsTo(self::class, 'parentCategoryNodeId', 'categoryNodeId');
}

public function children()
{
    return $this->hasMany(self::class, 'parentCategoryNodeId', 'categoryNodeId');
}

public function getAscendantsAttribute()
{
    $parents = collect([]);
    $parent = $this->parent;
    while(!is_null($parent)) {
        $parents->push($parent);
        $parent = $parent->parent;
    }
    return $parents;
}

public function descendants()
{
    return $this->children()->with('descendants');
}

并且在您的控制器中,您可以像这样调用所有子类别 ID 的数组,

$descendantsIds = $category->descendants->pipe(function ($collection){
    $array = $collection->toArray();
    $ids = [];
    array_walk_recursive($array, function ($value, $key) use (&$ids) {
        if ($key === 'categoryNodeId') {
            $ids[] = $value;
        };
    });
    return $ids;
});

如果您需要当前 $category 的链式上级数组,只需调用类别模型中定义的访问器,

$ascendantsIds = $category->ascendants->pluck('categoryNodeId');

如你所见,returns 也是数组格式,例如,

//[sub-sub-subcategory, sub-subcategory, subcategory, category]

如果您想循环浏览此视图以获得类似导航栏中的视图,则需要以相反的顺序进行,因此,请执行

$category->ascendants->reverse()

然后你可以显示类似:

//category > subcategory > sub-subcategory > sub-sub-subcategory

如果您有进一步的问题或更好的解决方案,请通过评论告诉我。