Completed
Push — master ( df0849...3fdaef )
by Freek
07:05
created

FileAdder   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 341
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 16

Importance

Changes 0
Metric Value
wmc 44
lcom 2
cbo 16
dl 0
loc 341
rs 8.8798
c 0
b 0
f 0

23 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A setSubject() 0 6 1
A setFile() 0 30 4
A preservingOriginal() 0 6 1
A usingName() 0 4 1
A setName() 0 6 1
A usingFileName() 0 4 1
A setFileName() 0 6 1
A withCustomProperties() 0 6 1
A withManipulations() 0 6 1
A withProperties() 0 6 1
A withAttributes() 0 4 1
A withResponsiveImages() 0 6 1
A addCustomHeaders() 0 8 1
A toMediaCollectionOnCloudDisk() 0 4 1
B toMediaCollection() 0 46 5
A determineDiskName() 0 16 4
A defaultSanitizer() 0 4 1
A sanitizingFileName() 0 6 1
A attachMedia() 0 18 2
B processMediaItem() 0 32 7
A getMediaCollection() 0 9 1
A guardAgainstDisallowedFileAdditions() 0 16 5

How to fix   Complexity   

Complex Class

Complex classes like FileAdder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FileAdder, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Spatie\MediaLibrary\FileAdder;
4
5
use Spatie\MediaLibrary\Helpers\File;
6
use Spatie\MediaLibrary\Models\Media;
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Support\Traits\Macroable;
9
use Spatie\MediaLibrary\HasMedia\HasMedia;
10
use Spatie\MediaLibrary\File as PendingFile;
11
use Spatie\MediaLibrary\Filesystem\Filesystem;
12
use Spatie\MediaLibrary\Jobs\GenerateResponsiveImages;
13
use Symfony\Component\HttpFoundation\File\UploadedFile;
14
use Spatie\MediaLibrary\MediaCollection\MediaCollection;
15
use Symfony\Component\HttpFoundation\File\File as SymfonyFile;
16
use Spatie\MediaLibrary\Exceptions\FileCannotBeAdded\UnknownType;
17
use Spatie\MediaLibrary\Exceptions\FileCannotBeAdded\FileIsTooBig;
18
use Spatie\MediaLibrary\Exceptions\FileCannotBeAdded\DiskDoesNotExist;
19
use Spatie\MediaLibrary\Exceptions\FileCannotBeAdded\FileDoesNotExist;
20
use Spatie\MediaLibrary\ImageGenerators\FileTypes\Image as ImageGenerator;
21
use Spatie\MediaLibrary\Exceptions\FileCannotBeAdded\FileUnacceptableForCollection;
22
23
class FileAdder
24
{
25
    use Macroable;
26
27
    /** @var \Illuminate\Database\Eloquent\Model subject */
28
    protected $subject;
29
30
    /** @var \Spatie\MediaLibrary\Filesystem\Filesystem */
31
    protected $filesystem;
32
33
    /** @var bool */
34
    protected $preserveOriginal = false;
35
36
    /** @var string|\Symfony\Component\HttpFoundation\File\UploadedFile */
37
    protected $file;
38
39
    /** @var array */
40
    protected $properties = [];
41
42
    /** @var array */
43
    protected $customProperties = [];
44
45
    /** @var array */
46
    protected $manipulations = [];
47
48
    /** @var string */
49
    protected $pathToFile;
50
51
    /** @var string */
52
    protected $fileName;
53
54
    /** @var string */
55
    protected $mediaName;
56
57
    /** @var string */
58
    protected $diskName = '';
59
60
    /** @var null|callable */
61
    protected $fileNameSanitizer;
62
63
    /** @var bool */
64
    protected $generateResponsiveImages = false;
65
66
    /** @var array */
67
    protected $customHeaders = [];
68
69
    /**
70
     * @param Filesystem $fileSystem
71
     */
72
    public function __construct(Filesystem $fileSystem)
73
    {
74
        $this->filesystem = $fileSystem;
75
76
        $this->fileNameSanitizer = function ($fileName) {
77
            return $this->defaultSanitizer($fileName);
78
        };
79
    }
80
81
    /**
82
     * @param \Illuminate\Database\Eloquent\Model $subject
83
     *
84
     * @return FileAdder
85
     */
86
    public function setSubject(Model $subject)
87
    {
88
        $this->subject = $subject;
89
90
        return $this;
91
    }
92
93
    /*
94
     * Set the file that needs to be imported.
95
     *
96
     * @param string|\Symfony\Component\HttpFoundation\File\UploadedFile $file
97
     *
98
     * @return $this
99
     */
100
    public function setFile($file): self
101
    {
102
        $this->file = $file;
103
104
        if (is_string($file)) {
105
            $this->pathToFile = $file;
106
            $this->setFileName(pathinfo($file, PATHINFO_BASENAME));
107
            $this->mediaName = pathinfo($file, PATHINFO_FILENAME);
108
109
            return $this;
110
        }
111
112
        if ($file instanceof UploadedFile) {
113
            $this->pathToFile = $file->getPath().'/'.$file->getFilename();
114
            $this->setFileName($file->getClientOriginalName());
115
            $this->mediaName = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
116
117
            return $this;
118
        }
119
120
        if ($file instanceof SymfonyFile) {
121
            $this->pathToFile = $file->getPath().'/'.$file->getFilename();
122
            $this->setFileName(pathinfo($file->getFilename(), PATHINFO_BASENAME));
123
            $this->mediaName = pathinfo($file->getFilename(), PATHINFO_FILENAME);
124
125
            return $this;
126
        }
127
128
        throw UnknownType::create();
129
    }
130
131
    public function preservingOriginal(): self
132
    {
133
        $this->preserveOriginal = true;
134
135
        return $this;
136
    }
137
138
    public function usingName(string $name): self
139
    {
140
        return $this->setName($name);
141
    }
142
143
    public function setName(string $name): self
144
    {
145
        $this->mediaName = $name;
146
147
        return $this;
148
    }
149
150
    public function usingFileName(string $fileName): self
151
    {
152
        return $this->setFileName($fileName);
153
    }
154
155
    public function setFileName(string $fileName): self
156
    {
157
        $this->fileName = $fileName;
158
159
        return $this;
160
    }
161
162
    public function withCustomProperties(array $customProperties): self
163
    {
164
        $this->customProperties = $customProperties;
165
166
        return $this;
167
    }
168
169
    public function withManipulations(array $manipulations): self
170
    {
171
        $this->manipulations = $manipulations;
172
173
        return $this;
174
    }
175
176
    public function withProperties(array $properties): self
177
    {
178
        $this->properties = $properties;
179
180
        return $this;
181
    }
182
183
    public function withAttributes(array $properties): self
184
    {
185
        return $this->withProperties($properties);
186
    }
187
188
    public function withResponsiveImages(): self
189
    {
190
        $this->generateResponsiveImages = true;
191
192
        return $this;
193
    }
194
195
    public function addCustomHeaders(array $customRemoteHeaders): self
196
    {
197
        $this->customHeaders = $customRemoteHeaders;
198
199
        $this->filesystem->addCustomRemoteHeaders($customRemoteHeaders);
200
201
        return $this;
202
    }
203
204
    public function toMediaCollectionOnCloudDisk(string $collectionName = 'default'): Media
205
    {
206
        return $this->toMediaCollection($collectionName, config('filesystems.cloud'));
207
    }
208
209
    public function toMediaCollection(string $collectionName = 'default', string $diskName = ''): Media
210
    {
211
        if (! is_file($this->pathToFile)) {
212
            throw FileDoesNotExist::create($this->pathToFile);
213
        }
214
215
        if (filesize($this->pathToFile) > config('medialibrary.max_file_size')) {
216
            throw FileIsTooBig::create($this->pathToFile);
217
        }
218
219
        $mediaClass = config('medialibrary.media_model');
220
        /** @var \Spatie\MediaLibrary\Models\Media $media */
221
        $media = new $mediaClass();
222
223
        $media->name = $this->mediaName;
224
225
        $this->fileName = ($this->fileNameSanitizer)($this->fileName);
226
227
        $media->file_name = $this->fileName;
228
229
        $media->disk = $this->determineDiskName($diskName, $collectionName);
230
231
        if (is_null(config("filesystems.disks.{$media->disk}"))) {
232
            throw DiskDoesNotExist::create($media->disk);
233
        }
234
235
        $media->collection_name = $collectionName;
236
237
        $media->mime_type = File::getMimetype($this->pathToFile);
238
        $media->size = filesize($this->pathToFile);
239
        $media->custom_properties = $this->customProperties;
240
241
        $media->responsive_images = [];
242
243
        $media->manipulations = $this->manipulations;
244
245
        if (filled($this->customHeaders)) {
246
            $media->setCustomHeaders($this->customHeaders);
247
        }
248
249
        $media->fill($this->properties);
250
251
        $this->attachMedia($media);
252
253
        return $media;
254
    }
255
256
    protected function determineDiskName(string $diskName, string $collectionName): string
257
    {
258
        if ($diskName !== '') {
259
            return $diskName;
260
        }
261
262
        if ($collection = $this->getMediaCollection($collectionName)) {
263
            $collectionDiskName = $collection->diskName;
264
265
            if ($collectionDiskName !== '') {
266
                return $collectionDiskName;
267
            }
268
        }
269
270
        return config('medialibrary.disk_name');
271
    }
272
273
    public function defaultSanitizer(string $fileName): string
274
    {
275
        return str_replace(['#', '/', '\\', ' '], '-', $fileName);
276
    }
277
278
    public function sanitizingFileName(callable $fileNameSanitizer): self
279
    {
280
        $this->fileNameSanitizer = $fileNameSanitizer;
281
282
        return $this;
283
    }
284
285
    protected function attachMedia(Media $media)
286
    {
287
        if (! $this->subject->exists) {
288
            $this->subject->prepareToAttachMedia($media, $this);
289
290
            $class = get_class($this->subject);
291
292
            $class::created(function ($model) {
293
                $model->processUnattachedMedia(function (Media $media, self $fileAdder) use ($model) {
294
                    $this->processMediaItem($model, $media, $fileAdder);
295
                });
296
            });
297
298
            return;
299
        }
300
301
        $this->processMediaItem($this->subject, $media, $this);
0 ignored issues
show
Documentation introduced by
$this is of type this<Spatie\MediaLibrary\FileAdder\FileAdder>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
302
    }
303
304
    protected function processMediaItem(HasMedia $model, Media $media, self $fileAdder)
305
    {
306
        $this->guardAgainstDisallowedFileAdditions($media, $model);
0 ignored issues
show
Unused Code introduced by
The call to FileAdder::guardAgainstDisallowedFileAdditions() has too many arguments starting with $model.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
307
308
        $model->media()->save($media);
309
310
        $this->filesystem->add($fileAdder->pathToFile, $media, $fileAdder->fileName);
311
312
        if (! $fileAdder->preserveOriginal) {
313
            unlink($fileAdder->pathToFile);
314
        }
315
316
        if ($this->generateResponsiveImages && (new ImageGenerator())->canConvert($media)) {
317
            $generateResponsiveImagesJobClass = config('medialibrary.jobs.generate_responsive_images', GenerateResponsiveImages::class);
318
319
            $job = new $generateResponsiveImagesJobClass($media);
320
321
            if ($customQueue = config('medialibrary.queue_name')) {
322
                $job->onQueue($customQueue);
323
            }
324
325
            dispatch($job);
326
        }
327
328
        if ($collectionSizeLimit = optional($this->getMediaCollection($media->collection_name))->collectionSizeLimit) {
329
            $collectionMedia = $this->subject->fresh()->getMedia($media->collection_name);
330
331
            if ($collectionMedia->count() > $collectionSizeLimit) {
332
                $model->clearMediaCollectionExcept($media->collection_name, $collectionMedia->reverse()->take($collectionSizeLimit));
333
            }
334
        }
335
    }
336
337
    protected function getMediaCollection(string $collectionName): ?MediaCollection
338
    {
339
        $this->subject->registerMediaCollections();
340
341
        return collect($this->subject->mediaCollections)
342
            ->first(function (MediaCollection $collection) use ($collectionName) {
343
                return $collection->name === $collectionName;
344
            });
345
    }
346
347
    protected function guardAgainstDisallowedFileAdditions(Media $media)
348
    {
349
        $file = PendingFile::createFromMedia($media);
350
351
        if (! $collection = $this->getMediaCollection($media->collection_name)) {
352
            return;
353
        }
354
355
        if (! ($collection->acceptsFile)($file, $this->subject)) {
356
            throw FileUnacceptableForCollection::create($file, $collection, $this->subject);
357
        }
358
359
        if (! empty($collection->acceptsMimeTypes) && ! in_array($file->mimeType, $collection->acceptsMimeTypes)) {
360
            throw FileUnacceptableForCollection::create($file, $collection, $this->subject);
361
        }
362
    }
363
}
364