Issues (281)

Branch: master

MediaLibrary/Domain/MediaItem/MediaItem.php (1 issue)

1
<?php
2
3
namespace Backend\Modules\MediaLibrary\Domain\MediaItem;
4
5
use Backend\Modules\MediaLibrary\Component\StorageProvider\LiipImagineBundleStorageProviderInterface;
6
use Backend\Modules\MediaLibrary\Component\StorageProvider\StorageProviderInterface;
7
use Backend\Modules\MediaLibrary\Domain\MediaGroupMediaItem\MediaGroupMediaItem;
8
use DateTime;
9
use Doctrine\Common\Collections\Collection;
10
use Doctrine\ORM\Mapping as ORM;
11
use Doctrine\Common\Collections\ArrayCollection;
12
use Exception;
13
use JsonSerializable;
14
use Symfony\Component\HttpFoundation\File\File;
15
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
16
use Backend\Modules\MediaLibrary\Domain\MediaFolder\MediaFolder;
17
use Backend\Core\Engine\Model;
18
19
/**
20
 * MediaItem
21
 *
22
 * @ORM\Entity(repositoryClass="Backend\Modules\MediaLibrary\Domain\MediaItem\MediaItemRepository")
23
 * @ORM\HasLifecycleCallbacks
24
 */
25
class MediaItem implements JsonSerializable
26
{
27
    /**
28
     * @var string
29
     *
30
     * @ORM\Id()
31
     * @ORM\GeneratedValue(strategy="UUID")
32
     * @ORM\Column(type="guid")
33
     */
34
    private $id;
35
36
    /**
37
     * @var MediaFolder
38
     *
39
     * @ORM\ManyToOne(
40
     *      targetEntity="Backend\Modules\MediaLibrary\Domain\MediaFolder\MediaFolder",
41
     *      inversedBy="items",
42
     *      cascade={"persist"}
43
     * )
44
     * @ORM\JoinColumn(
45
     *      name="mediaFolderId",
46
     *      referencedColumnName="id",
47
     *      onDelete="cascade",
48
     *      nullable=false
49
     * )
50
     */
51
    protected $folder;
52
53
    /**
54
     * @var int
55
     *
56
     * @ORM\Column(type="integer")
57
     */
58
    protected $userId;
59
60
    /**
61
     * @var StorageType
62
     *
63
     * @ORM\Column(type="media_item_storage_type", options={"default"="local"})
64
     */
65
    protected $storageType;
66
67
    /**
68
     * @var Type
69
     *
70
     * @ORM\Column(type="media_item_type")
71
     */
72
    protected $type;
73
74
    /**
75
     * @var string|null
76
     *
77
     * @ORM\Column(type="string", nullable=true)
78
     */
79
    protected $mime;
80
81
    /**
82
     * @var string|null
83
     *
84
     * @ORM\Column(type="string", nullable=true)
85
     */
86
    protected $shardingFolderName;
87
88
    /**
89
     * @var string
90
     *
91
     * @ORM\Column(type="string")
92
     */
93
    protected $url;
94
95
    /**
96
     * @var string
97
     *
98
     * @ORM\Column(type="string")
99
     */
100
    protected $title;
101
102
    /**
103
     * @var int|null
104
     *
105
     * @ORM\Column(type="integer", nullable=true)
106
     */
107
    protected $size;
108
109
    /**
110
     * @var int|null
111
     *
112
     * @ORM\Column(type="integer", nullable=true)
113
     */
114
    protected $width;
115
116
    /**
117
     * @var int|null
118
     *
119
     * @ORM\Column(type="integer", nullable=true)
120
     */
121
    protected $height;
122
123
    /**
124
     * @var AspectRatio|null
125
     *
126
     * @ORM\Column(type="media_item_aspect_ratio", nullable=true)
127
     */
128
    protected $aspectRatio;
129
130
    /**
131
     * @var DateTime
132
     *
133
     * @ORM\Column(type="datetime")
134
     */
135
    protected $createdOn;
136
137
    /**
138
     * @var DateTime
139
     *
140
     * @ORM\Column(type="datetime")
141
     */
142
    protected $editedOn;
143
144
    /**
145
     * @var Collection<MediaGroupMediaItem>
146
     *
147
     * @ORM\OneToMany(
148
     *     targetEntity="Backend\Modules\MediaLibrary\Domain\MediaGroupMediaItem\MediaGroupMediaItem",
149
     *     mappedBy="item",
150
     *     cascade={"persist","merge"},
151
     *     orphanRemoval=true
152
     * )
153
     */
154
    protected $groups;
155
156
    private function __construct(
157
        string $title,
158
        string $url,
159
        Type $type,
160
        StorageType $storageType,
161
        MediaFolder $folder,
162
        int $userId
163
    ) {
164
        $this->folder = $folder;
165
        $this->userId = $userId;
166
        $this->type = $type;
167
        $this->storageType = $storageType;
168
        $this->url = $url;
169
        $this->title = $title;
170
        $this->createdOn = new DateTime();
171
        $this->editedOn = new DateTime();
172
        $this->groups = new ArrayCollection();
173
    }
174
175
    public static function createFromLocalStorageType(
176
        string $path,
177
        MediaFolder $folder,
178
        int $userId
179
    ): self {
180
        $file = self::getFileFromPath($path);
181
182
        $mediaItem = new self(
183
            self::getTitleFromFile($file),
184
            $file->getFilename(),
185
            self::getTypeFromFile($file),
186
            StorageType::local(),
187
            $folder,
188
            $userId
189
        );
190
191
        $mediaItem->setForFile($file);
192
193
        if (!in_array($mediaItem->getMime(), ['image/svg', 'image/svg+xml'])) {
194
            $mediaItem->setResolutionFromPath($path);
195
        } else {
196
            $mediaItem->setResolutionForSvg($path);
197
        }
198
199
        return $mediaItem;
200
    }
201
202
    private function setForFile(File $file)
203
    {
204
        $this->mime = $file->getMimeType();
205
        $this->size = $file->getSize();
206
207
        // Define sharding folder (getPath gets the path without the trailing slash)
208
        $this->shardingFolderName = basename($file->getPath());
209
    }
210
211
    private function setResolutionFromPath(string $path)
212
    {
213
        if ($this->getType()->isImage()) {
214
            try {
215
                [$width, $height] = getimagesize($path);
216
            } catch (Exception $e) {
217
                throw new Exception(
218
                    'Error happened when creating MediaItem from path "' . $path . '". The error = ' . $e->getMessage()
219
                );
220
            }
221
222
            $this->setResolution($width, $height);
223
        }
224
    }
225
226
    private function setResolutionForSvg(string $path)
227
    {
228
        if (!$this->getType()->isImage()) {
229
            throw new \LogicException('SVG file must be of type image.');
230
        }
231
232
        try {
233
            $xmlget = simplexml_load_file($path);
234
            $viewBoxString = $xmlget->attributes()->viewBox;
235
            if ($viewBoxString !== null) {
236
                $viewBox = explode(' ', $viewBoxString);
237
                $width = (int) $viewBox[2];
238
                $height = (int) $viewBox[3];
239
            } else {
240
                $width = (int) $xmlget->attributes()->width;
241
                $height = (int) $xmlget->attributes()->height;
242
            }
243
244
            // no size defined in svg
245
            if ($width === 0 && $height === 0) {
246
                $width = 200;
247
                $height = 200;
248
            }
249
        } catch (Exception $e) {
250
            throw new Exception(
251
                'Error happened when creating MediaItem from path "' . $path . '". The error = ' . $e->getMessage()
252
            );
253
        }
254
255
        $this->setResolution($width, $height);
256
    }
257
258
    public static function createFromMovieUrl(
259
        StorageType $movieStorageType,
260
        string $movieId,
261
        string $movieTitle,
262
        MediaFolder $folder,
263
        int $userId
264
    ): MediaItem {
265
        return new self(
266
            $movieTitle,
267
            $movieId,
268
            Type::movie(),
269
            $movieStorageType,
270
            $folder,
271
            $userId
272
        );
273
    }
274
275
    public static function fromDataTransferObject(MediaItemDataTransferObject $mediaItemDataTransferObject): ?MediaItem
276
    {
277
        if (!$mediaItemDataTransferObject->hasExistingMediaItem()) {
278
            throw new \BadFunctionCallException('This method can not be used to create a new media item');
279
        }
280
281
        $mediaItem = $mediaItemDataTransferObject->getMediaItemEntity();
282
283
        $mediaItem->title = $mediaItemDataTransferObject->title;
284
        $mediaItem->folder = $mediaItemDataTransferObject->folder;
285
        $mediaItem->userId = $mediaItemDataTransferObject->userId;
286
        $mediaItem->url = $mediaItemDataTransferObject->url;
287
288
        return $mediaItem;
289
    }
290
291
    public function jsonSerialize(): array
292
    {
293
        return [
294
            'id' => $this->id,
295
            'folder' => $this->folder,
296
            'userId' => $this->userId,
297
            'type' => (string) $this->type,
298
            'storageType' => (string) $this->storageType,
299
            'mime' => $this->mime,
300
            'shardingFolderName' => $this->shardingFolderName,
301
            'url' => $this->url,
302
            'fullUrl' => $this->getFullUrl(),
303
            'title' => $this->title,
304
            'size' => $this->size,
305
            'width' => $this->width,
306
            'height' => $this->height,
307
            'createdOn' => $this->createdOn->getTimestamp(),
308
            'editedOn' => $this->editedOn->getTimestamp(),
309
            'source' => $this->getWebPath(),
310
            'preview_source' => $this->getWebPath('backend'),
311
            'direct_url' => $this->getWebPath(),
312
            $this->type->getType() => true,
313
        ];
314
    }
315
316
    private static function getFileFromPath(string $path): File
317
    {
318
        try {
319
            // Define file from path
320
            $file = new File($path);
321
        } catch (FileNotFoundException $e) {
322
            throw new Exception(
323
                'This is not a valid file: "' . $path . '".'
324
            );
325
        }
326
327
        // We don't have a file
328
        if (!$file->isFile()) {
329
            throw new Exception(
330
                'The given source is not a file.'
331
            );
332
        }
333
334
        return $file;
335
    }
336
337
    private static function getTitleFromFile(File $file): string
338
    {
339
        return str_replace('.' . $file->getExtension(), '', $file->getFilename());
340
    }
341
342
    private static function getTypeFromFile(File $file): Type
343
    {
344
        $extensionType = Type::fromExtension($file->getExtension());
345
346
        try {
347
            $mimeTypeType = Type::fromMimeType($file->getMimeType());
0 ignored issues
show
It seems like $file->getMimeType() can also be of type null; however, parameter $mimeType of Backend\Modules\MediaLib...em\Type::fromMimeType() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

347
            $mimeTypeType = Type::fromMimeType(/** @scrutinizer ignore-type */ $file->getMimeType());
Loading history...
348
        } catch (Exception $exception) {
349
            return $extensionType;
350
        }
351
352
        if (!$extensionType->equals($mimeTypeType)) {
353
            return $extensionType;
354
        }
355
356
        return $mimeTypeType;
357
    }
358
359
    public function getId(): string
360
    {
361
        return $this->id;
362
    }
363
364
    public function getFolder(): MediaFolder
365
    {
366
        return $this->folder;
367
    }
368
369
    public function setFolder(MediaFolder $folder): self
370
    {
371
        $this->folder = $folder;
372
373
        return $this;
374
    }
375
376
    public function getUserId(): int
377
    {
378
        return $this->userId;
379
    }
380
381
    public function getStorageType(): StorageType
382
    {
383
        return $this->storageType;
384
    }
385
386
    public function setStorageType(StorageType $storageType)
387
    {
388
        $this->storageType = $storageType;
389
    }
390
391
    public function getType(): Type
392
    {
393
        return $this->type;
394
    }
395
396
    public function getMime(): string
397
    {
398
        return $this->mime;
399
    }
400
401
    public function getScardingFolderName(): ?string
402
    {
403
        return $this->shardingFolderName;
404
    }
405
406
    public function getUrl(): string
407
    {
408
        return $this->url;
409
    }
410
411
    public function getTitle(): string
412
    {
413
        return $this->title;
414
    }
415
416
    public function setTitle(string $title)
417
    {
418
        $this->title = $title;
419
    }
420
421
    public function getFullUrl(): string
422
    {
423
        return $this->getScardingFolderName() . '/' . $this->getUrl();
424
    }
425
426
    public function getSize(): int
427
    {
428
        return $this->size;
429
    }
430
431
    public function setResolution(int $width, int $height): self
432
    {
433
        $this->width = $width;
434
        $this->height = $height;
435
436
        $this->refreshAspectRatio();
437
438
        return $this;
439
    }
440
441
    public function getWidth(): ?int
442
    {
443
        return $this->width;
444
    }
445
446
    public function getHeight(): ?int
447
    {
448
        return $this->height;
449
    }
450
451
    public function getCreatedOn(): DateTime
452
    {
453
        return $this->createdOn;
454
    }
455
456
    public function getEditedOn(): DateTime
457
    {
458
        return $this->editedOn;
459
    }
460
461
    public function getGroups(): Collection
462
    {
463
        return $this->groups;
464
    }
465
466
    public function hasGroups(): bool
467
    {
468
        return $this->groups->count() > 0;
469
    }
470
471
    public function getAbsolutePath(): string
472
    {
473
        return Model::get('media_library.manager.storage')->getStorageProvider(
474
            $this->getStorageType()
475
        )->getAbsolutePath($this);
476
    }
477
478
    public function getAbsoluteWebPath(): string
479
    {
480
        return Model::get('media_library.manager.storage')->getStorageProvider(
481
            $this->getStorageType()
482
        )->getAbsoluteWebPath($this);
483
    }
484
485
    public function getLinkHTML(): string
486
    {
487
        return Model::get('media_library.manager.storage')->getStorageProvider($this->getStorageType())->getLinkHTML(
488
            $this
489
        );
490
    }
491
492
    public function getIncludeHTML(): string
493
    {
494
        return Model::get('media_library.manager.storage')->getStorageProvider($this->getStorageType())->getIncludeHTML(
495
            $this
496
        );
497
    }
498
499
    public function getAspectRatio(): AspectRatio
500
    {
501
        return $this->aspectRatio;
502
    }
503
504
    public function getWebPath(string $liipImagineBundleFilter = null): string
505
    {
506
        /** @var StorageProviderInterface $storage */
507
        $storage = Model::get('media_library.manager.storage')->getStorageProvider($this->getStorageType());
508
509
        if (!$storage instanceof LiipImagineBundleStorageProviderInterface || $liipImagineBundleFilter === null) {
510
            return $storage->getWebPath($this);
511
        }
512
513
        return $storage->getWebPathWithFilter($this, $liipImagineBundleFilter);
514
    }
515
516
    public function getThumbnail(string $liipImagineBundleFilter = null): string
517
    {
518
        /** @var StorageProviderInterface $storage */
519
        $storage = Model::get('media_library.manager.storage')->getStorageProvider($this->getStorageType());
520
521
        if (!$storage instanceof LiipImagineBundleStorageProviderInterface || $liipImagineBundleFilter === null) {
522
            return $storage->getThumbnail($this);
523
        }
524
525
        return $storage->getWebPathWithFilter($this, $liipImagineBundleFilter);
526
    }
527
528
    private function refreshAspectRatio(): void
529
    {
530
        if ($this->height === null || $this->width === null) {
531
            $this->aspectRatio = null;
532
533
            return;
534
        }
535
536
        $this->aspectRatio = AspectRatio::fromWidthAndHeight($this->width, $this->height);
537
    }
538
539
    /**
540
     * @ORM\PrePersist
541
     */
542
    public function onPrePersist()
543
    {
544
        $this->createdOn = $this->editedOn = new DateTime();
545
546
        $this->refreshAspectRatio();
547
    }
548
549
    /**
550
     * @ORM\PreUpdate
551
     */
552
    public function onPreUpdate()
553
    {
554
        $this->editedOn = new DateTime();
555
556
        $this->refreshAspectRatio();
557
    }
558
}
559