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
如果您有进一步的问题或更好的解决方案,请通过评论告诉我。
如何 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
如果您有进一步的问题或更好的解决方案,请通过评论告诉我。