MediaObjectFactory::populateMediaObjectFromCache()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 5
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 8
ccs 0
cts 6
cp 0
crap 2
rs 10
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\Exception\InvalidArgumentException;
26
use Silverback\ApiComponentsBundle\Flysystem\FilesystemFactory;
27
use Silverback\ApiComponentsBundle\Flysystem\FilesystemProvider;
28
use Silverback\ApiComponentsBundle\Helper\Uploadable\FileInfoCacheManager;
29
use Silverback\ApiComponentsBundle\Imagine\FlysystemDataLoader;
30
use Silverback\ApiComponentsBundle\Model\Uploadable\MediaObject;
31
use Silverback\ApiComponentsBundle\Utility\ClassMetadataTrait;
32
use Symfony\Component\DependencyInjection\ServiceLocator;
33
use Symfony\Component\HttpFoundation\RequestStack;
34
35
/**
36
 * @author Daniel West <[email protected]>
37
 */
38
class MediaObjectFactory
39
{
40
    use ClassMetadataTrait;
41
42
    public function __construct(
43
        ManagerRegistry $managerRegistry,
44
        private readonly FileInfoCacheManager $fileInfoCacheManager,
45
        private readonly UploadableAttributeReader $annotationReader,
46
        private readonly FilesystemProvider $filesystemProvider,
47
        private readonly FlysystemDataLoader $flysystemDataLoader,
48
        private readonly RequestStack $requestStack,
49
        private readonly FilesystemFactory $filesystemFactory,
50
        private readonly ServiceLocator $urlGenerators,
51
        private readonly ?FilterService $filterService = null
52
    ) {
53
        $this->initRegistry($managerRegistry);
54
    }
55
56
    public function createMediaObjects(object $object): ?ArrayCollection
57
    {
58
        $collection = new ArrayCollection();
59
        $classMetadata = $this->getClassMetadata($object);
60
61
        $configuredProperties = $this->annotationReader->getConfiguredProperties($object, true);
62
63
        foreach ($configuredProperties as $fileProperty => $fieldConfiguration) {
64
            $propertyMediaObjects = [];
65
66
            $filesystem = $this->filesystemProvider->getFilesystem($fieldConfiguration->adapter);
67
            $path = $classMetadata->getFieldValue($object, $fieldConfiguration->property);
68
            if (!$path) {
69
                continue;
70
            }
71
72
            if (!$filesystem->fileExists($path)) {
73
                continue;
74
            }
75
76
            // todo: consultation on perhaps attributes which can be configured with environment variables or best way to achieve easier implementation
77
            $urlGeneratorReference = $fieldConfiguration->urlGenerator ?? 'api';
78
            $urlGenerator = $this->urlGenerators->get($urlGeneratorReference);
79
            if ('api' !== $urlGenerator) {
80
                $adapter = $this->filesystemFactory->getAdapter($fieldConfiguration->adapter);
81
                if (
82
                    ($urlGenerator instanceof TemporaryUrlGenerator && !($adapter instanceof \League\Flysystem\UrlGeneration\TemporaryUrlGenerator))
83
                    || ($urlGenerator instanceof PublicUrlGenerator && !($adapter instanceof \League\Flysystem\UrlGeneration\PublicUrlGenerator))
84
                ) {
85
                    $urlGeneratorReference = 'api';
86
                    $urlGenerator = $this->urlGenerators->get($urlGeneratorReference);
87
                }
88
            }
89
90
            if (!$urlGenerator instanceof UploadableUrlGeneratorInterface) {
91
                throw new InvalidArgumentException(sprintf('The url generator provided must implement %s', UploadableUrlGeneratorInterface::class));
92
            }
93
            $contentUrl = $urlGenerator->generateUrl($object, $fileProperty, $filesystem, $path);
94
95
            // Populate the primary MediaObject
96
            try {
97
                $propertyMediaObjects[] = $this->create($filesystem, $path, $contentUrl);
98
            } catch (UnableToReadFile $exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
99
            }
100
101
            array_push($propertyMediaObjects, ...$this->getMediaObjectsForImagineFilters($object, $path, $fieldConfiguration, $fileProperty));
102
103
            $collection->set($fileProperty, $propertyMediaObjects);
104
        }
105
106
        return $collection->count() ? $collection : null;
107
    }
108
109
    /**
110
     * @return MediaObject[]
111
     */
112
    private function getMediaObjectsForImagineFilters(object $object, string $path, UploadableField $uploadableField, string $fileProperty): array
113
    {
114
        $mediaObjects = [];
115
        if (!$this->filterService) {
116
            return $mediaObjects;
117
        }
118
119
        // Let the data loader which should be configured for imagine to know which adapter to use
120
        $this->flysystemDataLoader->setAdapter($uploadableField->adapter);
121
122
        $filters = $uploadableField->imagineFilters;
123
        if ($object instanceof ImagineFiltersInterface) {
124
            $request = $this->requestStack->getMainRequest();
125
            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

125
            array_push(/** @scrutinizer ignore-type */ $filters, ...$object->getImagineFilters($fileProperty, $request));
Loading history...
126
        }
127
128
        foreach ($filters as $filter) {
129
            $resolvedUrl = $this->filterService->getUrlOfFilteredImage($path, $filter);
130
            $mediaObjects[] = $this->createFromImagine($resolvedUrl, $path, $filter);
131
        }
132
133
        return $mediaObjects;
134
    }
135
136
    private function create(Filesystem $filesystem, string $filename, string $contentUrl): MediaObject
137
    {
138
        $mediaObject = new MediaObject();
139
140
        $mediaObject->contentUrl = $contentUrl;
141
        $mediaObject->imagineFilter = null;
142
143
        $fileInfo = $this->fileInfoCacheManager->resolveCache($filename);
144
        if ($fileInfo) {
145
            return $this->populateMediaObjectFromCache($mediaObject, $fileInfo);
146
        }
147
148
        $mediaObject->fileSize = $filesystem->fileSize($filename);
149
        $mediaObject->mimeType = $filesystem->mimeType($filename);
150
        if (str_contains($mediaObject->mimeType, 'image/')) {
151
            $file = str_replace("\0", '', $filesystem->read($filename));
152
            if ('image/svg+xml' === $mediaObject->mimeType) {
153
                $xmlGet = simplexml_load_string($file);
154
                $xmlAttributes = $xmlGet->attributes();
155
                $mediaObject->width = $xmlAttributes ? (int) $xmlAttributes->width : null;
156
                $mediaObject->height = $xmlAttributes ? (int) $xmlAttributes->height : null;
157
            } else {
158
                [$mediaObject->width, $mediaObject->height] = @getimagesize($file);
159
            }
160
        }
161
162
        $fileInfo = new FileInfo($filename, $mediaObject->mimeType, $mediaObject->fileSize, $mediaObject->width, $mediaObject->height);
163
        $this->fileInfoCacheManager->saveCache($fileInfo);
164
165
        return $mediaObject;
166
    }
167
168
    private function createFromImagine(string $contentUrl, string $path, string $imagineFilter): MediaObject
169
    {
170
        $mediaObject = new MediaObject();
171
        $mediaObject->contentUrl = $contentUrl;
172
        $mediaObject->imagineFilter = $imagineFilter;
173
174
        $fileInfo = $this->fileInfoCacheManager->resolveCache($path, $imagineFilter);
175
        if ($fileInfo) {
176
            return $this->populateMediaObjectFromCache($mediaObject, $fileInfo);
177
        }
178
179
        $mediaObject->width = $mediaObject->height = $mediaObject->fileSize = -1;
180
        $mediaObject->mimeType = '';
181
182
        return $mediaObject;
183
    }
184
185
    private function populateMediaObjectFromCache(MediaObject $mediaObject, FileInfo $fileInfo): MediaObject
186
    {
187
        $mediaObject->fileSize = $fileInfo->fileSize;
188
        $mediaObject->mimeType = $fileInfo->mimeType;
189
        $mediaObject->width = $fileInfo->width;
190
        $mediaObject->height = $fileInfo->height;
191
192
        return $mediaObject;
193
    }
194
}
195