Completed
Push — master ( d713cf...03896d )
by Song
03:09
created

MultipleFile::setupScripts()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 61

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 4
nop 1
dl 0
loc 61
rs 8.8509
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Encore\Admin\Form\Field;
4
5
use Encore\Admin\Form\Field;
6
use Illuminate\Support\Arr;
7
use Illuminate\Support\Facades\Validator;
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 (request()->has(static::FILE_SORT_FLAG)) {
67
            return false;
68
        }
69
70
        if ($this->validator) {
71
            return $this->validator->call($this, $input);
72
        }
73
74
        $attributes = [];
75
76
        if (!$fieldRules = $this->getRules()) {
77
            return false;
78
        }
79
80
        $attributes[$this->column] = $this->label;
81
82
        list($rules, $input) = $this->hydrateFiles(array_get($input, $this->column, []));
0 ignored issues
show
Bug introduced by
It seems like $this->column can also be of type array; however, array_get() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
Deprecated Code introduced by
The function array_get() has been deprecated with message: Arr::get() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
83
84
        return Validator::make($input, $rules, $this->validationMessages, $attributes);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return \Illuminate\Suppo...Messages, $attributes); (Illuminate\Contracts\Validation\Validator) is incompatible with the return type of the parent method Encore\Admin\Form\Field::getValidator of type boolean|Illuminate\Support\Facades\Validator.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
85
    }
86
87
    /**
88
     * Hydrate the files array.
89
     *
90
     * @param array $value
91
     *
92
     * @return array
93
     */
94
    protected function hydrateFiles(array $value)
95
    {
96
        if (empty($value)) {
97
            return [[$this->column => $this->getRules()], []];
98
        }
99
100
        $rules = $input = [];
101
102
        foreach ($value as $key => $file) {
103
            $rules[$this->column.$key] = $this->getRules();
104
            $input[$this->column.$key] = $file;
105
        }
106
107
        return [$rules, $input];
108
    }
109
110
    /**
111
     * Sort files.
112
     *
113
     * @param string $order
114
     * @return array
115
     */
116
    protected function sortFiles($order)
117
    {
118
        $order = explode(',', $order);
119
120
        $new      = [];
121
        $original = $this->original();
122
123
        foreach ($order as $item) {
124
            $new[] = Arr::get($original, $item);
125
        }
126
127
        return $new;
128
    }
129
130
    /**
131
     * Prepare for saving.
132
     *
133
     * @param UploadedFile|array $files
134
     *
135
     * @return mixed|string
136
     */
137
    public function prepare($files)
138
    {
139
        if (request()->has(static::FILE_DELETE_FLAG)) {
140
            return $this->destroy(request(static::FILE_DELETE_FLAG));
141
        }
142
143
        if (is_string($files) && request()->has(static::FILE_SORT_FLAG)) {
144
            return $this->sortFiles($files);
145
        }
146
147
        $targets = array_map([$this, 'prepareForeach'], $files);
148
149
        return array_merge($this->original(), $targets);
150
    }
151
152
    /**
153
     * @return array|mixed
154
     */
155
    public function original()
156
    {
157
        if (empty($this->original)) {
158
            return [];
159
        }
160
161
        return $this->original;
162
    }
163
164
    /**
165
     * Prepare for each file.
166
     *
167
     * @param UploadedFile $file
168
     *
169
     * @return mixed|string
170
     */
171 View Code Duplication
    protected function prepareForeach(UploadedFile $file = null)
0 ignored issues
show
Duplication introduced by
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...
172
    {
173
        $this->name = $this->getStoreName($file);
0 ignored issues
show
Bug introduced by
It seems like $file defined by parameter $file on line 171 can be null; however, Encore\Admin\Form\Field\...adField::getStoreName() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
Documentation Bug introduced by
It seems like $this->getStoreName($file) of type string is incompatible with the declared type null of property $name.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
174
175
        return tap($this->upload($file), function () {
0 ignored issues
show
Bug introduced by
It seems like $file defined by parameter $file on line 171 can be null; however, Encore\Admin\Form\Field\UploadField::upload() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
176
            $this->name = null;
177
        });
178
    }
179
180
    /**
181
     * Preview html for file-upload plugin.
182
     *
183
     * @return array
184
     */
185
    protected function preview()
186
    {
187
        $files = $this->value ?: [];
188
189
        return array_map([$this, 'objectUrl'], $files);
190
    }
191
192
    /**
193
     * Initialize the caption.
194
     *
195
     * @param array $caption
196
     *
197
     * @return string
198
     */
199
    protected function initialCaption($caption)
200
    {
201
        if (empty($caption)) {
202
            return '';
203
        }
204
205
        $caption = array_map('basename', $caption);
206
207
        return implode(',', $caption);
208
    }
209
210
    /**
211
     * @return array
212
     */
213
    protected function initialPreviewConfig()
214
    {
215
        $files = $this->value ?: [];
216
217
        $config = [];
218
219
        foreach ($files as $index => $file) {
220
            $config[] = [
221
                'caption' => basename($file),
222
                'key'     => $index,
223
            ];
224
        }
225
226
        return $config;
227
    }
228
229
    /**
230
     * Allow to sort files.
231
     *
232
     * @return $this
233
     */
234
    public function sortable()
235
    {
236
        $this->fileActionSettings['showDrag'] = true;
237
238
        return $this;
239
    }
240
241
    /**
242
     * @param array $options
243
     * @param array $options
244
     */
245
    protected function setupScripts($options)
246
    {
247
        $this->script = <<<EOT
248
$("input{$this->getElementClassSelector()}").fileinput({$options});
249
EOT;
250
251
        if ($this->fileActionSettings['showRemove']) {
252
253
            $text = [
254
                'title'   => trans('admin.delete_confirm'),
255
                'confirm' => trans('admin.confirm'),
256
                'cancel'  => trans('admin.cancel'),
257
            ];
258
259
            $this->script .= <<<EOT
260
$("input{$this->getElementClassSelector()}").on('filebeforedelete', function() {
261
    
262
    return new Promise(function(resolve, reject) {
263
    
264
        var remove = resolve;
265
    
266
        swal({
267
            title: "{$text['title']}",
268
            type: "warning",
269
            showCancelButton: true,
270
            confirmButtonColor: "#DD6B55",
271
            confirmButtonText: "{$text['confirm']}",
272
            showLoaderOnConfirm: true,
273
            cancelButtonText: "{$text['cancel']}",
274
            preConfirm: function() {
275
                return new Promise(function(resolve) {
276
                    resolve(remove());
277
                });
278
            }
279
        });
280
    });
281
});
282
EOT;
283
        }
284
285
        if ($this->fileActionSettings['showDrag']) {
286
287
            $this->addVariables([
288
                'sortable'  => true,
289
                'sort_flag' => static::FILE_SORT_FLAG,
290
            ]);
291
292
            $this->script .= <<<EOT
293
$("input{$this->getElementClassSelector()}").on('filesorted', function(event, params) {
294
    
295
    var order = [];
296
    
297
    params.stack.forEach(function (item) {
298
        order.push(item.key);
299
    });
300
    
301
    $("input{$this->getElementClassSelector()}_sort").val(order);
302
});
303
EOT;
304
        }
305
    }
306
307
    /**
308
     * Render file upload field.
309
     *
310
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
311
     */
312
    public function render()
313
    {
314
        $this->attribute('multiple', true);
315
316
        $this->setupDefaultOptions();
317
318
        if (!empty($this->value)) {
319
            $this->options(['initialPreview' => $this->preview()]);
320
            $this->setupPreviewOptions();
321
        }
322
323
        $options = json_encode($this->options);
324
325
        $this->setupScripts($options);
326
327
        return parent::render();
0 ignored issues
show
Bug Compatibility introduced by
The expression parent::render(); of type string|Illuminate\View\V...\Contracts\View\Factory adds the type Illuminate\Contracts\View\Factory to the return on line 327 which is incompatible with the return type declared by the interface Illuminate\Contracts\Support\Renderable::render of type string.
Loading history...
328
    }
329
330
    /**
331
     * Destroy original files.
332
     *
333
     * @return string.
0 ignored issues
show
Documentation introduced by
The doc-type string. could not be parsed: Unknown type name "string." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
334
     */
335
    public function destroy($key)
336
    {
337
        $files = $this->original ?: [];
338
339
        $file = array_get($files, $key);
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated with message: Arr::get() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
340
341
        if ($this->storage->exists($file)) {
342
            $this->storage->delete($file);
343
        }
344
345
        unset($files[$key]);
346
347
        return array_values($files);
348
    }
349
}
350