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 (#5715)
by Pedro
41:54 queued 27:10
created

Uploader::storeUploadedFiles()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 10
nc 6
nop 1
dl 0
loc 19
rs 9.6111
c 0
b 0
f 0
1
<?php
2
3
namespace Backpack\CRUD\app\Library\Uploaders;
4
5
use Backpack\CRUD\app\Library\Uploaders\Support\Interfaces\UploaderInterface;
6
use Backpack\CRUD\app\Library\Uploaders\Support\Traits\HandleFileNaming;
7
use Backpack\CRUD\app\Library\Uploaders\Support\Traits\HandleRepeatableUploads;
8
use Illuminate\Database\Eloquent\Model;
9
use Illuminate\Database\Eloquent\SoftDeletes;
10
use Illuminate\Support\Facades\Storage;
11
use Illuminate\Support\Str;
12
13
abstract class Uploader implements UploaderInterface
14
{
15
    use HandleFileNaming;
16
    use HandleRepeatableUploads;
0 ignored issues
show
introduced by
The trait Backpack\CRUD\app\Librar...HandleRepeatableUploads requires some properties which are not provided by Backpack\CRUD\app\Library\Uploaders\Uploader: $model, $pivot, $pivotParent
Loading history...
17
18
    private string $name;
19
20
    private string $disk = 'public';
21
22
    private string $path = '';
23
24
    public bool $handleMultipleFiles = false;
25
26
    private bool $deleteWhenEntryIsDeleted = true;
27
28
    private bool|string $attachedToFakeField = false;
29
30
    /**
31
     * Cloud disks have the ability to generate temporary URLs to files, should we do it?
32
     */
33
    private bool $useTemporaryUrl = false;
34
35
    /**
36
     * When using temporary urls, define the time that the url will be valid.
37
     */
38
    private int $temporaryUrlExpirationTimeInMinutes = 1;
39
40
    /**
41
     * Indicates if the upload is relative to a relationship field/column.
42
     */
43
    private bool $isRelationship = false;
44
45
    /**
46
     * When previous files are updated, we need to keep track of them so that we don't add deleted files to the new list.
47
     */
48
    private $updatedPreviousFiles = null;
49
50
    public function __construct(array $crudObject, array $configuration)
51
    {
52
        $this->name = $crudObject['name'];
53
        $this->disk = $configuration['disk'] ?? $crudObject['disk'] ?? $this->disk;
54
        $this->path = $this->getPathFromConfiguration($crudObject, $configuration);
55
        $this->attachedToFakeField = isset($crudObject['fake']) && $crudObject['fake'] ? ($crudObject['store_in'] ?? 'extras') : ($crudObject['store_in'] ?? false);
56
        $this->useTemporaryUrl = $configuration['temporaryUrl'] ?? $this->useTemporaryUrl;
57
        $this->temporaryUrlExpirationTimeInMinutes = $configuration['temporaryUrlExpirationTime'] ?? $this->temporaryUrlExpirationTimeInMinutes;
58
        $this->deleteWhenEntryIsDeleted = $configuration['deleteWhenEntryIsDeleted'] ?? $this->deleteWhenEntryIsDeleted;
59
        $this->fileNamer = is_callable($configuration['fileNamer'] ?? null) ? $configuration['fileNamer'] : $this->getFileNameGeneratorInstance($configuration['fileNamer'] ?? null);
60
    }
61
62
    /*******************************
63
     * Static methods
64
     *******************************/
65
    public static function for(array $crudObject, array $definition): UploaderInterface
66
    {
67
        return new static($crudObject, $definition);
68
    }
69
70
    /*******************************
71
     * public methods - event handler methods
72
     *******************************/
73
    public function storeUploadedFiles(Model $entry): Model
74
    {
75
        if ($this->handleRepeatableFiles) {
76
            return $this->handleRepeatableFiles($entry);
77
        }
78
79
        if ($this->attachedToFakeField) {
80
            $fakeFieldValue = $entry->{$this->attachedToFakeField};
81
            $fakeFieldValue = is_string($fakeFieldValue) ? json_decode($fakeFieldValue, true) : (array) $fakeFieldValue;
82
            $fakeFieldValue[$this->getAttributeName()] = $this->uploadFiles($entry);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $fakeFieldValue[$this->getAttributeName()] is correct as $this->uploadFiles($entry) targeting Backpack\CRUD\app\Librar...Uploader::uploadFiles() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
83
84
            $entry->{$this->attachedToFakeField} = isset($entry->getCasts()[$this->attachedToFakeField]) ? $fakeFieldValue : json_encode($fakeFieldValue);
85
86
            return $entry;
87
        }
88
89
        $entry->{$this->getAttributeName()} = $this->uploadFiles($entry);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $entry->$this->getAttributeName() is correct as $this->uploadFiles($entry) targeting Backpack\CRUD\app\Librar...Uploader::uploadFiles() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
90
91
        return $entry;
92
    }
93
94
    public function retrieveUploadedFiles(Model $entry): Model
95
    {
96
        if ($this->handleRepeatableFiles) {
97
            return $this->retrieveRepeatableFiles($entry);
98
        }
99
100
        return $this->retrieveFiles($entry);
101
    }
102
103
    public function deleteUploadedFiles(Model $entry): void
104
    {
105
        if (! in_array(SoftDeletes::class, class_uses_recursive($entry), true)) {
106
            $this->performFileDeletion($entry);
107
108
            return;
109
        }
110
111
        if ($entry->isForceDeleting() === true) {
112
            $this->performFileDeletion($entry);
113
        }
114
    }
115
116
    /*******************************
117
     * Getters
118
     *******************************/
119
    public function getName(): string
120
    {
121
        return $this->name;
122
    }
123
124
    public function getAttributeName(): string
125
    {
126
        return Str::afterLast($this->name, '.');
127
    }
128
129
    public function getDisk(): string
130
    {
131
        return $this->disk;
132
    }
133
134
    public function getPath(): string
135
    {
136
        return $this->path;
137
    }
138
139
    public function useTemporaryUrl(): bool
140
    {
141
        return $this->useTemporaryUrl;
142
    }
143
144
    public function getExpirationTimeInMinutes(): int
145
    {
146
        return $this->temporaryUrlExpirationTimeInMinutes;
147
    }
148
149
    public function shouldDeleteFiles(): bool
150
    {
151
        return $this->deleteWhenEntryIsDeleted;
152
    }
153
154
    public function getIdentifier(): string
155
    {
156
        if ($this->handleRepeatableFiles) {
157
            return $this->repeatableContainerName.'_'.$this->name;
158
        }
159
160
        return $this->name;
161
    }
162
163
    public function getNameForRequest(): string
164
    {
165
        return $this->repeatableContainerName ?? $this->name;
166
    }
167
168
    public function canHandleMultipleFiles(): bool
169
    {
170
        return $this->handleMultipleFiles;
171
    }
172
173
    public function isRelationship(): bool
174
    {
175
        return $this->isRelationship;
176
    }
177
178
    public function getPreviousFiles(Model $entry): mixed
179
    {
180
        if (! $this->attachedToFakeField) {
181
            return $this->getOriginalValue($entry);
182
        }
183
        $value = $this->getOriginalValue($entry, $this->attachedToFakeField);
184
        $value = is_string($value) ? json_decode($value, true) : (array) $value;
185
186
        return $value[$this->getAttributeName()] ?? null;
187
    }
188
189
    public function getValueWithoutPath(?string $value = null): ?string
190
    {
191
        return $value ? Str::after($value, $this->path) : null;
192
    }
193
194
    public function isFake(): bool
195
    {
196
        return $this->attachedToFakeField !== false;
197
    }
198
199
    public function getFakeAttribute(): bool|string
200
    {
201
        return $this->attachedToFakeField;
202
    }
203
204
    public function shouldKeepPreviousValueUnchanged(Model $entry, $entryValue): bool
205
    {
206
        return $entry->exists && ($entryValue === null || $entryValue === [null]);
207
    }
208
209
    /*******************************
210
     * Setters - fluently configure the uploader
211
     *******************************/
212
    public function multiple(): self
213
    {
214
        $this->handleMultipleFiles = true;
215
216
        return $this;
217
    }
218
219
    public function relationship(bool $isRelationship): self
220
    {
221
        $this->isRelationship = $isRelationship;
222
223
        return $this;
224
    }
225
226
    public function fake(bool|string $isFake): self
227
    {
228
        $this->attachedToFakeField = $isFake;
229
230
        return $this;
231
    }
232
233
    /*******************************
234
     * Default implementation functions
235
     *******************************/
236
    public function uploadFiles(Model $entry, $values = null)
237
    {
238
    }
239
240
    private function retrieveFiles(Model $entry): Model
241
    {
242
        $value = $entry->{$this->getAttributeName()};
243
244
        if ($this->attachedToFakeField) {
245
            $values = $entry->{$this->attachedToFakeField};
246
247
            $values = is_string($values) ? json_decode($values, true) : $values;
248
            $attributeValue = $values[$this->getAttributeName()] ?? null;
249
            $attributeValue = is_array($attributeValue) ? array_map(fn ($value) => $this->getValueWithoutPath($value), $attributeValue) : $this->getValueWithoutPath($attributeValue);
250
            $values[$this->getAttributeName()] = $attributeValue;
251
            $entry->{$this->attachedToFakeField} = isset($entry->getCasts()[$this->attachedToFakeField]) ? $values : json_encode($values);
252
253
            return $entry;
254
        }
255
256
        if ($this->handleMultipleFiles) {
257
            if (! isset($entry->getCasts()[$this->getName()]) && is_string($value)) {
258
                $entry->{$this->getAttributeName()} = json_decode($value, true);
259
            }
260
261
            return $entry;
262
        }
263
264
        $entry->{$this->getAttributeName()} = $this->getValueWithoutPath($value);
265
266
        return $entry;
267
    }
268
269
    protected function deleteFiles(Model $entry)
270
    {
271
        if (! $this->shouldDeleteFiles()) {
272
            return;
273
        }
274
275
        if ($this->attachedToFakeField) {
276
            $values = $entry->{$this->attachedToFakeField};
277
            $values = is_string($values) ? json_decode($values, true) : $values;
278
            $values = $values[$this->getAttributeName()] ?? null;
279
        }
280
281
        $values ??= $entry->{$this->getAttributeName()};
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $values does not seem to be defined for all execution paths leading up to this point.
Loading history...
282
283
        if ($values === null) {
284
            return;
285
        }
286
287
        if ($this->handleMultipleFiles) {
288
            // ensure we have an array of values when field is not casted in model.
289
            if (is_string($values)) {
290
                $values = json_decode($values, true);
291
            }
292
            foreach ($values ?? [] as $value) {
293
                $value = Str::start($value, $this->path);
294
                Storage::disk($this->disk)->delete($value);
295
            }
296
297
            return;
298
        }
299
300
        $values = Str::start($values, $this->path);
301
        Storage::disk($this->disk)->delete($values);
302
    }
303
304
    private function performFileDeletion(Model $entry)
305
    {
306
        if (! $this->handleRepeatableFiles && $this->deleteWhenEntryIsDeleted) {
307
            $this->deleteFiles($entry);
308
309
            return;
310
        }
311
312
        $this->deleteRepeatableFiles($entry);
313
    }
314
315
    /*******************************
316
     * helper methods
317
     *******************************/
318
    private function getPathFromConfiguration(array $crudObject, array $configuration): string
319
    {
320
        $this->path = $configuration['path'] ?? $crudObject['prefix'] ?? $this->path;
321
322
        return empty($this->path) ? $this->path : Str::of($this->path)->finish('/')->value();
323
    }
324
325
    private function getOriginalValue(Model $entry, $field = null)
326
    {
327
        $field ??= $this->getAttributeName();
328
329
        if ($this->updatedPreviousFiles !== null) {
330
            return $this->updatedPreviousFiles;
331
        }
332
333
        $previousValue = $entry->getOriginal($field);
334
335
        if (! $previousValue) {
336
            return $previousValue;
337
        }
338
339
        if (
340
            method_exists($entry, 'translationEnabled') &&
341
            $entry->translationEnabled() &&
342
            $entry->isTranslatableAttribute($field)
343
        ) {
344
            return $previousValue[$entry->getLocale()] ?? null;
345
        }
346
347
        return $previousValue;
348
    }
349
}
350