ParentChildTree   A
last analyzed

Coupling/Cohesion

Components 1
Dependencies 0

Complexity

Total Complexity 25

Size/Duplication

Total Lines 205
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 6
Bugs 1 Features 1
Metric Value
c 6
b 1
f 1
dl 0
loc 205
ccs 0
cts 95
cp 0
rs 10
wmc 25
lcom 1
cbo 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 2
B buildTree() 0 19 5
A ungracefulExit() 0 6 2
B add() 0 26 4
A getTree() 0 4 1
A getChilds() 0 9 2
A recursiveGetChilds() 0 9 2
A sort() 0 5 1
B recursiveSort() 0 22 6
1
<?php
2
3
/**
4
 * Generate tree from a list of entries
5
 */
6
namespace Rocket\Utilities;
7
8
/**
9
 * Class to manage trees with "parent_id" columns
10
 */
11
class ParentChildTree
12
{
13
    /**
14
     * @var array The tree itself
15
     */
16
    public $tree;
17
18
    /**
19
     * @var array The flat tree to find elements
20
     */
21
    public $finder;
22
23
    /**
24
     * @var array Configuration values
25
     */
26
    public $config = [
27
        'id' => 'id',
28
        'childs' => 'childs',
29
        'parent' => 'parent_id',
30
        'default_parent' => [null, ''],
31
        'create_root' => false,
32
        'default_root' => [],
33
        'default_root_id' => 0,
34
    ];
35
36
    /**
37
     * Generate the tree
38
     *
39
     * @param array $tree_data The raw data to create a tree from
40
     * @param array $config The configuration on how to create this tree
41
     *
42
     * @throws \Exception
43
     */
44
    public function __construct($tree_data, $config = [])
45
    {
46
        //configure default vars
47
        $this->config = array_merge($this->config, $config);
48
49
        $this->tree = [];
50
        $this->finder = [];
51
52
        if ($this->config['create_root']) {
53
            $this->tree[$this->config['default_root_id']] = $this->config['default_root'];
54
            $this->finder[$this->config['default_root_id']] = &$this->tree[$this->config['default_root_id']];
55
        }
56
57
        $this->buildTree($tree_data);
58
    }
59
60
    /**
61
     * Build the tree from the received data
62
     *
63
     * @param array $tree_data The raw data to create a tree from
64
     * @throws \Exception
65
     */
66
    protected function buildTree($tree_data)
67
    {
68
        $parent_key = $this->config['parent'];
69
        $default_root_id = $this->config['default_root_id'];
70
71
        while (count($tree_data)) {
72
            $beginning_with = count($tree_data);
73
74
            foreach ($tree_data as $node_id => $node) {
75
                $node[$this->config['childs']] = [];
76
                $parent = (array_key_exists($parent_key, $node)) ? $node[$parent_key] : $default_root_id;
77
                if ($this->add($parent, $node[$this->config['id']], $node)) {
0 ignored issues
show
Documentation introduced by Stéphane Goetz
$node[$this->config['id']] is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
78
                    unset($tree_data[$node_id]);
79
                }
80
            }
81
82
            $this->ungracefulExit($beginning_with, $tree_data);
83
        }
84
    }
85
86
    /**
87
     * Exit the tree creation if the tree can't be built completely
88
     *
89
     * @param int $beginning_with The number of nodes left to place on the tree
90
     * @param array $tree_data The rest of the tree data to place
91
     * @throws \Exception
92
     */
93
    protected function ungracefulExit($beginning_with, $tree_data)
94
    {
95
        if ($beginning_with == count($tree_data)) {
96
            throw new \Exception('This tree has some missing parent items: ' . print_r($tree_data, true));
97
        }
98
    }
99
100
    /**
101
     * Add a leaf on the tree.
102
     *
103
     * @param  string $parent_id
104
     * @param  string $node_id
105
     * @param  array $node
106
     * @return bool
107
     */
108
    public function add($parent_id, $node_id, $node)
109
    {
110
        //is it a root ?
111
        if (in_array($parent_id, $this->config['default_parent'])) {
112
            if (!$this->config['create_root']) {
113
                $this->tree[$node_id] = $node;
114
                $this->finder[$node_id] = &$this->tree[$node_id];
115
116
                return true;
117
            }
118
119
            $node[$this->config['parent']] = $this->config['default_root_id'];
120
            $parent_id = $this->config['default_root_id'];
121
        }
122
123
        //is it in the finder ?
124
        if (array_key_exists($parent_id, $this->finder)) {
125
            $this->finder[$parent_id][$this->config['childs']][$node_id] = $node;
126
            $this->finder[$node_id] = &$this->finder[$parent_id][$this->config['childs']][$node_id];
127
128
            return true;
129
        }
130
131
        //could'nt find anything
132
        return false;
133
    }
134
135
    /**
136
     * Get the prepared tree.
137
     *
138
     * @return array
139
     */
140
    public function getTree()
141
    {
142
        return $this->tree;
143
    }
144
145
    /**
146
     * Get all node's children.
147
     *
148
     * @param  string $id The entry's ID
149
     * @return array
150
     */
151
    public function getChilds($id)
152
    {
153
        $result = [];
154
        if (array_key_exists($id, $this->finder)) {
155
            return $this->recursiveGetChilds($this->finder[$id][$this->config['childs']], $result);
156
        } else {
157
            return $result;
158
        }
159
    }
160
161
    /**
162
     * Internal recursive function to get children.
163
     *
164
     * @param  array $childs
165
     * @param  array $result
166
     * @return array
167
     */
168
    private function recursiveGetChilds($childs, $result)
169
    {
170
        foreach ($childs as $node) {
171
            $result[] = $node[$this->config['id']];
172
            $result = $this->recursiveGetChilds($node[$this->config['childs']], $result);
173
        }
174
175
        return $result;
176
    }
177
178
    /**
179
     * Sort the tree
180
     * @param mixed $key
181
     */
182
    public function sort($key)
183
    {
184
        $this->config['sort_key'] = $key;
185
        $this->recursiveSort($this->tree);
186
    }
187
188
    /**
189
     * Internal recursive function
190
     * @param  array $tree
191
     * @return bool
192
     */
193
    private function recursiveSort(&$tree)
194
    {
195
        //execute sort
196
        usort(
197
            $tree,
198
            function ($a, $b) {
199
                if ($a[$this->config['sort_key']] == $b[$this->config['sort_key']]) {
200
                    return 0;
201
                }
202
203
                return ($a[$this->config['sort_key']] < $b[$this->config['sort_key']]) ? -1 : 1;
204
            }
205
        );
206
207
        foreach ($tree as &$t) {
208
            if (array_key_exists($this->config['childs'], $t) && $t[$this->config['childs']] != '') {
209
                $t[$this->config['childs']] = $this->recursiveSort($t[$this->config['childs']]);
210
            }
211
        }
212
213
        return $tree;
214
    }
215
}
216