Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Passed
Pull Request — main (#4988)
by Pedro
38:52 queued 24:06
created

Uploader::modelInstance()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 1
c 1
b 0
f 1
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
namespace Backpack\CRUD\app\Library\CrudPanel\Uploads\Uploaders;
4
5
use Backpack\CRUD\app\Library\CrudPanel\CrudPanelFacade as CRUD;
6
use Backpack\CRUD\app\Library\CrudPanel\Uploads\Interfaces\UploaderInterface;
7
use Backpack\CRUD\app\Library\CrudPanel\Uploads\Traits\HasCrudObjectType;
8
use Backpack\CRUD\app\Library\CrudPanel\Uploads\Traits\HasName;
9
use Closure;
10
use Illuminate\Database\Eloquent\Model;
11
use Illuminate\Http\UploadedFile;
12
use Illuminate\Support\Facades\Storage;
13
use Illuminate\Support\Str;
14
15
abstract class Uploader implements UploaderInterface
16
{
17
    use HasCrudObjectType, HasName;
18
19
    /**
20
     * Indicates the uploaded file should be deleted when entry is deleted.
21
     *
22
     * @var bool
23
     */
24
    public $deleteWhenEntryIsDeleted = true;
25
26
    /**
27
     * Indicates if this uploader instance is inside a repeatable container.
28
     *
29
     * @var bool
30
     */
31
    public $isRepeatable = false;
32
33
    /**
34
     * When inside a repeatable container, indicates the container name.
35
     *
36
     * @var string|null
37
     */
38
    public $repeatableContainerName = null;
39
40
    /**
41
     * Developer provided filename.
42
     *
43
     * @var null|string|Closure
44
     */
45
    public $fileName = null;
46
47
    /**
48
     * The disk where upload will be stored. By default `public`.
49
     *
50
     * @var string
51
     */
52
    public $disk = 'public';
53
54
    /**
55
     * Indicates if the upload handles multiple files.
56
     *
57
     * @var bool
58
     */
59
    public $isMultiple = false;
60
61
    /**
62
     * The class of the entry where uploads will be attached to.
63
     *
64
     * @var string
65
     */
66
    public $entryClass;
67
68
    /**
69
     * The path inside the disk to store the uploads.
70
     *
71
     * @var string
72
     */
73
    public $path = '';
74
75
    /**
76
     * Should the url to the object be a temporary one (eg: s3).
77
     *
78
     * @var bool
79
     */
80
    public $temporary = false;
81
82
    /**
83
     * When using temporary urls, defines the time that the url
84
     * should be available in minutes.
85
     *
86
     * By default 1 minute
87
     *
88
     * @var int
89
     */
90
    public $expiration = 1;
91
92
    /**
93
     * Indicates if the upload is relative to a relationship field/column.
94
     *
95
     * @var bool
96
     */
97
    public $isRelationship = false;
98
99
    public function __construct(array $crudObject, array $configuration)
100
    {
101
        $this->name = $crudObject['name'];
102
        $this->disk = $configuration['disk'] ?? $crudObject['disk'] ?? $this->disk;
103
        $this->temporary = $configuration['temporary'] ?? $this->temporary;
104
        $this->expiration = $configuration['expiration'] ?? $this->expiration;
105
        $this->entryClass = $crudObject['entryClass'];
106
        $this->path = $configuration['path'] ?? $crudObject['prefix'] ?? $this->path;
107
        $this->path = empty($this->path) ? $this->path : Str::of($this->path)->finish('/')->value;
108
        $this->crudObjectType = $crudObject['crudObjectType'];
109
        $this->fileName = $configuration['fileName'] ?? $this->fileName;
110
        $this->deleteWhenEntryIsDeleted = $configuration['whenDelete'] ?? $this->deleteWhenEntryIsDeleted;
111
    }
112
113
    /**
114
     * An abstract function that all uploaders must implement with the saving process.
115
     *
116
     * @param  Model  $entry
117
     * @param  mixed  $values
118
     * @return mixed
119
     */
120
    abstract public function save(Model $entry, $values = null);
121
122
    /**
123
     * The function called in the saving event that starts the upload process.
124
     *
125
     * @param  Model  $entry
126
     * @return Model
127
     */
128
    public function processFileUpload(Model $entry)
129
    {
130
        $entry->{$this->name} = $this->save($entry);
131
132
        return $entry;
133
    }
134
135
    /**
136
     * Return the uploader name.
137
     *
138
     * @return string
139
     */
140
    public function getName()
141
    {
142
        return $this->name;
143
    }
144
145
    /**
146
     * Return the uploader disk.
147
     *
148
     * @return string
149
     */
150
    public function getDisk()
151
    {
152
        return $this->disk;
153
    }
154
155
    /**
156
     * Return the uploader path.
157
     *
158
     * @return string
159
     */
160
    public function getPath()
161
    {
162
        return $this->path;
163
    }
164
165
    /**
166
     * Return the uploader temporary option.
167
     *
168
     * @return bool
169
     */
170
    public function getTemporary()
171
    {
172
        return $this->temporary;
173
    }
174
175
    /**
176
     * Return the uploader expiration time in minutes.
177
     *
178
     * @return int
179
     */
180
    public function getExpiration()
181
    {
182
        return $this->expiration;
183
    }
184
185
    /**
186
     * The function called in the retrieved event that handles the display of uploaded values.
187
     *
188
     * @param  Model  $entry
189
     * @return Model
190
     */
191
    public function retrieveUploadedFile(Model $entry)
192
    {
193
        $value = $entry->{$this->name};
194
195
        if ($this->isMultiple && ! isset($entry->getCasts()[$this->name]) && is_string($value)) {
196
            $entry->{$this->name} = json_decode($value, true);
197
        } else {
198
            $entry->{$this->name} = Str::after($value, $this->path);
199
        }
200
201
        return $entry;
202
    }
203
204
    /**
205
     * The function called in the deleting event to delete the uploaded files upon entry deletion.
206
     *
207
     * @param  Model  $entry
208
     * @return void
209
     */
210
    public function deleteUploadedFile(Model $entry)
211
    {
212
        $values = $entry->{$this->name};
213
214
        if ($this->isMultiple) {
215
            if (! isset($entry->getCasts()[$this->name]) && is_string($values)) {
216
                $values = json_decode($values, true);
217
            }
218
        } else {
219
            $values = (array) Str::after($values, $this->path);
220
        }
221
222
        foreach ($values as $value) {
223
            Storage::disk($this->disk)->delete($this->path.$value);
224
        }
225
    }
226
227
    /**
228
     * Build an uploader instance.
229
     *
230
     * @param  array  $crudObject
231
     * @param  array  $definition
232
     * @return self
233
     */
234
    public static function for(array $crudObject, array $definition)
235
    {
236
        return new static($crudObject, $definition);
237
    }
238
239
    /**
240
     * Set multiple attribute to true in the uploader.
241
     *
242
     * @return self
243
     */
244
    protected function multiple()
245
    {
246
        $this->isMultiple = true;
247
248
        return $this;
249
    }
250
251
    /**
252
     * Set relationship attribute in uploader.
253
     * When true, it also removes the repeatable in case the relationship is handled
254
     * by repeatable interface.
255
     * This is because the uploads are only repeatable on the "main model", but they represent
256
     * one entry per row. (not repeatable in the "relationship model").
257
     *
258
     * @param  bool  $isRelationship
259
     * @return self
260
     */
261
    public function relationship(bool $isRelationship): self
262
    {
263
        if ($isRelationship) {
264
            $this->isRepeatable = false;
265
        }
266
        $this->isRelationship = $isRelationship;
267
268
        return $this;
269
    }
270
271
    /**
272
     * Set the repeatable attribute to true in the uploader and the
273
     * corresponding container name.
274
     *
275
     * @param  string  $repeatableContainerName
276
     * @return self
277
     */
278
    public function repeats(string $repeatableContainerName): self
279
    {
280
        $this->isRepeatable = true;
281
282
        $this->repeatableContainerName = $repeatableContainerName;
283
284
        return $this;
285
    }
286
287
    /**
288
     * Repeatable items send _order_ parameter in the request.
289
     * This olds the information for uploads inside repeatable containers.
290
     *
291
     * @return array
292
     */
293
    protected function getFileOrderFromRequest()
294
    {
295
        $items = CRUD::getRequest()->input('_order_'.$this->repeatableContainerName) ?? [];
0 ignored issues
show
Bug introduced by
The method getRequest() does not exist on Backpack\CRUD\app\Librar...udPanel\CrudPanelFacade. Since you implemented __callStatic, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

295
        $items = CRUD::/** @scrutinizer ignore-call */ getRequest()->input('_order_'.$this->repeatableContainerName) ?? [];
Loading history...
296
297
        array_walk($items, function (&$key, $value) {
0 ignored issues
show
Unused Code introduced by
The parameter $value is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

297
        array_walk($items, function (&$key, /** @scrutinizer ignore-unused */ $value) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
298
            $requestValue = $key[$this->name] ?? null;
299
            $key = $this->isMultiple ? (is_string($requestValue) ? explode(',', $requestValue) : $requestValue) : $requestValue;
300
        });
301
302
        return $items;
303
    }
304
305
    /**
306
     * Return a new instance of the entry class for the uploader.
307
     *
308
     * @return Model
309
     */
310
    protected function modelInstance()
311
    {
312
        return new $this->entryClass;
313
    }
314
315
    /**
316
     * Return the uploader stored values when in a repeatable container.
317
     *
318
     * @param  Model  $entry
319
     * @return array
320
     */
321
    protected function getPreviousRepeatableValues(Model $entry)
322
    {
323
        $previousValues = json_decode($entry->getOriginal($this->repeatableContainerName), true);
0 ignored issues
show
Bug introduced by
It seems like $entry->getOriginal($thi...epeatableContainerName) can also be of type array; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

323
        $previousValues = json_decode(/** @scrutinizer ignore-type */ $entry->getOriginal($this->repeatableContainerName), true);
Loading history...
324
        if (! empty($previousValues)) {
325
            $previousValues = array_column($previousValues, $this->name);
326
        }
327
328
        return $previousValues ?? [];
329
    }
330
331
    /**
332
     * Return the file extension.
333
     *
334
     * @param  mixed  $file
335
     * @return string
336
     */
337
    protected function getExtensionFromFile($file)
338
    {
339
        return is_a($file, UploadedFile::class, true) ? $file->extension() : Str::after(mime_content_type($file), '/');
340
    }
341
342
    /**
343
     * Return the file name built by Backpack or by the developer in `fileName` configuration.
344
     *
345
     * @param  mixed  $file
346
     * @return string
347
     */
348
    protected function getFileName($file)
349
    {
350
        if (is_file($file)) {
351
            return Str::of($this->fileNameFrom($file) ?? Str::of($file->getClientOriginalName())->beforeLast('.')->slug()->append('-'.Str::random(4)));
352
        }
353
354
        return Str::of($this->fileNameFrom($file) ?? Str::random(40));
355
    }
356
357
    /**
358
     * Return the complete filename and extension.
359
     *
360
     * @param  mixed  $file
361
     * @return string
362
     */
363
    protected function getFileNameWithExtension($file)
364
    {
365
        if (is_file($file)) {
366
            return $this->getFileName($file).'.'.$this->getExtensionFromFile($file);
367
        }
368
369
        return Str::of($this->fileNameFrom($file) ?? Str::random(40)).'.'.$this->getExtensionFromFile($file);
370
    }
371
372
    /**
373
     * Allow developer to override the default Backpack fileName.
374
     *
375
     * @param  mixed  $file
376
     * @return string|null
377
     */
378
    private function fileNameFrom($file)
379
    {
380
        if (is_callable($this->fileName)) {
381
            return ($this->fileName)($file, $this);
382
        }
383
384
        return $this->fileName;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->fileName also could return the type Closure which is incompatible with the documented return type null|string.
Loading history...
385
    }
386
}
387