Passed
Push — main ( fe2f17...ba3d9b )
by Daniel
15:33
created

MediaObjectFactory   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 145
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 67
dl 0
loc 145
ccs 0
cts 69
cp 0
rs 10
c 1
b 0
f 0
wmc 20

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getMediaObjectsForImagineFilters() 0 22 4
A createFromImagine() 0 17 2
B create() 0 30 6
A createMediaObjects() 0 40 6
A __construct() 0 11 1
A populateMediaObjectFromCache() 0 8 1
1
<?php
2
3
/*
4
 * This file is part of the Silverback API Components Bundle Project
5
 *
6
 * (c) Daniel West <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Silverback\ApiComponentsBundle\Factory\Uploadable;
15
16
use Doctrine\Common\Collections\ArrayCollection;
17
use Doctrine\Persistence\ManagerRegistry;
18
use League\Flysystem\Filesystem;
19
use League\Flysystem\UnableToReadFile;
20
use Liip\ImagineBundle\Service\FilterService;
21
use Silverback\ApiComponentsBundle\Annotation\UploadableField;
22
use Silverback\ApiComponentsBundle\AttributeReader\UploadableAttributeReader;
23
use Silverback\ApiComponentsBundle\Entity\Core\FileInfo;
24
use Silverback\ApiComponentsBundle\Entity\Utility\ImagineFiltersInterface;
25
use Silverback\ApiComponentsBundle\Flysystem\FilesystemProvider;
26
use Silverback\ApiComponentsBundle\Helper\Uploadable\FileInfoCacheManager;
27
use Silverback\ApiComponentsBundle\Imagine\FlysystemDataLoader;
28
use Silverback\ApiComponentsBundle\Model\Uploadable\MediaObject;
29
use Silverback\ApiComponentsBundle\Utility\ClassMetadataTrait;
30
use Symfony\Component\HttpFoundation\RequestStack;
31
32
/**
33
 * @author Daniel West <[email protected]>
34
 */
35
class MediaObjectFactory
36
{
37
    use ClassMetadataTrait;
38
39
    public function __construct(
40
        ManagerRegistry $managerRegistry,
41
        private readonly FileInfoCacheManager $fileInfoCacheManager,
42
        private readonly UploadableAttributeReader $annotationReader,
43
        private readonly FilesystemProvider $filesystemProvider,
44
        private readonly FlysystemDataLoader $flysystemDataLoader,
45
        private readonly RequestStack $requestStack,
46
        private readonly ApiUrlGenerator $urlGenerator,
47
        private readonly ?FilterService $filterService = null)
48
    {
49
        $this->initRegistry($managerRegistry);
50
    }
51
52
    public function createMediaObjects(object $object): ?ArrayCollection
53
    {
54
        $collection = new ArrayCollection();
55
        $classMetadata = $this->getClassMetadata($object);
56
57
        $configuredProperties = $this->annotationReader->getConfiguredProperties($object, true);
58
59
        foreach ($configuredProperties as $fileProperty => $fieldConfiguration) {
60
            $propertyMediaObjects = [];
61
            // todo: we may need to look at the performance of this when getting the components. yes, the response is cached, but even first load on a page with lots of files, could be very bad
62
            $filesystem = $this->filesystemProvider->getFilesystem($fieldConfiguration->adapter);
0 ignored issues
show
Bug introduced by
It seems like $fieldConfiguration->adapter can also be of type null; however, parameter $name of Silverback\ApiComponents...ovider::getFilesystem() 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

62
            $filesystem = $this->filesystemProvider->getFilesystem(/** @scrutinizer ignore-type */ $fieldConfiguration->adapter);
Loading history...
63
            $path = $classMetadata->getFieldValue($object, $fieldConfiguration->property);
64
            if (!$path) {
65
                continue;
66
            }
67
            if (!$filesystem->fileExists($path)) {
68
                continue;
69
            }
70
71
            // todo: the content URL perhaps will just be a public URL from the source/CDN instead of via this API download action
72
//            if ($filesystem instanceof PublicUrlGenerator) {
73
//                // $filesystem->publicUrl();
74
//            }
75
//            if ($filesystem instanceof TemporaryUrlGenerator) {
76
//                // $filesystem->temporaryUrl();
77
//            }
78
            $contentUrl = $this->urlGenerator->generateUrl($object, $fileProperty);
79
80
            // Populate the primary MediaObject
81
            try {
82
                $propertyMediaObjects[] = $this->create($filesystem, $path, $contentUrl);
83
            } catch (UnableToReadFile $exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
84
            }
85
86
            array_push($propertyMediaObjects, ...$this->getMediaObjectsForImagineFilters($object, $path, $fieldConfiguration, $fileProperty));
87
88
            $collection->set($fileProperty, $propertyMediaObjects);
89
        }
90
91
        return $collection->count() ? $collection : null;
92
    }
93
94
    /**
95
     * @return MediaObject[]
96
     */
97
    private function getMediaObjectsForImagineFilters(object $object, string $path, UploadableField $uploadableField, string $fileProperty): array
98
    {
99
        $mediaObjects = [];
100
        if (!$this->filterService) {
101
            return $mediaObjects;
102
        }
103
104
        // Let the data loader which should be configured for imagine to know which adapter to use
105
        $this->flysystemDataLoader->setAdapter($uploadableField->adapter);
106
107
        $filters = $uploadableField->imagineFilters;
108
        if ($object instanceof ImagineFiltersInterface) {
109
            $request = $this->requestStack->getMainRequest();
110
            array_push($filters, ...$object->getImagineFilters($fileProperty, $request));
0 ignored issues
show
Bug introduced by
It seems like $filters can also be of type null; however, parameter $array of array_push() does only seem to accept array, 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

110
            array_push(/** @scrutinizer ignore-type */ $filters, ...$object->getImagineFilters($fileProperty, $request));
Loading history...
111
        }
112
113
        foreach ($filters as $filter) {
114
            $resolvedUrl = $this->filterService->getUrlOfFilteredImage($path, $filter);
115
            $mediaObjects[] = $this->createFromImagine($resolvedUrl, $path, $filter);
116
        }
117
118
        return $mediaObjects;
119
    }
120
121
    private function create(Filesystem $filesystem, string $filename, string $contentUrl): MediaObject
122
    {
123
        $mediaObject = new MediaObject();
124
125
        $mediaObject->contentUrl = $contentUrl;
126
        $mediaObject->imagineFilter = null;
127
128
        $fileInfo = $this->fileInfoCacheManager->resolveCache($filename);
129
        if ($fileInfo) {
130
            return $this->populateMediaObjectFromCache($mediaObject, $fileInfo);
131
        }
132
133
        $mediaObject->fileSize = $filesystem->fileSize($filename);
134
        $mediaObject->mimeType = $filesystem->mimeType($filename);
135
        if (str_contains($mediaObject->mimeType, 'image/')) {
136
            $file = str_replace("\0", '', $filesystem->read($filename));
137
            if ('image/svg+xml' === $mediaObject->mimeType) {
138
                $xmlGet = simplexml_load_string($file);
139
                $xmlAttributes = $xmlGet->attributes();
140
                $mediaObject->width = $xmlAttributes ? (int) $xmlAttributes->width : null;
141
                $mediaObject->height = $xmlAttributes ? (int) $xmlAttributes->height : null;
142
            } else {
143
                [$mediaObject->width, $mediaObject->height] = @getimagesize($file);
144
            }
145
        }
146
147
        $fileInfo = new FileInfo($filename, $mediaObject->mimeType, $mediaObject->fileSize, $mediaObject->width, $mediaObject->height);
148
        $this->fileInfoCacheManager->saveCache($fileInfo);
149
150
        return $mediaObject;
151
    }
152
153
    private function createFromImagine(string $contentUrl, string $path, string $imagineFilter): MediaObject
154
    {
155
        $mediaObject = new MediaObject();
156
        $mediaObject->contentUrl = $contentUrl;
157
        $mediaObject->imagineFilter = $imagineFilter;
158
159
        $fileInfo = $this->fileInfoCacheManager->resolveCache($path, $imagineFilter);
160
        if ($fileInfo) {
161
            return $this->populateMediaObjectFromCache($mediaObject, $fileInfo);
162
        }
163
164
        // todo: check why we are setting this, from imagine we should know this info I'm guessing
165
        // todo: should we not save the info to cache as well as above?
166
        $mediaObject->width = $mediaObject->height = $mediaObject->fileSize = -1;
167
        $mediaObject->mimeType = '';
168
169
        return $mediaObject;
170
    }
171
172
    private function populateMediaObjectFromCache(MediaObject $mediaObject, FileInfo $fileInfo): MediaObject
173
    {
174
        $mediaObject->fileSize = $fileInfo->fileSize;
175
        $mediaObject->mimeType = $fileInfo->mimeType;
176
        $mediaObject->width = $fileInfo->width;
177
        $mediaObject->height = $fileInfo->height;
178
179
        return $mediaObject;
180
    }
181
}
182