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
Push — fix-upload-validators-in-repea... ( 0d6f3d )
by Pedro
14:44
created

Uploader::getIdentifier()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 7
rs 10
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\Collection;
11
use Illuminate\Support\Facades\Storage;
12
use Illuminate\Support\Str;
13
14
abstract class Uploader implements UploaderInterface
15
{
16
    use HandleFileNaming;
17
    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...
18
19
    private string $name;
20
21
    private string $disk = 'public';
22
23
    private string $path = '';
24
25
    private bool $handleMultipleFiles = false;
26
27
    private bool $deleteWhenEntryIsDeleted = true;
28
29
    private bool|string $attachedToFakeField = false;
30
31
    /**
32
     * Cloud disks have the ability to generate temporary URLs to files, should we do it?
33
     */
34
    private bool $useTemporaryUrl = false;
35
36
    /**
37
     * When using temporary urls, define the time that the url will be valid.
38
     */
39
    private int $temporaryUrlExpirationTimeInMinutes = 1;
40
41
    /**
42
     * Indicates if the upload is relative to a relationship field/column.
43
     */
44
    private bool $isRelationship = false;
45
46
    /**
47
     * When previous files are updated, we need to keep track of them so that we don't add deleted files to the new list.
48
     */
49
    private $updatedPreviousFiles = null;
50
51
    public function __construct(array $crudObject, array $configuration)
52
    {
53
        $this->name = $crudObject['name'];
54
        $this->disk = $configuration['disk'] ?? $crudObject['disk'] ?? $this->disk;
55
        $this->path = $this->getPathFromConfiguration($crudObject, $configuration);
56
        $this->attachedToFakeField = isset($crudObject['fake']) && $crudObject['fake'] ? ($crudObject['store_in'] ?? 'extras') : ($crudObject['store_in'] ?? false);
57
        $this->useTemporaryUrl = $configuration['temporaryUrl'] ?? $this->useTemporaryUrl;
58
        $this->temporaryUrlExpirationTimeInMinutes = $configuration['temporaryUrlExpirationTime'] ?? $this->temporaryUrlExpirationTimeInMinutes;
59
        $this->deleteWhenEntryIsDeleted = $configuration['deleteWhenEntryIsDeleted'] ?? $this->deleteWhenEntryIsDeleted;
60
        $this->fileNamer = is_callable($configuration['fileNamer'] ?? null) ? $configuration['fileNamer'] : $this->getFileNameGeneratorInstance($configuration['fileNamer'] ?? null);
61
    }
62
63
    /*******************************
64
     * Static methods
65
     *******************************/
66
    public static function for(array $crudObject, array $definition): UploaderInterface
67
    {
68
        return new static($crudObject, $definition);
69
    }
70
71
    /*******************************
72
     * public methods - event handler methods
73
     *******************************/
74
    public function storeUploadedFiles(Model $entry): Model
75
    {
76
        if ($this->handleRepeatableFiles) {
77
            return $this->handleRepeatableFiles($entry);
78
        }
79
80
        if ($this->attachedToFakeField) {
81
            $fakeFieldValue = $entry->{$this->attachedToFakeField};
82
            $fakeFieldValue = is_string($fakeFieldValue) ? json_decode($fakeFieldValue, true) : (array) $fakeFieldValue;
83
            $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...
84
85
            $entry->{$this->attachedToFakeField} = isset($entry->getCasts()[$this->attachedToFakeField]) ? $fakeFieldValue : json_encode($fakeFieldValue);
86
87
            return $entry;
88
        }
89
90
        $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...
91
92
        return $entry;
93
    }
94
95
    public function retrieveUploadedFiles(Model $entry): Model
96
    {
97
        if ($this->handleRepeatableFiles) {
98
            return $this->retrieveRepeatableFiles($entry);
99
        }
100
101
        return $this->retrieveFiles($entry);
102
    }
103
104
    public function deleteUploadedFiles(Model $entry): void
105
    {
106
        if ($this->deleteWhenEntryIsDeleted) {
107
            if (! in_array(SoftDeletes::class, class_uses_recursive($entry), true)) {
108
                $this->performFileDeletion($entry);
109
110
                return;
111
            }
112
113
            if ($entry->isForceDeleting() === true) {
114
                $this->performFileDeletion($entry);
115
            }
116
        }
117
    }
118
119
    /*******************************
120
     * Getters
121
     *******************************/
122
    public function getName(): string
123
    {
124
        return $this->name;
125
    }
126
127
    public function getAttributeName(): string
128
    {
129
        return Str::afterLast($this->name, '.');
130
    }
131
132
    public function getDisk(): string
133
    {
134
        return $this->disk;
135
    }
136
137
    public function getPath(): string
138
    {
139
        return $this->path;
140
    }
141
142
    public function useTemporaryUrl(): bool
143
    {
144
        return $this->useTemporaryUrl;
145
    }
146
147
    public function getExpirationTimeInMinutes(): int
148
    {
149
        return $this->temporaryUrlExpirationTimeInMinutes;
150
    }
151
152
    public function shouldDeleteFiles(): bool
153
    {
154
        return $this->deleteWhenEntryIsDeleted;
155
    }
156
157
    public function getIdentifier(): string
158
    {
159
        if ($this->handleRepeatableFiles) {
160
            return $this->repeatableContainerName.'_'.$this->name;
161
        }
162
163
        return $this->name;
164
    }
165
166
    public function getNameForRequest(): string
167
    {
168
        return $this->repeatableContainerName ?? $this->name;
169
    }
170
171
    public function canHandleMultipleFiles(): bool
172
    {
173
        return $this->handleMultipleFiles;
174
    }
175
176
    public function isRelationship(): bool
177
    {
178
        return $this->isRelationship;
179
    }
180
181
    public function getPreviousFiles(Model $entry): mixed
182
    {
183
        if (! $this->attachedToFakeField) {
184
            return $this->getOriginalValue($entry);
185
        }
186
187
        $value = $this->getOriginalValue($entry, $this->attachedToFakeField);
188
        $value = is_string($value) ? json_decode($value, true) : (array) $value;
189
190
        return $value[$this->getAttributeName()] ?? null;
191
    }
192
193
    /*******************************
194
     * Setters - fluently configure the uploader
195
     *******************************/
196
    public function multiple(): self
197
    {
198
        $this->handleMultipleFiles = true;
199
200
        return $this;
201
    }
202
203
    public function relationship(bool $isRelationship): self
204
    {
205
        $this->isRelationship = $isRelationship;
206
207
        return $this;
208
    }
209
210
    /*******************************
211
     * Default implementation functions
212
     *******************************/
213
    public function uploadFiles(Model $entry, $values = null)
0 ignored issues
show
Unused Code introduced by
The parameter $values 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

213
    public function uploadFiles(Model $entry, /** @scrutinizer ignore-unused */ $values = null)

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...
214
    {
215
    }
216
217
    private function retrieveFiles(Model $entry): Model
218
    {
219
        $value = $entry->{$this->getAttributeName()};
220
221
        if ($this->handleMultipleFiles) {
222
            if (! isset($entry->getCasts()[$this->getName()]) && is_string($value)) {
223
                $entry->{$this->getAttributeName()} = json_decode($value, true);
224
            }
225
226
            return $entry;
227
        }
228
229
        if ($this->attachedToFakeField) {
230
            $values = $entry->{$this->attachedToFakeField};
231
            $values = is_string($values) ? json_decode($values, true) : (array) $values;
232
233
            $values[$this->getAttributeName()] = isset($values[$this->getAttributeName()]) ? Str::after($values[$this->getAttributeName()], $this->path) : null;
234
            $entry->{$this->attachedToFakeField} = json_encode($values);
235
236
            return $entry;
237
        }
238
239
        $entry->{$this->getAttributeName()} = Str::after($value, $this->path);
240
241
        return $entry;
242
    }
243
244
    private function deleteFiles(Model $entry)
245
    {
246
        $values = $entry->{$this->getAttributeName()};
247
248
        if ($values === null) {
249
            return;
250
        }
251
252
        if ($this->handleMultipleFiles) {
253
            // ensure we have an array of values when field is not casted in model.
254
            if (! isset($entry->getCasts()[$this->name]) && is_string($values)) {
255
                $values = json_decode($values, true);
256
            }
257
            foreach ($values ?? [] as $value) {
258
                $value = Str::start($value, $this->path);
259
                Storage::disk($this->disk)->delete($value);
260
            }
261
262
            return;
263
        }
264
265
        $values = Str::start($values, $this->path);
266
        Storage::disk($this->disk)->delete($values);
267
    }
268
269
    private function performFileDeletion(Model $entry)
270
    {
271
        if (! $this->handleRepeatableFiles) {
272
            $this->deleteFiles($entry);
273
274
            return;
275
        }
276
277
        $this->deleteRepeatableFiles($entry);
278
    }
279
280
    /*******************************
281
     * Private helper methods
282
     *******************************/
283
    private function getPathFromConfiguration(array $crudObject, array $configuration): string
284
    {
285
        $this->path = $configuration['path'] ?? $crudObject['prefix'] ?? $this->path;
286
287
        return empty($this->path) ? $this->path : Str::of($this->path)->finish('/')->value();
288
    }
289
290
    private function getOriginalValue(Model $entry, $field = null)
291
    {
292
        if ($this->updatedPreviousFiles !== null) {
293
            return $this->updatedPreviousFiles;
294
        }
295
296
        $previousValue = $entry->getOriginal($field ?? $this->getAttributeName());
297
298
        if (! $previousValue) {
299
            return $previousValue;
300
        }
301
302
        if (method_exists($entry, 'translationEnabled') && $entry->translationEnabled()) {
303
            return $previousValue[$entry->getLocale()] ?? null;
304
        }
305
306
        return $previousValue;
307
    }
308
309
    /*******************************
310
     * Helper methods
311
     *******************************/
312
313
    /**
314
     * Given two multidimensional arrays/collections, merge them recursively.
315
     */
316
    public static function mergeFilesAndValuesRecursive(array|Collection $array1, array|Collection $array2): array|Collection
317
    {
318
        $merged = $array1;
319
        foreach ($array2 as $key => &$value) {
320
            if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
321
                $merged[$key] = self::mergeFilesAndValuesRecursive($merged[$key], $value);
322
            } else {
323
                $merged[$key] = $value;
324
            }
325
        }
326
327
        return $merged;
328
    }
329
}
330