PHP 层次结构构建数据


PHP 层次结构构建数据


正文

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


返回