Completed
Push — master ( dece78...dc515c )
by Freek
02:21
created

HasMediaTrait::prepareToAttachMedia()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 2
1
<?php
2
3
namespace Spatie\MediaLibrary\HasMedia;
4
5
use Spatie\MediaLibrary\FileAdder\FileAdder;
6
use Spatie\MediaLibrary\Media;
7
use Illuminate\Support\Collection;
8
use Spatie\MediaLibrary\MediaRepository;
9
use Spatie\MediaLibrary\Conversion\Conversion;
10
use Spatie\MediaLibrary\FileAdder\FileAdderFactory;
11
use Spatie\MediaLibrary\HasMedia\Interfaces\HasMedia;
12
use Spatie\MediaLibrary\Events\CollectionHasBeenCleared;
13
use Spatie\MediaLibrary\Exceptions\MediaCannotBeDeleted;
14
use Spatie\MediaLibrary\Exceptions\MediaCannotBeUpdated;
15
use Spatie\MediaLibrary\Exceptions\FileCannotBeAdded\UnreachableUrl;
16
use Spatie\MediaLibrary\Exceptions\FileCannotBeAdded\InvalidBase64Data;
17
18
trait HasMediaTrait
19
{
20
    /** @var array */
21
    public $mediaConversions = [];
22
23
    /** @var bool */
24
    protected $deletePreservingMedia = false;
25
26
    /** @var array */
27
    protected $unAttachedMediaLibraryItems = [];
28
29
    public static function bootHasMediaTrait()
30
    {
31
        static::deleted(function (HasMedia $entity) {
32
            if ($entity->shouldDeletePreservingMedia()) {
33
                return;
34
            }
35
36
            $entity->media()->get()->each->delete();
37
        });
38
    }
39
40
    /**
41
     * Set the polymorphic relation.
42
     *
43
     * @return mixed
44
     */
45
    public function media()
46
    {
47
        return $this->morphMany(config('medialibrary.media_model'), 'model');
48
    }
49
50
    /**
51
     * Add a file to the medialibrary.
52
     *
53
     * @param string|\Symfony\Component\HttpFoundation\File\UploadedFile $file
54
     *
55
     * @return \Spatie\MediaLibrary\FileAdder\FileAdder
56
     */
57
    public function addMedia($file)
58
    {
59
        return app(FileAdderFactory::class)->create($this, $file);
60
    }
61
62
    /**
63
     * Add a file from a request.
64
     *
65
     * @param string $key
66
     *
67
     * @return \Spatie\MediaLibrary\FileAdder\FileAdder
68
     */
69
    public function addMediaFromRequest(string $key)
70
    {
71
        return app(FileAdderFactory::class)->createFromRequest($this, $key);
72
    }
73
74
    /**
75
     * Add multiple files from a request by keys.
76
     *
77
     * @param string[] $keys
78
     *
79
     * @return \Spatie\MediaLibrary\FileAdder\FileAdder[]
80
     */
81
    public function addMultipleMediaFromRequest(array $keys)
82
    {
83
        return app(FileAdderFactory::class)->createMultipleFromRequest($this, $keys);
84
    }
85
86
    /**
87
     * Add all files from a request.
88
     *
89
     * @return \Spatie\MediaLibrary\FileAdder\FileAdder[]
90
     */
91
    public function addAllMediaFromRequest()
92
    {
93
        return app(FileAdderFactory::class)->createAllFromRequest($this);
94
    }
95
96
    /**
97
     * Add a remote file to the medialibrary.
98
     *
99
     * @param string $url
100
     *
101
     * @return \Spatie\MediaLibrary\FileAdder\FileAdder
102
     *
103
     * @throws \Spatie\MediaLibrary\Exceptions\FileCannotBeAdded
104
     */
105
    public function addMediaFromUrl(string $url)
106
    {
107
        if (!$stream = @fopen($url, 'r')) {
108
            throw UnreachableUrl::create($url);
109
        }
110
111
        $tmpFile = tempnam(sys_get_temp_dir(), 'media-library');
112
        file_put_contents($tmpFile, $stream);
113
114
        $filename = basename(parse_url($url, PHP_URL_PATH));
115
116
        return app(FileAdderFactory::class)
117
            ->create($this, $tmpFile)
118
            ->usingName(pathinfo($filename, PATHINFO_FILENAME))
119
            ->usingFileName($filename);
120
    }
121
122
    /**
123
     * Add a base64 encoded file to the medialibrary.
124
     *
125
     * @param string $base64data
126
     *
127
     * @throws InvalidBase64Data
128
     * @throws \Spatie\MediaLibrary\Exceptions\FileCannotBeAdded
129
     *
130
     * @return \Spatie\MediaLibrary\FileAdder\FileAdder
131
     */
132
    public function addMediaFromBase64(string $base64data)
133
    {
134
        // strip out data uri scheme information (see RFC 2397)
135
        if (strpos($base64data, ';base64') !== false) {
136
            list(, $base64data) = explode(';', $base64data);
137
            list(, $base64data) = explode(',', $base64data);
138
        }
139
140
        // strict mode filters for non-base64 alphabet characters
141
        if (base64_decode($base64data, true) === false) {
142
            throw InvalidBase64Data::create();
143
        }
144
145
        // decoding and then reeconding should not change the data
146
        if (base64_encode(base64_decode($base64data)) !== $base64data) {
147
            throw InvalidBase64Data::create();
148
        }
149
150
        $binaryData = base64_decode($base64data);
151
152
        // temporarily store the decoded data on the filesystem to be able to pass it to the fileAdder
153
        $tmpFile = tempnam(sys_get_temp_dir(), 'medialibrary');
154
        file_put_contents($tmpFile, $binaryData);
155
156
        $file = app(FileAdderFactory::class)
157
            ->create($this, $tmpFile);
158
159
        return $file;
160
    }
161
162
    /**
163
     * Copy a file to the medialibrary.
164
     *
165
     * @param string|\Symfony\Component\HttpFoundation\File\UploadedFile $file
166
     *
167
     * @return \Spatie\MediaLibrary\FileAdder\FileAdder
168
     */
169
    public function copyMedia($file)
170
    {
171
        return $this->addMedia($file)->preservingOriginal();
172
    }
173
174
    /*
175
     * Determine if there is media in the given collection.
176
     */
177
    public function hasMedia(string $collectionName = 'default'): bool
178
    {
179
        return count($this->getMedia($collectionName)) ? true : false;
180
    }
181
182
    /**
183
     * Get media collection by its collectionName.
184
     *
185
     * @param string $collectionName
186
     * @param array|callable $filters
187
     *
188
     * @return \Illuminate\Support\Collection
189
     */
190
    public function getMedia(string $collectionName = 'default', $filters = []): Collection
191
    {
192
        return app(MediaRepository::class)->getCollection($this, $collectionName, $filters);
193
    }
194
195
    /**
196
     * Get the first media item of a media collection.
197
     *
198
     * @param string $collectionName
199
     * @param array $filters
200
     *
201
     * @return Media|null
202
     */
203
    public function getFirstMedia(string $collectionName = 'default', array $filters = [])
204
    {
205
        $media = $this->getMedia($collectionName, $filters);
206
207
        return $media->first();
208
    }
209
210
    /*
211
     * Get the url of the image for the given conversionName
212
     * for first media for the given collectionName.
213
     * If no profile is given, return the source's url.
214
     */
215
    public function getFirstMediaUrl(string $collectionName = 'default', string $conversionName = ''): string
216
    {
217
        $media = $this->getFirstMedia($collectionName);
218
219
        if (!$media) {
220
            return '';
221
        }
222
223
        return $media->getUrl($conversionName);
224
    }
225
226
    /*
227
     * Get the url of the image for the given conversionName
228
     * for first media for the given collectionName.
229
     * If no profile is given, return the source's url.
230
     */
231
    public function getFirstMediaPath(string $collectionName = 'default', string $conversionName = ''): string
232
    {
233
        $media = $this->getFirstMedia($collectionName);
234
235
        if (!$media) {
236
            return '';
237
        }
238
239
        return $media->getPath($conversionName);
240
    }
241
242
    /**
243
     * Update a media collection by deleting and inserting again with new values.
244
     *
245
     * @param array $newMediaArray
246
     * @param string $collectionName
247
     *
248
     * @return \Illuminate\Support\Collection
249
     *
250
     * @throws \Spatie\MediaLibrary\Exceptions\MediaCannotBeUpdated
251
     */
252
    public function updateMedia(array $newMediaArray, string $collectionName = 'default'): Collection
253
    {
254
        $this->removeMediaItemsNotPresentInArray($newMediaArray, $collectionName);
255
256
        return collect($newMediaArray)
257
            ->map(function (array $newMediaItem) use ($collectionName) {
258
                static $orderColumn = 1;
259
260
                $mediaClass = config('medialibrary.media_model');
261
                $currentMedia = $mediaClass::findOrFail($newMediaItem['id']);
262
263
                if ($currentMedia->collection_name != $collectionName) {
264
                    throw MediaCannotBeUpdated::doesNotBelongToCollection($collectionName, $currentMedia);
265
                }
266
267
                if (array_key_exists('name', $newMediaItem)) {
268
                    $currentMedia->name = $newMediaItem['name'];
269
                }
270
271
                if (array_key_exists('custom_properties', $newMediaItem)) {
272
                    $currentMedia->custom_properties = $newMediaItem['custom_properties'];
273
                }
274
275
                $currentMedia->order_column = $orderColumn++;
276
277
                $currentMedia->save();
278
279
                return $currentMedia;
280
            });
281
    }
282
283
    /**
284
     * @param array $newMediaArray
285
     * @param string $collectionName
286
     */
287
    protected function removeMediaItemsNotPresentInArray(array $newMediaArray, string $collectionName = 'default')
288
    {
289
        $this->getMedia($collectionName)
290
            ->reject(function (Media $currentMediaItem) use ($newMediaArray) {
291
                return in_array($currentMediaItem->id, array_column($newMediaArray, 'id'));
292
            })
293
            ->each->delete();
294
    }
295
296
    /**
297
     * Remove all media in the given collection.
298
     *
299
     * @param string $collectionName
300
     *
301
     * @return $this
302
     */
303
    public function clearMediaCollection(string $collectionName = 'default')
304
    {
305
        $this->getMedia($collectionName)
306
            ->each->delete();
307
308
        event(new CollectionHasBeenCleared($this, $collectionName));
309
310
        if ($this->mediaIsPreloaded()) {
311
            unset($this->media);
312
        }
313
314
        return $this;
315
    }
316
317
    /**
318
     * Remove all media in the given collection except some.
319
     *
320
     * @param string $collectionName
321
     * @param \Spatie\MediaLibrary\Media[]|\Illuminate\Support\Collection $excludedMedia
322
     *
323
     * @return $this
324
     */
325
    public function clearMediaCollectionExcept(string $collectionName = 'default', $excludedMedia = [])
326
    {
327
        $excludedMedia = collect($excludedMedia);
328
329
        if ($excludedMedia->isEmpty()) {
330
            return $this->clearMediaCollection($collectionName);
331
        }
332
333
        $this->getMedia($collectionName)
334
            ->reject(function (Media $media) use ($excludedMedia) {
335
                return $excludedMedia->where('id', $media->id)->count();
336
            })
337
            ->each->delete();
338
339
        if ($this->mediaIsPreloaded()) {
340
            unset($this->media);
341
        }
342
343
        return $this;
344
    }
345
346
    /**
347
     * Delete the associated media with the given id.
348
     * You may also pass a media object.
349
     *
350
     * @param int|\Spatie\MediaLibrary\Media $mediaId
351
     *
352
     * @throws \Spatie\MediaLibrary\Exceptions\MediaCannotBeDeleted
353
     */
354
    public function deleteMedia($mediaId)
355
    {
356
        if ($mediaId instanceof Media) {
357
            $mediaId = $mediaId->id;
358
        }
359
360
        $media = $this->media->find($mediaId);
361
362
        if (!$media) {
363
            throw MediaCannotBeDeleted::doesNotBelongToModel($media, $this);
364
        }
365
366
        $media->delete();
367
    }
368
369
    /*
370
     * Add a conversion.
371
     */
372
    public function addMediaConversion(string $name): Conversion
373
    {
374
        $conversion = Conversion::create($name);
375
376
        $this->mediaConversions[] = $conversion;
377
378
        return $conversion;
379
    }
380
381
    /**
382
     * Delete the model, but preserve all the associated media.
383
     *
384
     * @return bool
385
     */
386
    public function deletePreservingMedia(): bool
387
    {
388
        $this->deletePreservingMedia = true;
389
390
        return $this->delete();
0 ignored issues
show
Bug introduced by
The method delete() does not exist on Spatie\MediaLibrary\HasMedia\HasMediaTrait. Did you maybe mean deleteMedia()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
391
    }
392
393
    /**
394
     * Determines if the media files should be preserved when the media object gets deleted.
395
     *
396
     * @return \Spatie\MediaLibrary\Media
397
     */
398
    public function shouldDeletePreservingMedia()
399
    {
400
        return $this->deletePreservingMedia ?? false;
401
    }
402
403
    protected function mediaIsPreloaded(): bool
404
    {
405
        return $this->relationLoaded('media');
0 ignored issues
show
Bug introduced by
It seems like relationLoaded() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
406
    }
407
408
    /**
409
     * Cache the media on the object.
410
     *
411
     * @param string $collectionName
412
     *
413
     * @return mixed
414
     */
415
    public function loadMedia(string $collectionName)
416
    {
417
        $collection = $this->exists ? $this->media : collect($this->unsavedMediaItems);
0 ignored issues
show
Bug introduced by
The property exists does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
Bug introduced by
The property unsavedMediaItems does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
418
419
        return $collection
420
            ->filter(function (Media $mediaItem) use ($collectionName) {
421
                if ($collectionName == '') {
422
                    return true;
423
                }
424
425
                return $mediaItem->collection_name === $collectionName;
426
            })
427
            ->sortBy('order_column')
428
            ->values();
429
    }
430
431
    public function prepareToAttachMedia(Media $media, FileAdder $fileAdder)
432
    {
433
        $this->unAttachedMediaLibraryItems[] = compact('media', 'fileAdder');
434
    }
435
436
    public function processUnattachedMedia(callable $callable) {
437
        foreach($this->unAttachedMediaLibraryItems as $item) {
438
            $callable($item['media'], $item['fileAdder']);
439
        }
440
441
        $this->unAttachedMediaLibraryItems = [];
442
    }
443
}
444