These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Encore\Admin\Traits; |
||
4 | |||
5 | use Illuminate\Database\Eloquent\Model; |
||
6 | use Illuminate\Support\Arr; |
||
7 | use Illuminate\Support\Facades\DB; |
||
8 | use Illuminate\Support\Facades\Request; |
||
9 | |||
10 | trait ModelTree |
||
11 | { |
||
12 | /** |
||
13 | * @var array |
||
14 | */ |
||
15 | protected static $branchOrder = []; |
||
16 | |||
17 | /** |
||
18 | * @var string |
||
19 | */ |
||
20 | protected $parentColumn = 'parent_id'; |
||
21 | |||
22 | /** |
||
23 | * @var string |
||
24 | */ |
||
25 | protected $titleColumn = 'title'; |
||
26 | |||
27 | /** |
||
28 | * @var string |
||
29 | */ |
||
30 | protected $orderColumn = 'order'; |
||
31 | |||
32 | /** |
||
33 | * @var \Closure |
||
34 | */ |
||
35 | protected $queryCallback; |
||
36 | |||
37 | /** |
||
38 | * Get children of current node. |
||
39 | * |
||
40 | * @return \Illuminate\Database\Eloquent\Relations\HasMany |
||
41 | */ |
||
42 | public function children() |
||
43 | { |
||
44 | return $this->hasMany(static::class, $this->parentColumn); |
||
45 | } |
||
46 | |||
47 | /** |
||
48 | * Get parent of current node. |
||
49 | * |
||
50 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo |
||
51 | */ |
||
52 | public function parent() |
||
53 | { |
||
54 | return $this->belongsTo(static::class, $this->parentColumn); |
||
55 | } |
||
56 | |||
57 | /** |
||
58 | * @return string |
||
59 | */ |
||
60 | public function getParentColumn() |
||
61 | { |
||
62 | return $this->parentColumn; |
||
63 | } |
||
64 | |||
65 | /** |
||
66 | * Set parent column. |
||
67 | * |
||
68 | * @param string $column |
||
69 | */ |
||
70 | public function setParentColumn($column) |
||
71 | { |
||
72 | $this->parentColumn = $column; |
||
73 | } |
||
74 | |||
75 | /** |
||
76 | * Get title column. |
||
77 | * |
||
78 | * @return string |
||
79 | */ |
||
80 | public function getTitleColumn() |
||
81 | { |
||
82 | return $this->titleColumn; |
||
83 | } |
||
84 | |||
85 | /** |
||
86 | * Set title column. |
||
87 | * |
||
88 | * @param string $column |
||
89 | */ |
||
90 | public function setTitleColumn($column) |
||
91 | { |
||
92 | $this->titleColumn = $column; |
||
93 | } |
||
94 | |||
95 | /** |
||
96 | * Get order column name. |
||
97 | * |
||
98 | * @return string |
||
99 | */ |
||
100 | public function getOrderColumn() |
||
101 | { |
||
102 | return $this->orderColumn; |
||
103 | } |
||
104 | |||
105 | /** |
||
106 | * Set order column. |
||
107 | * |
||
108 | * @param string $column |
||
109 | */ |
||
110 | public function setOrderColumn($column) |
||
111 | { |
||
112 | $this->orderColumn = $column; |
||
113 | } |
||
114 | |||
115 | /** |
||
116 | * Set query callback to model. |
||
117 | * |
||
118 | * @param \Closure|null $query |
||
119 | * |
||
120 | * @return $this |
||
121 | */ |
||
122 | public function withQuery(\Closure $query = null) |
||
123 | { |
||
124 | $this->queryCallback = $query; |
||
125 | |||
126 | return $this; |
||
127 | } |
||
128 | |||
129 | /** |
||
130 | * Format data to tree like array. |
||
131 | * |
||
132 | * @return array |
||
133 | */ |
||
134 | public function toTree() |
||
135 | { |
||
136 | return $this->buildNestedArray(); |
||
137 | } |
||
138 | |||
139 | /** |
||
140 | * Build Nested array. |
||
141 | * |
||
142 | * @param array $nodes |
||
143 | * @param int $parentId |
||
144 | * |
||
145 | * @return array |
||
146 | */ |
||
147 | protected function buildNestedArray(array $nodes = [], $parentId = 0) |
||
148 | { |
||
149 | $branch = []; |
||
150 | |||
151 | if (empty($nodes)) { |
||
152 | $nodes = $this->allNodes(); |
||
153 | } |
||
154 | |||
155 | foreach ($nodes as $node) { |
||
156 | if ($node[$this->parentColumn] == $parentId) { |
||
157 | $children = $this->buildNestedArray($nodes, $node[$this->getKeyName()]); |
||
158 | |||
159 | if ($children) { |
||
0 ignored issues
–
show
|
|||
160 | $node['children'] = $children; |
||
161 | } |
||
162 | |||
163 | $branch[] = $node; |
||
164 | } |
||
165 | } |
||
166 | |||
167 | return $branch; |
||
168 | } |
||
169 | |||
170 | /** |
||
171 | * Get all elements. |
||
172 | * |
||
173 | * @return mixed |
||
174 | */ |
||
175 | public function allNodes() |
||
176 | { |
||
177 | $orderColumn = DB::getQueryGrammar()->wrap($this->orderColumn); |
||
178 | $byOrder = $orderColumn.' = 0,'.$orderColumn; |
||
179 | |||
180 | $self = new static(); |
||
181 | |||
182 | if ($this->queryCallback instanceof \Closure) { |
||
183 | $self = call_user_func($this->queryCallback, $self); |
||
184 | } |
||
185 | |||
186 | return $self->orderByRaw($byOrder)->get()->toArray(); |
||
187 | } |
||
188 | |||
189 | /** |
||
190 | * Set the order of branches in the tree. |
||
191 | * |
||
192 | * @param array $order |
||
193 | * |
||
194 | * @return void |
||
195 | */ |
||
196 | protected static function setBranchOrder(array $order) |
||
197 | { |
||
198 | static::$branchOrder = array_flip(Arr::flatten($order)); |
||
199 | |||
200 | static::$branchOrder = array_map(function ($item) { |
||
201 | return ++$item; |
||
202 | }, static::$branchOrder); |
||
203 | } |
||
204 | |||
205 | /** |
||
206 | * Save tree order from a tree like array. |
||
207 | * |
||
208 | * @param array $tree |
||
209 | * @param int $parentId |
||
210 | */ |
||
211 | public static function saveOrder($tree = [], $parentId = 0) |
||
212 | { |
||
213 | if (empty(static::$branchOrder)) { |
||
214 | static::setBranchOrder($tree); |
||
215 | } |
||
216 | |||
217 | foreach ($tree as $branch) { |
||
218 | $node = static::find($branch['id']); |
||
219 | |||
220 | $node->{$node->getParentColumn()} = $parentId; |
||
221 | $node->{$node->getOrderColumn()} = static::$branchOrder[$branch['id']]; |
||
222 | $node->save(); |
||
223 | |||
224 | if (isset($branch['children'])) { |
||
225 | static::saveOrder($branch['children'], $branch['id']); |
||
226 | } |
||
227 | } |
||
228 | } |
||
229 | |||
230 | /** |
||
231 | * Get options for Select field in form. |
||
232 | * |
||
233 | * @param \Closure|null $closure |
||
234 | * @param string $rootText |
||
235 | * |
||
236 | * @return array |
||
237 | */ |
||
238 | public static function selectOptions(\Closure $closure = null, $rootText = 'ROOT') |
||
239 | { |
||
240 | $options = (new static())->withQuery($closure)->buildSelectOptions(); |
||
241 | |||
242 | return collect($options)->prepend($rootText, 0)->all(); |
||
243 | } |
||
244 | |||
245 | /** |
||
246 | * Build options of select field in form. |
||
247 | * |
||
248 | * @param array $nodes |
||
249 | * @param int $parentId |
||
250 | * @param string $prefix |
||
251 | * @param string $space |
||
252 | * |
||
253 | * @return array |
||
254 | */ |
||
255 | protected function buildSelectOptions(array $nodes = [], $parentId = 0, $prefix = '', $space = ' ') |
||
256 | { |
||
257 | $prefix = $prefix ?: '┝'.$space; |
||
258 | |||
259 | $options = []; |
||
260 | |||
261 | if (empty($nodes)) { |
||
262 | $nodes = $this->allNodes(); |
||
263 | } |
||
264 | |||
265 | foreach ($nodes as $index => $node) { |
||
266 | if ($node[$this->parentColumn] == $parentId) { |
||
267 | $node[$this->titleColumn] = $prefix.$space.$node[$this->titleColumn]; |
||
268 | |||
269 | $childrenPrefix = str_replace('┝', str_repeat($space, 6), $prefix).'┝'.str_replace(['┝', $space], '', $prefix); |
||
270 | |||
271 | $children = $this->buildSelectOptions($nodes, $node[$this->getKeyName()], $childrenPrefix); |
||
272 | |||
273 | $options[$node[$this->getKeyName()]] = $node[$this->titleColumn]; |
||
274 | |||
275 | if ($children) { |
||
276 | $options += $children; |
||
277 | } |
||
278 | } |
||
279 | } |
||
280 | |||
281 | return $options; |
||
282 | } |
||
283 | |||
284 | /** |
||
285 | * {@inheritdoc} |
||
286 | */ |
||
287 | public function delete() |
||
288 | { |
||
289 | $this->where($this->parentColumn, $this->getKey())->delete(); |
||
290 | |||
291 | return parent::delete(); |
||
292 | } |
||
293 | |||
294 | /** |
||
295 | * {@inheritdoc} |
||
296 | */ |
||
297 | protected static function boot() |
||
298 | { |
||
299 | parent::boot(); |
||
300 | |||
301 | static::saving(function (Model $branch) { |
||
302 | $parentColumn = $branch->getParentColumn(); |
||
303 | |||
304 | if (Request::has($parentColumn) && Request::input($parentColumn) == $branch->getKey()) { |
||
305 | throw new \Exception(trans('admin.parent_select_error')); |
||
306 | } |
||
307 | |||
308 | if (Request::has('_order')) { |
||
309 | $order = Request::input('_order'); |
||
310 | |||
311 | Request::offsetUnset('_order'); |
||
312 | |||
313 | static::tree()->saveOrder($order); |
||
314 | |||
315 | return false; |
||
316 | } |
||
317 | |||
318 | return $branch; |
||
319 | }); |
||
320 | } |
||
321 | } |
||
322 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.