1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SleepingOwl\Admin\Display; |
4
|
|
|
|
5
|
|
|
use Exception; |
6
|
|
|
use Illuminate\Routing\Router; |
7
|
|
|
use Illuminate\Support\Facades\Request; |
8
|
|
|
use Illuminate\Database\Eloquent\Builder; |
9
|
|
|
use SleepingOwl\Admin\Traits\CardControl; |
10
|
|
|
use Illuminate\Database\Eloquent\Collection; |
11
|
|
|
use SleepingOwl\Admin\Display\Extension\Columns; |
12
|
|
|
use SleepingOwl\Admin\Display\Tree\OrderTreeType; |
13
|
|
|
use SleepingOwl\Admin\Repositories\TreeRepository; |
14
|
|
|
use SleepingOwl\Admin\Contracts\WithRoutesInterface; |
15
|
|
|
use SleepingOwl\Admin\Contracts\Display\ColumnInterface; |
16
|
|
|
use SleepingOwl\Admin\Contracts\Repositories\TreeRepositoryInterface; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* @method TreeRepositoryInterface getRepository() |
20
|
|
|
* @property TreeRepositoryInterface $repository |
21
|
|
|
* @method Columns getColumns() |
22
|
|
|
* @method $this setColumns(ColumnInterface|ColumnInterface[] $column) |
23
|
|
|
*/ |
24
|
|
|
class DisplayTree extends Display implements WithRoutesInterface |
25
|
|
|
{ |
26
|
|
|
use CardControl; |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @param Router $router |
30
|
|
|
*/ |
31
|
|
|
public static function registerRoutes(Router $router) |
32
|
|
|
{ |
33
|
|
|
$routeName = 'admin.display.tree.reorder'; |
34
|
|
|
if (! $router->has($routeName)) { |
35
|
|
|
$router->post('{adminModel}/reorder', |
36
|
|
|
['as' => $routeName, 'uses' => 'SleepingOwl\Admin\Http\Controllers\DisplayController@treeReorder']); |
|
|
|
|
37
|
|
|
} |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* @var int |
42
|
|
|
*/ |
43
|
|
|
protected $max_depth = 20; |
44
|
|
|
/** |
45
|
|
|
* @var string |
46
|
|
|
*/ |
47
|
|
|
protected $view = 'display.tree'; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* @var array |
51
|
|
|
*/ |
52
|
|
|
protected $parameters = []; |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* @var bool |
56
|
|
|
*/ |
57
|
|
|
protected $reorderable = true; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @var string|callable |
61
|
|
|
*/ |
62
|
|
|
protected $value = 'title'; |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* @var bool |
66
|
|
|
*/ |
67
|
|
|
protected $collapsed; |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* @var string |
71
|
|
|
*/ |
72
|
|
|
protected $parentField = 'parent_id'; |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* @var string |
76
|
|
|
*/ |
77
|
|
|
protected $orderField = 'order'; |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* @var string|null |
81
|
|
|
*/ |
82
|
|
|
protected $rootParentId = null; |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* @var string |
86
|
|
|
*/ |
87
|
|
|
protected $repositoryClass = TreeRepository::class; |
88
|
|
|
|
89
|
|
|
/** |
90
|
|
|
* @var Column\TreeControl |
91
|
|
|
*/ |
92
|
|
|
protected $controlColumn; |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* @var Collection |
96
|
|
|
*/ |
97
|
|
|
protected $collection; |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* @var string|null |
101
|
|
|
*/ |
102
|
|
|
protected $newEntryButtonText; |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* @var string |
106
|
|
|
*/ |
107
|
|
|
protected $treeType; |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* DisplayTree constructor. |
111
|
|
|
* |
112
|
|
|
* @param string|null $treeType |
113
|
|
|
*/ |
114
|
|
|
public function __construct($treeType = null) |
115
|
|
|
{ |
116
|
|
|
parent::__construct(); |
117
|
|
|
|
118
|
|
|
$this->treeType = $treeType; |
119
|
|
|
|
120
|
|
|
$this->setCardClass('card-tree'); |
121
|
|
|
|
122
|
|
|
$this->extend('columns', new Columns()); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
public function initialize() |
126
|
|
|
{ |
127
|
|
|
parent::initialize(); |
128
|
|
|
|
129
|
|
|
$repository = $this->getRepository() |
130
|
|
|
->setOrderField($this->getOrderField()) |
131
|
|
|
->setRootParentId($this->getRootParentId()); |
132
|
|
|
|
133
|
|
|
if ($this->getParentField()) { |
134
|
|
|
$repository = $repository->setParentField($this->getParentField()); |
135
|
|
|
} |
136
|
|
|
if (! is_null($this->treeType)) { |
|
|
|
|
137
|
|
|
$repository->setTreeType($this->treeType); |
|
|
|
|
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
if ($this->treeType == OrderTreeType::class) { |
141
|
|
|
$this->setMaxDepth(1); |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
$this->setHtmlAttribute('data-max-depth', $this->getMaxDepth()); |
145
|
|
|
$this->setHtmlAttribute('data-collapsed', $this->getCollapsed()); |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* @return string |
150
|
|
|
*/ |
151
|
|
|
public function getMaxDepth() |
152
|
|
|
{ |
153
|
|
|
return $this->max_depth; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* @param string|callable $value |
158
|
|
|
* |
159
|
|
|
* @return $this |
160
|
|
|
*/ |
161
|
|
|
public function setMaxDepth($value) |
162
|
|
|
{ |
163
|
|
|
$this->max_depth = $value; |
|
|
|
|
164
|
|
|
|
165
|
|
|
return $this; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* @return callable|string |
170
|
|
|
*/ |
171
|
|
|
public function getValue() |
172
|
|
|
{ |
173
|
|
|
return $this->value; |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* @param string|callable $value |
178
|
|
|
* |
179
|
|
|
* @return $this |
180
|
|
|
*/ |
181
|
|
|
public function setValue($value) |
182
|
|
|
{ |
183
|
|
|
$this->value = $value; |
184
|
|
|
|
185
|
|
|
return $this; |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* @return bool |
190
|
|
|
*/ |
191
|
|
|
public function getCollapsed() |
192
|
|
|
{ |
193
|
|
|
return $this->collapsed; |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* @param bool $collapsed |
198
|
|
|
* |
199
|
|
|
* @return $this |
200
|
|
|
*/ |
201
|
|
|
public function setCollapsed($collapsed = false) |
202
|
|
|
{ |
203
|
|
|
if ($this->max_depth > 1) { |
204
|
|
|
$this->collapsed = $collapsed; |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
return $this; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
/** |
211
|
|
|
* @return string |
212
|
|
|
*/ |
213
|
|
|
public function getParentField() |
214
|
|
|
{ |
215
|
|
|
return $this->parentField; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
/** |
219
|
|
|
* @param string $parentField |
220
|
|
|
* |
221
|
|
|
* @return $this |
222
|
|
|
*/ |
223
|
|
|
public function setParentField($parentField) |
224
|
|
|
{ |
225
|
|
|
$this->parentField = $parentField; |
226
|
|
|
|
227
|
|
|
return $this; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* @return array|\Illuminate\Contracts\Translation\Translator|null|string |
232
|
|
|
*/ |
233
|
|
|
public function getNewEntryButtonText() |
234
|
|
|
{ |
235
|
|
|
if (is_null($this->newEntryButtonText)) { |
236
|
|
|
$this->newEntryButtonText = trans('sleeping_owl::lang.button.new-entry'); |
|
|
|
|
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
return $this->newEntryButtonText; |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
/** |
243
|
|
|
* @param string $newEntryButtonText |
244
|
|
|
* |
245
|
|
|
* @return $this |
246
|
|
|
*/ |
247
|
|
|
public function setNewEntryButtonText($newEntryButtonText) |
248
|
|
|
{ |
249
|
|
|
$this->newEntryButtonText = $newEntryButtonText; |
250
|
|
|
|
251
|
|
|
return $this; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
/** |
255
|
|
|
* @return string |
256
|
|
|
*/ |
257
|
|
|
public function getOrderField() |
258
|
|
|
{ |
259
|
|
|
return $this->orderField; |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
/** |
263
|
|
|
* @param string $orderField |
264
|
|
|
* |
265
|
|
|
* @return $this |
266
|
|
|
*/ |
267
|
|
|
public function setOrderField($orderField) |
268
|
|
|
{ |
269
|
|
|
$this->orderField = $orderField; |
270
|
|
|
|
271
|
|
|
return $this; |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
/** |
275
|
|
|
* @return null|string |
276
|
|
|
*/ |
277
|
|
|
public function getRootParentId() |
278
|
|
|
{ |
279
|
|
|
return $this->rootParentId; |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
/** |
283
|
|
|
* @param null|string $rootParentId |
284
|
|
|
* |
285
|
|
|
* @return $this |
286
|
|
|
*/ |
287
|
|
|
public function setRootParentId($rootParentId) |
288
|
|
|
{ |
289
|
|
|
$this->rootParentId = $rootParentId; |
290
|
|
|
|
291
|
|
|
return $this; |
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
/** |
295
|
|
|
* @return array |
296
|
|
|
*/ |
297
|
|
|
public function getParameters() |
298
|
|
|
{ |
299
|
|
|
return $this->parameters; |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* @param array $parameters |
304
|
|
|
* |
305
|
|
|
* @return $this |
306
|
|
|
*/ |
307
|
|
|
public function setParameters($parameters) |
308
|
|
|
{ |
309
|
|
|
$this->parameters = $parameters; |
310
|
|
|
|
311
|
|
|
return $this; |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
/** |
315
|
|
|
* @param string $key |
316
|
|
|
* @param mixed $value |
317
|
|
|
* |
318
|
|
|
* @return $this |
319
|
|
|
*/ |
320
|
|
|
public function setParameter($key, $value) |
321
|
|
|
{ |
322
|
|
|
$this->parameters[$key] = $value; |
323
|
|
|
|
324
|
|
|
return $this; |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* @return bool |
329
|
|
|
*/ |
330
|
|
|
public function isReorderable() |
331
|
|
|
{ |
332
|
|
|
return $this->reorderable; |
333
|
|
|
} |
334
|
|
|
|
335
|
|
|
/** |
336
|
|
|
* @param bool $reorderable |
337
|
|
|
* |
338
|
|
|
* @return $this |
339
|
|
|
*/ |
340
|
|
|
public function setReorderable($reorderable) |
341
|
|
|
{ |
342
|
|
|
$this->reorderable = (bool) $reorderable; |
343
|
|
|
|
344
|
|
|
return $this; |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
/** |
348
|
|
|
* @return array |
349
|
|
|
* @throws \Exception |
350
|
|
|
*/ |
351
|
|
|
public function toArray() |
352
|
|
|
{ |
353
|
|
|
$model = $this->getModelConfiguration(); |
354
|
|
|
$this->setHtmlAttribute('class', 'dd nestable'); |
355
|
|
|
|
356
|
|
|
return parent::toArray() + [ |
357
|
|
|
'items' => $this->getRepository()->getTree($this->getCollection()), |
358
|
|
|
'reorderable' => $this->isReorderable(), |
359
|
|
|
'url' => $model->getDisplayUrl(), |
360
|
|
|
'value' => $this->getValue(), |
361
|
|
|
'collapsed' => $this->getCollapsed(), |
362
|
|
|
'creatable' => $model->isCreatable(), |
363
|
|
|
'createUrl' => $model->getCreateUrl($this->getParameters() + Request::all()), |
364
|
|
|
'controls' => [$this->getColumns()->getControlColumn()], |
365
|
|
|
'newEntryButtonText' => $this->getNewEntryButtonText(), |
366
|
|
|
'max_depth' => $this->getMaxDepth(), |
367
|
|
|
'card_class' => $this->getCardClass(), |
368
|
|
|
]; |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
/** |
372
|
|
|
* @return Collection |
373
|
|
|
* @throws \Exception |
374
|
|
|
*/ |
375
|
|
|
public function getCollection() |
376
|
|
|
{ |
377
|
|
|
if (! $this->isInitialized()) { |
378
|
|
|
throw new Exception('Display is not initialized'); |
379
|
|
|
} |
380
|
|
|
|
381
|
|
|
if (! is_null($this->collection)) { |
382
|
|
|
return $this->collection; |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
$query = $this->getRepository()->getQuery(); |
386
|
|
|
|
387
|
|
|
$this->modifyQuery($query); |
388
|
|
|
|
389
|
|
|
if (method_exists($query, 'defaultOrder')) { |
390
|
|
|
return $query->defaultOrder()->get(); |
391
|
|
|
} |
392
|
|
|
|
393
|
|
|
return $query->get(); |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
/** |
397
|
|
|
* @param \Illuminate\Database\Eloquent\Builder $query |
398
|
|
|
*/ |
399
|
|
|
protected function modifyQuery(Builder $query) |
400
|
|
|
{ |
401
|
|
|
$this->extensions->modifyQuery($query); |
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
/** |
405
|
|
|
* @return \Illuminate\Foundation\Application|mixed |
406
|
|
|
* @throws \Exception |
407
|
|
|
*/ |
408
|
|
|
protected function makeRepository() |
409
|
|
|
{ |
410
|
|
|
$repository = parent::makeRepository(); |
411
|
|
|
|
412
|
|
|
if (! ($repository instanceof TreeRepositoryInterface)) { |
413
|
|
|
throw new Exception('Repository class must be instanced of [TreeRepositoryInterface]'); |
414
|
|
|
} |
415
|
|
|
|
416
|
|
|
return $repository; |
417
|
|
|
} |
418
|
|
|
} |
419
|
|
|
|
If there is a route defined but the controller class cannot be found there are two options: 1. the controller class needs to be implemented or 2. the route is outdated and can be removed.
If ?FooController? was found and ?BarController? is missing for the following example, either the controller should be implemented or the route should be removed: