Completed
Push — master ( 9b49f7...0d2bac )
by Song
03:00 queued 10s
created

src/Form/Field/MultipleFile.php (1 issue)

Upgrade to new PHP Analysis Engine

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
This method seems to be duplicated in your project.

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.

Loading history...
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