tree-tools-php
114 строк · 3.0 Кб
1<?php
2
3declare(strict_types=1);
4
5namespace Smoren\TreeTools;
6
7use Smoren\TypeTools\MapAccess;
8use stdClass;
9
10class TreeBuilder
11{
12/**
13* Builds a tree from given flat collection of items with relations.
14*
15* @param iterable<mixed> $collection
16* @param string $idField
17* @param string $parentIdField
18* @param string $childrenContainerField
19* @param string $itemContainerField
20*
21* @return array<mixed>
22*/
23public static function build(
24iterable $collection,
25string $idField = 'id',
26string $parentIdField = 'parent_id',
27string $childrenContainerField = 'children',
28string $itemContainerField = 'item'
29): array {
30$result = [];
31$map = [];
32
33foreach($collection as $item) {
34$map[MapAccess::get($item, $idField)] = static::wrapItem(
35$item,
36$childrenContainerField,
37$itemContainerField
38);
39}
40
41foreach($map as &$item) {
42if(($parentId = static::getParentId($item, $parentIdField, $itemContainerField)) !== null) {
43$childrenContainer = &static::getChildrenContainer($map[$parentId], $childrenContainerField);
44$childrenContainer[] = &$item;
45} else {
46$result[] = &$item;
47}
48}
49
50return $result;
51}
52
53/**
54* Returns value of parent relation.
55*
56* @param mixed $item
57* @param string $parentIdField
58* @param string $itemContainerField
59*
60* @return scalar|null
61*/
62protected static function getParentId($item, string $parentIdField, string $itemContainerField)
63{
64/** @var scalar|null $parentId */
65$parentId = MapAccess::get($item, $parentIdField);
66
67if($parentId !== null) {
68return $parentId;
69}
70
71return MapAccess::get(MapAccess::get($item, $itemContainerField), $parentIdField);
72}
73
74/**
75* Returns children container of given item.
76*
77* @param mixed $item
78* @param string $childrenContainerField
79*
80* @return array<mixed>
81*/
82protected static function &getChildrenContainer(&$item, string $childrenContainerField): array
83{
84if(is_array($item)) {
85return $item[$childrenContainerField];
86}
87
88return $item->{$childrenContainerField};
89}
90
91/**
92* Wraps collection item for tree representation.
93*
94* @param mixed $item
95* @param string $childrenContainerField
96* @param string $itemContainerField
97*
98* @return array<mixed>|stdClass
99*/
100protected static function wrapItem($item, string $childrenContainerField, string $itemContainerField)
101{
102if(is_array($item)) {
103$item[$childrenContainerField] = [];
104return $item;
105}
106
107if($item instanceof stdClass) {
108$item->{$childrenContainerField} = [];
109return $item;
110}
111
112return [$itemContainerField => &$item, $childrenContainerField => []];
113}
114}
115