正文
bluem/tree
composer引入:
"bluem/tree": "^3.1",
Creating a tree
// Create the tree with an array of arrays (or use an array of Iterators,
// Traversable of arrays or Traversable of Iterators):
$data = [
['id' => 1, 'parent' => 0, 'title' => 'Node 1'],
['id' => 2, 'parent' => 1, 'title' => 'Node 1.1'],
['id' => 3, 'parent' => 0, 'title' => 'Node 3'],
['id' => 4, 'parent' => 1, 'title' => 'Node 1.2'],
];
$tree = new BlueM\Tree($data);
// When using a data source that uses different keys for "id" and "parent",
// or if the root node ID is not 0 (in this example: -1), use the options
// array you can pass to the constructor:
$data = [
['nodeId' => 1, 'parentId' => -1, 'title' => 'Node 1'],
['nodeId' => 2, 'parentId' => 1, 'title' => 'Node 1.1'],
['nodeId' => 3, 'parentId' => -1, 'title' => 'Node 3'],
['nodeId' => 4, 'parentId' => 1, 'title' => 'Node 1.2'],
];
$tree = new BlueM\Tree(
$data,
['rootId' => -1, 'id' => 'nodeId', 'parent' => 'parentId']
);
Updating the tree with new data
// Rebuild the tree from new data
$tree->rebuildWithData($newData);
Retrieving nodes
// Get the top-level nodes (returns array)
$rootNodes = $tree->getRootNodes();
// Get all nodes (returns array)
$allNodes = $tree->getNodes();
// Get a single node by its unique identifier
$node = $tree->getNodeById(12345);
Getting a node’s parent, siblings, children, ancestors and descendants
// Get a node's parent node (will be null for the root node)
$parentNode = $node->getParent();
// Get a node's siblings as an array
$siblings = $node->getSiblings();
// Ditto, but include the node itself (identical to $node->getParent()->getChildren())
$siblings = $node->getSiblingsAndSelf();
// Get a node's preceding sibling (null, if there is no preceding sibling)
$precedingSibling = $node->getPrecedingSibling();
// Get a node's following sibling (null, if there is no following sibling)
$followingSibling = $node->getFollowingSibling();
// Does the node have children?
$bool = $node->hasChildren();
// Get the number of Children
$bool = $node->countChildren();
// Get a node's child nodes
$children = $node->getChildren();
// Get a node's ancestors (parent, grandparent, ...)
$ancestors = $node->getAncestors();
// Ditto, but include the node itself
$ancestorsPlusSelf = $node->getAncestorsAndSelf();
// Get a node's descendants (children, grandchildren, ...)
$descendants = $node->getDescendants();
// Ditto, but include the node itself
$descendantsPlusSelf = $node->getDescendantsAndSelf();
Accessing a node’s properties
// Get a node's ID
$id = $node->getId();
// Get the node's hierarchical level (1-based)
$level = $node->getLevel();
// Access node properties using get() overloaded getters or __get():
$value = $node->get('myproperty');
$value = $node->myproperty;
$value = $node->getMyProperty();
// Get the node's properties as an associative array
$array = $node->toArray();
// Get a string representation (which will be the node ID)
echo "$node";
wxxiong6/tree
composer引入:
"wxxiong6/tree": "^1.2",
tree
- 使用递归把数据构造成树形结构数据
- 常用于菜单、权限、分类等功能实现
- 避免多次查询数据库,使用php来处理
Installation
composer require wxxiong6/tree
Usage
// 设置主键、parent标识名称 子节点名称
Tree::setConfig($primary = '', $parentId = '', $child = '');
// 生成tree
$tree = Tree::makeTree($data);
// 查找子类
$children = Tree::findChild($tree, 0);
示例:
$data = [
['id' => '1', 'city' => '中国', 'parent_id' => '0'],
['id' => '2', 'city' => '北京', 'parent_id' => '1'],
['id' => '3', 'city' => '北京市', 'parent_id' => '2'],
['id' => '4', 'city' => '东城区', 'parent_id' => '3'],
['id' => '5', 'city' => '西城区', 'parent_id' => '3'],
['id' => '6', 'city' => '丰台区', 'parent_id' => '3'],
['id' => '7', 'city' => '海淀区', 'parent_id' => '3'],
['id' => '8', 'city' => '房山区', 'parent_id' => '3'],
['id' => '9', 'city' => '通州区', 'parent_id' => '3'],
['id' => '10', 'city' => '昌平区', 'parent_id' => '3'],
['id' => '11', 'city' => '上海', 'parent_id' => '1'],
['id' => '12', 'city' => '上海市', 'parent_id' => '11'],
['id' => '13', 'city' => '黄浦区', 'parent_id' => '11'],
['id' => '14', 'city' => '长宁区', 'parent_id' => '11'],
['id' => '15', 'city' => '卢湾区', 'parent_id' => '11'],
['id' => '16', 'city' => '徐汇区', 'parent_id' => '11'],
['id' => '17', 'city' => '普陀区', 'parent_id' => '11'],
['id' => '18', 'city' => '闸北区', 'parent_id' => '11'],
['id' => '19', 'city' => '虹口区', 'parent_id' => '11'],
];
Tree::setConfig('id', 'parent_id', 'children');
$tree = Tree::makeTree($data);
运行结果:
Array
(
[0] = Array
(
[id] = 1
[city] = 中国
[parent_id] = 0
[child] = Array
(
[0] = Array
(
[id] = 2
[city] = 北京
[parent_id] = 1
[child] = Array
(
[0] = Array
(
[id] = 3
[city] = 北京市
[parent_id] = 2
[child] = Array
(
[0] = Array
(
[id] = 4
[city] = 东城区
[parent_id] = 3
)
)
)
)
)
[1] = Array
(
[id] = 11
[city] = 上海
[parent_id] = 1
[child] = Array
(
[0] = Array
(
[id] = 12
[city] = 上海市
[parent_id] = 11
)
)
)
)
)
)
修正版
上面只适合于顶级有一个的树,如顶级部门id为0,如果有多个或者不知道顶级部门id将不再适用。 我们需要完善这个结构,源码如下。
<?php
namespace app\widgets;
use wxxiong6\tree\Tree as TreeLadder;
/**
* 树组织组件
* Class Tree
* @package app\widgets
*/
class Tree
{
/**
* 主键名称.
*
* @var string
*/
private static $primary = 'id';
/**
* 父键名称.
*
* @var string
*/
private static $parentId = 'parent_id';
/**
* 子节点名称.
*
* @var string
*/
private static $child = 'child';
/**
* 修改主键名称、父键名称、子节点名称.
*
* @param string $primary
* @param string $parentId
* @param string $child
*/
public static function setConfig($primary = '', $parentId = '', $child = '')
{
if (!empty($primary)) {
self::$primary = $primary;
}
if (!empty($parentId)) {
self::$parentId = $parentId;
}
if (!empty($child)) {
self::$child = $child;
}
}
/**
* 生成Tree.
*
* @param array $data 需要处理的数据(二维数组)
* @param int $index 初始parent id
*
* @return array
*/
public static function makeTree($data, $index = null)
{
if (empty($data)) {
return [];
}
// 指定父级索引
if ($index !== null) {
TreeLadder::setConfig(self::$primary, self::$parentId, self::$child);
return TreeLadder::makeTree($data, $index);
}
$first_layer_ids = [];
foreach ($data as $item1)
{
$parent_is_exit = false;
foreach ($data as $item2) {
if ($item1[self::$parentId] == $item2[self::$primary]) {
$parent_is_exit = true;
break;
}
}
if (!$parent_is_exit) {
array_push($first_layer_ids, $item1[self::$parentId]);
}
}
$tree = [];
TreeLadder::setConfig(self::$primary, self::$parentId, self::$child);
foreach ($first_layer_ids as $parent_id) {
$part = TreeLadder::makeTree($data, $parent_id);
$tree = array_merge($tree, $part);
}
return $tree;
}
}
源码
<?php
/**
* Tree 构建tree状数据.
*/
namespace wxxiong6\tree;
class Tree
{
/**
* 主键名称.
*
* @var string
*/
private static $primary = 'id';
/**
* 父键名称.
*
* @var string
*/
private static $parentId = 'parent_id';
/**
* 子节点名称.
*
* @var string
*/
private static $child = 'child';
/**
* 修改主键名称、父键名称、子节点名称.
*
* @param string $primary
* @param string $parentId
* @param string $child
*/
public static function setConfig($primary = '', $parentId = '', $child = '')
{
if (!empty($primary)) {
self::$primary = $primary;
}
if (!empty($parentId)) {
self::$parentId = $parentId;
}
if (!empty($child)) {
self::$child = $child;
}
}
/**
* 生成Tree.
*
* @param array $data 需要处理的数据(二维数组)
* @param int $index 初始parent id
*
* @return array
*/
public static function makeTree(&$data, $index = 0)
{
if (empty($data)) {
return [];
}
$children = self::findChild($data, $index);
if (empty($children)) {
return $children;
}
foreach ($children as $k => &$v) {
if (empty($data)) {
break;
}
$child = self::makeTree($data, $v[self::$primary]);
if (!empty($child)) {
$v[self::$child] = $child;
}
}
unset($v);
return $children;
}
/**
* 查找子类.
*
* @param array $data
* @param number $index
*
* @return array
*/
public static function findChild(&$data, $index)
{
$children = [];
foreach ($data as $k => $v) {
if ((string) ($v[self::$parentId]) === (string) $index) {
$children[] = $v;
unset($data[$k]);
}
}
return $children;
}
}
参考资料
BlueM/Tree https://github.com/bluem/tree
wxxiong6/tree https://github.com/wxxiong6/tree