1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Sco\Admin\Form\Elements; |
4
|
|
|
|
5
|
|
|
use Illuminate\Database\Eloquent\Model; |
6
|
|
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany; |
7
|
|
|
use InvalidArgumentException; |
8
|
|
|
use Sco\Admin\Contracts\RepositoryInterface; |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
* Class Tree |
12
|
|
|
* |
13
|
|
|
* @package Sco\Admin\Form\Elements |
14
|
|
|
* @see http://element.eleme.io/#/en-US/component/tree |
15
|
|
|
*/ |
16
|
|
|
class Tree extends NamedElement |
17
|
|
|
{ |
18
|
|
|
protected $type = 'tree'; |
19
|
|
|
|
20
|
|
|
protected $defaultValue = []; |
21
|
|
|
|
22
|
|
|
protected $rootNodeValue = ''; |
23
|
|
|
|
24
|
|
|
protected $nodesModelParentAttribute = 'parent_id'; |
25
|
|
|
|
26
|
|
|
protected $nodesModelLabelAttribute; |
27
|
|
|
|
28
|
|
|
protected $nodesModelValueAttribute; |
29
|
|
|
|
30
|
|
|
protected $nodes; |
31
|
|
|
|
32
|
|
|
public function __construct(string $name, string $title, $nodes = null) |
33
|
|
|
{ |
34
|
|
|
parent::__construct($name, $title); |
35
|
|
|
|
36
|
|
|
if ($nodes) { |
37
|
|
|
$this->setNodes($nodes); |
38
|
|
|
} |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
public function getValue() |
42
|
|
|
{ |
43
|
|
|
$value = $this->getValueFromModel(); |
44
|
|
|
if (empty($value)) { |
45
|
|
|
return []; |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
if ($this->isNodesModel() && $this->isRelation()) { |
49
|
|
|
$model = $this->getNodesModel(); |
50
|
|
|
$key = $this->getNodesModelValueAttribute() ?: $model->getKeyName(); |
51
|
|
|
|
52
|
|
|
return $value->pluck($key)->map(function ($item) { |
53
|
|
|
return (string) $item; |
54
|
|
|
}); |
55
|
|
|
} else { |
56
|
|
|
return explode(',', $value); |
57
|
|
|
} |
58
|
|
|
} |
59
|
|
|
|
60
|
|
|
public function save() |
61
|
|
|
{ |
62
|
|
|
if (! ($this->isNodesModel() && $this->isRelation())) { |
63
|
|
|
parent::save(); |
64
|
|
|
} |
65
|
|
|
} |
66
|
|
|
|
67
|
|
View Code Duplication |
public function finishSave() |
|
|
|
|
68
|
|
|
{ |
69
|
|
|
if (! ($this->isNodesModel() && $this->isRelation())) { |
70
|
|
|
return; |
71
|
|
|
} |
72
|
|
|
$attribute = $this->getName(); |
73
|
|
|
$values = $this->getValueFromRequest(); |
74
|
|
|
|
75
|
|
|
$relation = $this->getModel()->{$attribute}(); |
76
|
|
|
if ($relation instanceof BelongsToMany) { |
77
|
|
|
$relation->sync($values); |
|
|
|
|
78
|
|
|
} |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
protected function isNodesModel() |
82
|
|
|
{ |
83
|
|
|
return is_string($this->nodes) || $this->nodes instanceof Model; |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
protected function isRelation() |
87
|
|
|
{ |
88
|
|
|
return method_exists($this->getModel(), $this->getName()); |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
public function getNodes() |
92
|
|
|
{ |
93
|
|
|
if ($this->nodes instanceof \Closure) { |
94
|
|
|
$nodes = ($this->nodes)(); |
95
|
|
|
} elseif (is_string($this->nodes) || $this->nodes instanceof Model) { |
96
|
|
|
$nodes = $this->setNodesFromModel(); |
97
|
|
|
} elseif (is_array($this->nodes)) { |
98
|
|
|
$nodes = $this->nodes; |
99
|
|
|
} else { |
100
|
|
|
throw new InvalidArgumentException( |
101
|
|
|
"The form tree element's nodes must be return array" |
102
|
|
|
); |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
return $nodes; |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* @param $nodes |
110
|
|
|
* @param $parentId |
111
|
|
|
* |
112
|
|
|
* @return \Illuminate\Support\Collection |
113
|
|
|
*/ |
114
|
|
|
public function getNodesTree($nodes, $parentId) |
115
|
|
|
{ |
116
|
|
|
return collect($nodes)->filter(function ($value) use ($parentId) { |
117
|
|
|
if (isset($value['parent_id'])) { |
118
|
|
|
if ($value['parent_id'] == $parentId) { |
119
|
|
|
return true; |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
return false; |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
return true; |
126
|
|
|
})->mapWithKeys(function ($value, $key) use ($nodes) { |
127
|
|
|
$data = [ |
128
|
|
|
'id' => (string) $value['id'], |
129
|
|
|
'label' => $value['label'], |
130
|
|
|
]; |
131
|
|
|
|
132
|
|
|
if (isset($value['parent_id'])) { |
133
|
|
|
$children = $this->getNodesTree($nodes, $value['id']); |
134
|
|
|
if ($children->isNotEmpty()) { |
135
|
|
|
$data['children'] = $children; |
136
|
|
|
} |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
return [ |
140
|
|
|
$key => $data, |
141
|
|
|
]; |
142
|
|
|
})->values(); |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
public function setNodes($nodes) |
146
|
|
|
{ |
147
|
|
|
$this->nodes = $nodes; |
148
|
|
|
|
149
|
|
|
return $this; |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* @return array |
154
|
|
|
*/ |
155
|
|
|
protected function setNodesFromModel() |
156
|
|
|
{ |
157
|
|
|
if (is_null($this->getNodesModelLabelAttribute())) { |
158
|
|
|
throw new InvalidArgumentException( |
159
|
|
|
sprintf( |
160
|
|
|
"Form %s element's nodes must be set model label attribute", |
161
|
|
|
$this->getType() |
162
|
|
|
) |
163
|
|
|
); |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
$model = $this->getNodesModel(); |
167
|
|
|
|
168
|
|
|
$repository = app(RepositoryInterface::class); |
169
|
|
|
$repository->setModel($model); |
170
|
|
|
|
171
|
|
|
$results = $repository->getQuery()->get(); |
172
|
|
|
|
173
|
|
|
$key = $this->getNodesModelValueAttribute() ?: $model->getKeyName(); |
174
|
|
|
|
175
|
|
|
return $results->mapWithKeys(function ($item, $ikey) use ($key) { |
176
|
|
|
$data = [ |
177
|
|
|
'id' => $item[$key], |
178
|
|
|
'label' => $item[$this->getNodesModelLabelAttribute()], |
179
|
|
|
]; |
180
|
|
|
if (isset($item[$this->getNodesModelParentAttribute()])) { |
181
|
|
|
$data['parent_id'] = $item[$this->getNodesModelParentAttribute()]; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
return [ |
185
|
|
|
$ikey => $data, |
186
|
|
|
]; |
187
|
|
|
}); |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* @return Model |
192
|
|
|
*/ |
193
|
|
View Code Duplication |
protected function getNodesModel() |
|
|
|
|
194
|
|
|
{ |
195
|
|
|
$model = $this->nodes; |
196
|
|
|
|
197
|
|
|
if (is_string($model)) { |
198
|
|
|
$model = app($model); |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
if (! ($model instanceof Model)) { |
202
|
|
|
throw new InvalidArgumentException( |
203
|
|
|
sprintf( |
204
|
|
|
"Form tree element's nodes class must be instanced of '%s'.", |
205
|
|
|
Model::class |
206
|
|
|
) |
207
|
|
|
); |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
return $model; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
public function getNodesModelLabelAttribute() |
214
|
|
|
{ |
215
|
|
|
return $this->nodesModelLabelAttribute; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
public function setNodesModelLabelAttribute($value) |
219
|
|
|
{ |
220
|
|
|
$this->nodesModelLabelAttribute = $value; |
221
|
|
|
|
222
|
|
|
return $this; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
public function getNodesModelValueAttribute() |
226
|
|
|
{ |
227
|
|
|
return $this->nodesModelValueAttribute; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
public function setNodesModelValueAttribute($value) |
231
|
|
|
{ |
232
|
|
|
$this->nodesModelValueAttribute = $value; |
233
|
|
|
|
234
|
|
|
return $this; |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
public function getNodesModelParentAttribute() |
238
|
|
|
{ |
239
|
|
|
return $this->nodesModelParentAttribute; |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
public function setNodesModelParentAttribute($value) |
243
|
|
|
{ |
244
|
|
|
$this->nodesModelParentAttribute = $value; |
245
|
|
|
|
246
|
|
|
return $this; |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
public function getRootNodeValue() |
250
|
|
|
{ |
251
|
|
|
return $this->rootNodeValue; |
252
|
|
|
} |
253
|
|
|
|
254
|
|
|
public function setRootNodeValue($value) |
255
|
|
|
{ |
256
|
|
|
$this->rootNodeValue = $value; |
257
|
|
|
|
258
|
|
|
return $this; |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
public function toArray() |
262
|
|
|
{ |
263
|
|
|
return parent::toArray() + [ |
264
|
|
|
'nodes' => $this->getNodesTree( |
265
|
|
|
$this->getNodes(), |
266
|
|
|
$this->getRootNodeValue() |
267
|
|
|
), |
268
|
|
|
]; |
269
|
|
|
} |
270
|
|
|
} |
271
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.