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

Test Failed
Pull Request — main (#5478)
by Cristian
34:13 queued 19:11
created

Uploader::isRelationship()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 1
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: $pivot, $exists, $pivotParent
Loading history...
17
18
    private string $name;
19
20
    private string $disk = 'public';
21
22
    private string $path = '';
23
24
    private 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
    /*******************************
205
     * Setters - fluently configure the uploader
206
     *******************************/
207
    public function multiple(): self
208
    {
209
        $this->handleMultipleFiles = true;
210
211
        return $this;
212
    }
213
214
    public function relationship(bool $isRelationship): self
215
    {
216
        $this->isRelationship = $isRelationship;
217
218
        return $this;
219
    }
220
221
    public function fake(bool|string $isFake): self
222
    {
223
        $this->attachedToFakeField = $isFake;
224
225
        return $this;
226
    }
227
228
    /*******************************
229
     * Default implementation functions
230
     *******************************/
231
    public function uploadFiles(Model $entry, $values = null)
232
    {
233
    }
234
235
    private function retrieveFiles(Model $entry): Model
236
    {
237
        $value = $entry->{$this->getAttributeName()};
238
239
        if ($this->attachedToFakeField) {
240
            $values = $entry->{$this->attachedToFakeField};
241
242
            $values = is_string($values) ? json_decode($values, true) : $values;
243
            $attributeValue = $values[$this->getAttributeName()] ?? null;
244
            $attributeValue = is_array($attributeValue) ? array_map(fn ($value) => $this->getValueWithoutPath($value), $attributeValue) : $this->getValueWithoutPath($attributeValue);
245
            $values[$this->getAttributeName()] = $attributeValue;
246
            $entry->{$this->attachedToFakeField} = isset($entry->getCasts()[$this->attachedToFakeField]) ? $values : json_encode($values);
247
248
            return $entry;
249
        }
250
251
        if ($this->handleMultipleFiles) {
252
            if (! isset($entry->getCasts()[$this->getName()]) && is_string($value)) {
253
                $entry->{$this->getAttributeName()} = json_decode($value, true);
254
            }
255
256
            return $entry;
257
        }
258
259
        $entry->{$this->getAttributeName()} = $this->getValueWithoutPath($value);
260
261
        return $entry;
262
    }
263
264
    private function deleteFiles(Model $entry)
265
    {
266
        if (! $this->shouldDeleteFiles()) {
267
            return;
268
        }
269
270
        if ($this->attachedToFakeField) {
271
            $values = $entry->{$this->attachedToFakeField};
272
            $values = is_string($values) ? json_decode($values, true) : $values;
273
            $values = $values[$this->getAttributeName()] ?? null;
274
        }
275
276
        $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...
277
278
        if ($values === null) {
279
            return;
280
        }
281
282
        if ($this->handleMultipleFiles) {
283
            // ensure we have an array of values when field is not casted in model.
284
            if (is_string($values)) {
285
                $values = json_decode($values, true);
286
            }
287
            foreach ($values ?? [] as $value) {
288
                $value = Str::start($value, $this->path);
289
                Storage::disk($this->disk)->delete($value);
290
            }
291
292
            return;
293
        }
294
295
        $values = Str::start($values, $this->path);
296
        Storage::disk($this->disk)->delete($values);
297
    }
298
299
    private function performFileDeletion(Model $entry)
300
    {
301
        if (! $this->handleRepeatableFiles && $this->deleteWhenEntryIsDeleted) {
302
            $this->deleteFiles($entry);
303
304
            return;
305
        }
306
307
        $this->deleteRepeatableFiles($entry);
308
    }
309
310
    /*******************************
311
     * helper methods
312
     *******************************/
313
    private function getPathFromConfiguration(array $crudObject, array $configuration): string
314
    {
315
        $this->path = $configuration['path'] ?? $crudObject['prefix'] ?? $this->path;
316
317
        return empty($this->path) ? $this->path : Str::of($this->path)->finish('/')->value();
318
    }
319
320
    private function getOriginalValue(Model $entry, $field = null)
321
    {
322
        $field ??= $this->getAttributeName();
323
324
        if ($this->updatedPreviousFiles !== null) {
325
            return $this->updatedPreviousFiles;
326
        }
327
328
        $previousValue = $entry->getOriginal($field);
329
330
        if (! $previousValue) {
331
            return $previousValue;
332
        }
333
334
        if (
335
            method_exists($entry, 'translationEnabled') &&
336
            $entry->translationEnabled() &&
337
            $entry->isTranslatableAttribute($field)
338
        ) {
339
            return $previousValue[$entry->getLocale()] ?? null;
340
        }
341
342
        return $previousValue;
343
    }
344
}
345