laravel-nestedset 是一款基于嵌套集合模型(Nested Set Model)的用于实现有序树的 laravel 扩展包。
什么是嵌套集合模型?
嵌套集合或嵌套集合模型是一种在关系数据库表中高效存储分层数据的方法,理论基础为预排序遍历树算法(MPTT,Modified Preorder Tree Taversal)。
嵌套集合模型是根据树遍历对树中的每个节点进行数字编号,遍历会访问每个节点两次,按访问顺序分配数字编号,并在两次访问中都分配。这将为每个节点留下两个数字编号,它们作为节点的两个字段存储。这使得查询变得高效——可以通过比较这些数字编号来获得层级结构关系。但是更新数据将需要给节点重新分配数字,因此变得低效。尽管很复杂但是可以通过不使用整数而是使用有理数来提升写操作的效率。读高效,写低效
其常用的应用场景为实现无限级分类。
使用laravel-nestedset
使用 laravel-nestedset
不仅可以实现无限级分类,同样可以实现动态权限路由。
比如这里我们要实现根据权限获取系统整个菜单树中的局部菜单树(也可以是整个菜单树,这个根据权限过滤后的结果):
// 前置逻辑已经获取到根据权限筛选出的菜单路由id(menusIds)
// 使用linkNodes方法可以为数据集合中的每个节点重新填充父子关系,即生成树结构
$menus = Menu::whereIn('id', $menuIds)
->orWhereIn('parent_id', $menuIds)
->get()
->linkNodes();
// 过滤其中的非root节点
$user->menus = $menus->filter(function($item){
return $item->isRoot();
});
linkNodes
方法为数据集合中的每个节点重新填充父子关系,其源码如下:
<?php
namespace Kalnoy\Nestedset;
use Illuminate\Database\Eloquent\Collection as BaseCollection;
use Illuminate\Database\Eloquent\Model;
class Collection extends BaseCollection
{
/**
* Fill `parent` and `children` relationships for every node in the collection.
*
* This will overwrite any previously set relations.
*
* @return $this
*/
public function linkNodes()
{
if ($this->isEmpty()) return $this;
$groupedNodes = $this->groupBy($this->first()->getParentIdName());
/** @var NodeTrait|Model $node */
foreach ($this->items as $node) {
if ( ! $node->getParentId()) {
$node->setRelation('parent', null);
}
$children = $groupedNodes->get($node->getKey(), [ ]);
/** @var Model|NodeTrait $child */
foreach ($children as $child) {
$child->setRelation('parent', $node);
}
$node->setRelation('children', BaseCollection::make($children));
}
return $this;
}
Ref:
Tree by Nodes #292
左右值无限级分类算法(预排序遍历树算法)
嵌套集合模型(Nested set model)介绍
kalnoy/nestedset