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\Form\Field; |
||
4 | |||
5 | use Encore\Admin\Form; |
||
6 | use Encore\Admin\Form\Field; |
||
7 | use Illuminate\Support\Arr; |
||
8 | use Symfony\Component\HttpFoundation\File\UploadedFile; |
||
9 | |||
10 | class MultipleFile extends Field |
||
11 | { |
||
12 | use UploadField; |
||
13 | |||
14 | /** |
||
15 | * Css. |
||
16 | * |
||
17 | * @var array |
||
18 | */ |
||
19 | protected static $css = [ |
||
20 | '/vendor/laravel-admin/bootstrap-fileinput/css/fileinput.min.css?v=4.5.2', |
||
21 | ]; |
||
22 | |||
23 | /** |
||
24 | * Js. |
||
25 | * |
||
26 | * @var array |
||
27 | */ |
||
28 | protected static $js = [ |
||
29 | '/vendor/laravel-admin/bootstrap-fileinput/js/plugins/canvas-to-blob.min.js', |
||
30 | '/vendor/laravel-admin/bootstrap-fileinput/js/fileinput.min.js?v=4.5.2', |
||
31 | '/vendor/laravel-admin/bootstrap-fileinput/js/plugins/sortable.min.js?v=4.5.2', |
||
32 | ]; |
||
33 | |||
34 | /** |
||
35 | * Create a new File instance. |
||
36 | * |
||
37 | * @param string $column |
||
38 | * @param array $arguments |
||
39 | */ |
||
40 | public function __construct($column, $arguments = []) |
||
41 | { |
||
42 | $this->initStorage(); |
||
43 | |||
44 | parent::__construct($column, $arguments); |
||
45 | } |
||
46 | |||
47 | /** |
||
48 | * Default directory for file to upload. |
||
49 | * |
||
50 | * @return mixed |
||
51 | */ |
||
52 | public function defaultDirectory() |
||
53 | { |
||
54 | return config('admin.upload.directory.file'); |
||
55 | } |
||
56 | |||
57 | /** |
||
58 | * {@inheritdoc} |
||
59 | */ |
||
60 | public function getValidator(array $input) |
||
61 | { |
||
62 | if (request()->has(static::FILE_DELETE_FLAG)) { |
||
63 | return false; |
||
64 | } |
||
65 | |||
66 | if ($this->validator) { |
||
67 | return $this->validator->call($this, $input); |
||
68 | } |
||
69 | |||
70 | $attributes = []; |
||
71 | |||
72 | if (!$fieldRules = $this->getRules()) { |
||
73 | return false; |
||
74 | } |
||
75 | |||
76 | $attributes[$this->column] = $this->label; |
||
77 | |||
78 | list($rules, $input) = $this->hydrateFiles(Arr::get($input, $this->column, [])); |
||
79 | |||
80 | return \validator($input, $rules, $this->getValidationMessages(), $attributes); |
||
81 | } |
||
82 | |||
83 | /** |
||
84 | * Hydrate the files array. |
||
85 | * |
||
86 | * @param array $value |
||
87 | * |
||
88 | * @return array |
||
89 | */ |
||
90 | protected function hydrateFiles(array $value) |
||
91 | { |
||
92 | if (empty($value)) { |
||
93 | return [[$this->column => $this->getRules()], []]; |
||
94 | } |
||
95 | |||
96 | $rules = $input = []; |
||
97 | |||
98 | foreach ($value as $key => $file) { |
||
99 | $rules[$this->column.$key] = $this->getRules(); |
||
100 | $input[$this->column.$key] = $file; |
||
101 | } |
||
102 | |||
103 | return [$rules, $input]; |
||
104 | } |
||
105 | |||
106 | /** |
||
107 | * Sort files. |
||
108 | * |
||
109 | * @param string $order |
||
110 | * |
||
111 | * @return array |
||
112 | */ |
||
113 | protected function sortFiles($order) |
||
114 | { |
||
115 | $order = explode(',', $order); |
||
116 | |||
117 | $new = []; |
||
118 | $original = $this->original(); |
||
119 | |||
120 | foreach ($order as $item) { |
||
121 | $new[] = Arr::get($original, $item); |
||
122 | } |
||
123 | |||
124 | return $new; |
||
125 | } |
||
126 | |||
127 | /** |
||
128 | * Prepare for saving. |
||
129 | * |
||
130 | * @param UploadedFile|array $files |
||
131 | * |
||
132 | * @return mixed|string |
||
133 | */ |
||
134 | public function prepare($files) |
||
135 | { |
||
136 | if (request()->has(static::FILE_DELETE_FLAG)) { |
||
137 | if ($this->pathColumn) { |
||
138 | return $this->destroyFromHasMany(request(static::FILE_DELETE_FLAG)); |
||
139 | } |
||
140 | |||
141 | return $this->destroy(request(static::FILE_DELETE_FLAG)); |
||
142 | } |
||
143 | |||
144 | if (is_string($files) && request()->has(static::FILE_SORT_FLAG)) { |
||
145 | return $this->sortFiles($files); |
||
146 | } |
||
147 | |||
148 | $targets = array_map([$this, 'prepareForeach'], $files); |
||
149 | |||
150 | // for create or update |
||
151 | if ($this->pathColumn) { |
||
152 | $targets = array_map(function ($target) { |
||
153 | return [$this->pathColumn => $target]; |
||
154 | }, $targets); |
||
155 | } |
||
156 | |||
157 | return array_merge($this->original(), $targets); |
||
158 | } |
||
159 | |||
160 | /** |
||
161 | * @return array|mixed |
||
162 | */ |
||
163 | public function original() |
||
164 | { |
||
165 | if (empty($this->original)) { |
||
166 | return []; |
||
167 | } |
||
168 | |||
169 | return $this->original; |
||
170 | } |
||
171 | |||
172 | /** |
||
173 | * Prepare for each file. |
||
174 | * |
||
175 | * @param UploadedFile $file |
||
176 | * |
||
177 | * @return mixed|string |
||
178 | */ |
||
179 | View Code Duplication | protected function prepareForeach(UploadedFile $file = null) |
|
0 ignored issues
–
show
|
|||
180 | { |
||
181 | $this->name = $this->getStoreName($file); |
||
182 | |||
183 | return tap($this->upload($file), function () { |
||
184 | $this->name = null; |
||
185 | }); |
||
186 | } |
||
187 | |||
188 | /** |
||
189 | * Preview html for file-upload plugin. |
||
190 | * |
||
191 | * @return array |
||
192 | */ |
||
193 | protected function preview() |
||
194 | { |
||
195 | $files = $this->value ?: []; |
||
196 | |||
197 | return array_values(array_map([$this, 'objectUrl'], $files)); |
||
198 | } |
||
199 | |||
200 | /** |
||
201 | * Initialize the caption. |
||
202 | * |
||
203 | * @param array $caption |
||
204 | * |
||
205 | * @return string |
||
206 | */ |
||
207 | protected function initialCaption($caption) |
||
208 | { |
||
209 | if (empty($caption)) { |
||
210 | return ''; |
||
211 | } |
||
212 | |||
213 | $caption = array_map('basename', $caption); |
||
214 | |||
215 | return implode(',', $caption); |
||
216 | } |
||
217 | |||
218 | /** |
||
219 | * @return array |
||
220 | */ |
||
221 | protected function initialPreviewConfig() |
||
222 | { |
||
223 | $files = $this->value ?: []; |
||
224 | |||
225 | $config = []; |
||
226 | |||
227 | foreach ($files as $index => $file) { |
||
228 | if (is_array($file) && $this->pathColumn) { |
||
229 | $index = Arr::get($file, $this->getRelatedKeyName(), $index); |
||
230 | $file = Arr::get($file, $this->pathColumn); |
||
231 | } |
||
232 | |||
233 | $preview = array_merge([ |
||
234 | 'caption' => basename($file), |
||
235 | 'key' => $index, |
||
236 | ], $this->guessPreviewType($file)); |
||
237 | |||
238 | $config[] = $preview; |
||
239 | } |
||
240 | |||
241 | return $config; |
||
242 | } |
||
243 | |||
244 | /** |
||
245 | * Get related model key name. |
||
246 | * |
||
247 | * @return string |
||
248 | */ |
||
249 | protected function getRelatedKeyName() |
||
250 | { |
||
251 | if (is_null($this->form)) { |
||
252 | return; |
||
253 | } |
||
254 | |||
255 | return $this->form->model()->{$this->column}()->getRelated()->getKeyName(); |
||
256 | } |
||
257 | |||
258 | /** |
||
259 | * Allow to sort files. |
||
260 | * |
||
261 | * @return $this |
||
262 | */ |
||
263 | public function sortable() |
||
264 | { |
||
265 | $this->fileActionSettings['showDrag'] = true; |
||
266 | |||
267 | return $this; |
||
268 | } |
||
269 | |||
270 | /** |
||
271 | * @param string $options |
||
272 | */ |
||
273 | protected function setupScripts($options) |
||
274 | { |
||
275 | $this->script = <<<EOT |
||
276 | $("input{$this->getElementClassSelector()}").fileinput({$options}); |
||
277 | EOT; |
||
278 | |||
279 | View Code Duplication | if ($this->fileActionSettings['showRemove']) { |
|
280 | $text = [ |
||
281 | 'title' => trans('admin.delete_confirm'), |
||
282 | 'confirm' => trans('admin.confirm'), |
||
283 | 'cancel' => trans('admin.cancel'), |
||
284 | ]; |
||
285 | |||
286 | $this->script .= <<<EOT |
||
287 | $("input{$this->getElementClassSelector()}").on('filebeforedelete', function() { |
||
288 | |||
289 | return new Promise(function(resolve, reject) { |
||
290 | |||
291 | var remove = resolve; |
||
292 | |||
293 | swal({ |
||
294 | title: "{$text['title']}", |
||
295 | type: "warning", |
||
296 | showCancelButton: true, |
||
297 | confirmButtonColor: "#DD6B55", |
||
298 | confirmButtonText: "{$text['confirm']}", |
||
299 | showLoaderOnConfirm: true, |
||
300 | cancelButtonText: "{$text['cancel']}", |
||
301 | preConfirm: function() { |
||
302 | return new Promise(function(resolve) { |
||
303 | resolve(remove()); |
||
304 | }); |
||
305 | } |
||
306 | }); |
||
307 | }); |
||
308 | }); |
||
309 | EOT; |
||
310 | } |
||
311 | |||
312 | if ($this->fileActionSettings['showDrag']) { |
||
313 | $this->addVariables([ |
||
314 | 'sortable' => true, |
||
315 | 'sort_flag' => static::FILE_SORT_FLAG, |
||
316 | ]); |
||
317 | |||
318 | $this->script .= <<<EOT |
||
319 | $("input{$this->getElementClassSelector()}").on('filesorted', function(event, params) { |
||
320 | |||
321 | var order = []; |
||
322 | |||
323 | params.stack.forEach(function (item) { |
||
324 | order.push(item.key); |
||
325 | }); |
||
326 | |||
327 | $("input{$this->getElementClassSelector()}_sort").val(order); |
||
328 | }); |
||
329 | EOT; |
||
330 | } |
||
331 | } |
||
332 | |||
333 | /** |
||
334 | * Render file upload field. |
||
335 | * |
||
336 | * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View |
||
337 | */ |
||
338 | public function render() |
||
339 | { |
||
340 | $this->attribute('multiple', true); |
||
341 | |||
342 | $this->setupDefaultOptions(); |
||
343 | |||
344 | if (!empty($this->value)) { |
||
345 | $this->options(['initialPreview' => $this->preview()]); |
||
346 | $this->setupPreviewOptions(); |
||
347 | } |
||
348 | |||
349 | $options = json_encode($this->options); |
||
350 | |||
351 | $this->setupScripts($options); |
||
352 | |||
353 | return parent::render(); |
||
354 | } |
||
355 | |||
356 | /** |
||
357 | * Destroy original files. |
||
358 | * |
||
359 | * @param string $key |
||
360 | * |
||
361 | * @return array |
||
362 | */ |
||
363 | public function destroy($key) |
||
364 | { |
||
365 | $files = $this->original ?: []; |
||
366 | |||
367 | $path = Arr::get($files, $key); |
||
368 | |||
369 | if (!$this->retainable && $this->storage->exists($path)) { |
||
370 | $this->storage->delete($path); |
||
371 | } |
||
372 | |||
373 | unset($files[$key]); |
||
374 | |||
375 | return $files; |
||
376 | } |
||
377 | |||
378 | /** |
||
379 | * Destroy original files from hasmany related model. |
||
380 | * |
||
381 | * @param int $key |
||
382 | * |
||
383 | * @return array |
||
384 | */ |
||
385 | public function destroyFromHasMany($key) |
||
386 | { |
||
387 | $files = collect($this->original ?: [])->keyBy($this->getRelatedKeyName())->toArray(); |
||
388 | |||
389 | $path = Arr::get($files, "{$key}.{$this->pathColumn}"); |
||
390 | |||
391 | if (!$this->retainable && $this->storage->exists($path)) { |
||
392 | $this->storage->delete($path); |
||
393 | } |
||
394 | |||
395 | $files[$key][Form::REMOVE_FLAG_NAME] = 1; |
||
396 | |||
397 | return $files; |
||
398 | } |
||
399 | } |
||
400 |
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.