ParentChildTree   A
last analyzed

Complexity

Total Complexity 25

Size/Duplication

Total Lines 205
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 0%

Importance

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

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 2
A buildTree() 0 19 5
A ungracefulExit() 0 6 2
A 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 namespace Rocket\UI\Taxonomy;
2
3
/**
4
 * Class to manage trees with "parent_id" columns
5
 */
6
class ParentChildTree
7
{
8
    /**
9
     * @var array The tree itself
10
     */
11
    public $tree;
12
13
    /**
14
     * @var array The flat tree to find elements
15
     */
16
    public $finder;
17
18
    /**
19
     * @var array Configuration values
20
     */
21
    public $config = [
22
        'id' => 'id',
23
        'childs' => 'childs',
24
        'parent' => 'parent_id',
25
        'default_parent' => [null, ''],
26
        'create_root' => false,
27
        'default_root' => [],
28
        'default_root_id' => 0,
29
    ];
30
31
    /**
32
     * Generate the tree
33
     *
34
     * @param array $tree_data The raw data to create a tree from
35
     * @param array $config The configuration on how to create this tree
36
     *
37
     * @throws \Exception
38
     */
39
    public function __construct($tree_data, $config = [])
40
    {
41
        //configure default vars
42
        $this->config = array_merge($this->config, $config);
43
44
        $this->tree = [];
45
        $this->finder = [];
46
47
        if ($this->config['create_root']) {
48
            $this->tree[$this->config['default_root_id']] = $this->config['default_root'];
49
            $this->finder[$this->config['default_root_id']] = &$this->tree[$this->config['default_root_id']];
50
        }
51
52
        $this->buildTree($tree_data);
53
    }
54
55
    /**
56
     * Build the tree from the received data
57
     *
58
     * @param array $tree_data The raw data to create a tree from
59
     * @throws \Exception
60
     */
61
    protected function buildTree($tree_data)
62
    {
63
        $parent_key = $this->config['parent'];
64
        $default_root_id = $this->config['default_root_id'];
65
66
        while (count($tree_data)) {
67
            $beginning_with = count($tree_data);
68
69
            foreach ($tree_data as $node_id => $node) {
70
                $node[$this->config['childs']] = [];
71
                $parent = (array_key_exists($parent_key, $node)) ? $node[$parent_key] : $default_root_id;
72
                if ($this->add($parent, $node[$this->config['id']], $node)) {
0 ignored issues
show
Documentation introduced by
$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...
73
                    unset($tree_data[$node_id]);
74
                }
75
            }
76
77
            $this->ungracefulExit($beginning_with, $tree_data);
78
        }
79
    }
80
81
    /**
82
     * Exit the tree creation if the tree can't be built completely
83
     *
84
     * @param int $beginning_with The number of nodes left to place on the tree
85
     * @param array $tree_data The rest of the tree data to place
86
     * @throws \Exception
87
     */
88
    protected function ungracefulExit($beginning_with, $tree_data)
89
    {
90
        if ($beginning_with == count($tree_data)) {
91
            throw new \Exception('This tree has some missing parent items: ' . print_r($tree_data, true));
92
        }
93
    }
94
95
    /**
96
     * Add a leaf on the tree.
97
     *
98
     * @param  string $parent_id
99
     * @param  string $node_id
100
     * @param  array $node
101
     * @return bool
102
     */
103
    public function add($parent_id, $node_id, $node)
104
    {
105
        //is it a root ?
106
        if (in_array($parent_id, $this->config['default_parent'])) {
107
            if (!$this->config['create_root']) {
108
                $this->tree[$node_id] = $node;
109
                $this->finder[$node_id] = &$this->tree[$node_id];
110
111
                return true;
112
            }
113
114
            $node[$this->config['parent']] = $this->config['default_root_id'];
115
            $parent_id = $this->config['default_root_id'];
116
        }
117
118
        //is it in the finder ?
119
        if (array_key_exists($parent_id, $this->finder)) {
120
            $this->finder[$parent_id][$this->config['childs']][$node_id] = $node;
121
            $this->finder[$node_id] = &$this->finder[$parent_id][$this->config['childs']][$node_id];
122
123
            return true;
124
        }
125
126
        //could'nt find anything
127
        return false;
128
    }
129
130
    /**
131
     * Get the prepared tree.
132
     *
133
     * @return array
134
     */
135
    public function getTree()
136
    {
137
        return $this->tree;
138
    }
139
140
    /**
141
     * Get all node's children.
142
     *
143
     * @param  string $id The entry's ID
144
     * @return array
145
     */
146
    public function getChilds($id)
147
    {
148
        $result = [];
149
        if (array_key_exists($id, $this->finder)) {
150
            return $this->recursiveGetChilds($this->finder[$id][$this->config['childs']], $result);
151
        } else {
152
            return $result;
153
        }
154
    }
155
156
    /**
157
     * Internal recursive function to get children.
158
     *
159
     * @param  array $childs
160
     * @param  array $result
161
     * @return array
162
     */
163
    private function recursiveGetChilds($childs, $result)
164
    {
165
        foreach ($childs as $node) {
166
            $result[] = $node[$this->config['id']];
167
            $result = $this->recursiveGetChilds($node[$this->config['childs']], $result);
168
        }
169
170
        return $result;
171
    }
172
173
    /**
174
     * Sort the tree
175
     * @param mixed $key
176
     */
177
    public function sort($key)
178
    {
179
        $this->config['sort_key'] = $key;
180
        $this->recursiveSort($this->tree);
181
    }
182
183
    /**
184
     * Internal recursive function
185
     * @param  array $tree
186
     * @return bool
187
     */
188
    private function recursiveSort(&$tree)
189
    {
190
        //execute sort
191
        usort(
192
            $tree,
193
            function ($a, $b) {
194
                if ($a[$this->config['sort_key']] == $b[$this->config['sort_key']]) {
195
                    return 0;
196
                }
197
198
                return ($a[$this->config['sort_key']] < $b[$this->config['sort_key']]) ? -1 : 1;
199
            }
200
        );
201
202
        foreach ($tree as &$t) {
203
            if (array_key_exists($this->config['childs'], $t) && $t[$this->config['childs']] != '') {
204
                $t[$this->config['childs']] = $this->recursiveSort($t[$this->config['childs']]);
205
            }
206
        }
207
208
        return $tree;
209
    }
210
}
211