Passed
Pull Request — main (#161)
by Daniel
07:10 queued 03:15
created

MediaObjectFactory::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 8
dl 0
loc 11
ccs 0
cts 2
cp 0
crap 2
rs 10
c 1
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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\FilesystemProvider;
27
use Silverback\ApiComponentsBundle\Helper\Uploadable\FileInfoCacheManager;
28
use Silverback\ApiComponentsBundle\Imagine\FlysystemDataLoader;
29
use Silverback\ApiComponentsBundle\Model\Uploadable\MediaObject;
30
use Silverback\ApiComponentsBundle\Utility\ClassMetadataTrait;
31
use Symfony\Component\DependencyInjection\ServiceLocator;
32
use Symfony\Component\HttpFoundation\RequestStack;
33
34
/**
35
 * @author Daniel West <[email protected]>
36
 */
37
class MediaObjectFactory
38
{
39
    use ClassMetadataTrait;
40
41
    public function __construct(
42
        ManagerRegistry $managerRegistry,
43
        private readonly FileInfoCacheManager $fileInfoCacheManager,
44
        private readonly UploadableAttributeReader $annotationReader,
45
        private readonly FilesystemProvider $filesystemProvider,
46
        private readonly FlysystemDataLoader $flysystemDataLoader,
47
        private readonly RequestStack $requestStack,
48
        private readonly ServiceLocator $urlGenerators,
49
        private readonly ?FilterService $filterService = null
50
    ) {
51
        $this->initRegistry($managerRegistry);
52
    }
53
54
    public function createMediaObjects(object $object): ?ArrayCollection
55
    {
56
        $collection = new ArrayCollection();
57
        $classMetadata = $this->getClassMetadata($object);
58
59
        $configuredProperties = $this->annotationReader->getConfiguredProperties($object, true);
60
61
        foreach ($configuredProperties as $fileProperty => $fieldConfiguration) {
62
            $propertyMediaObjects = [];
63
            // 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
64
            $filesystem = $this->filesystemProvider->getFilesystem($fieldConfiguration->adapter);
65
            $path = $classMetadata->getFieldValue($object, $fieldConfiguration->property);
66
            if (!$path) {
67
                continue;
68
            }
69
            if (!$filesystem->fileExists($path)) {
70
                continue;
71
            }
72
73
            // todo: the content URL perhaps will just be a public URL from the source/CDN instead of via this API download action
74
//            if ($filesystem instanceof PublicUrlGenerator) {
75
//                // $filesystem->publicUrl();
76
//            }
77
//            if ($filesystem instanceof TemporaryUrlGenerator) {
78
//                // $filesystem->temporaryUrl();
79
//            }
80
81
            $urlGenerator = $this->urlGenerators->get($fieldConfiguration->urlGenerator);
82
            if (!$urlGenerator instanceof UploadableUrlGeneratorInterface) {
83
                throw new InvalidArgumentException(sprintf('The url generator provided must implement %s', UploadableUrlGeneratorInterface::class));
84
            }
85
            $contentUrl = $urlGenerator->generateUrl($object, $fileProperty);
86
87
            // Populate the primary MediaObject
88
            try {
89
                $propertyMediaObjects[] = $this->create($filesystem, $path, $contentUrl);
90
            } catch (UnableToReadFile $exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
91
            }
92
93
            array_push($propertyMediaObjects, ...$this->getMediaObjectsForImagineFilters($object, $path, $fieldConfiguration, $fileProperty));
94
95
            $collection->set($fileProperty, $propertyMediaObjects);
96
        }
97
98
        return $collection->count() ? $collection : null;
99
    }
100
101
    /**
102
     * @return MediaObject[]
103
     */
104
    private function getMediaObjectsForImagineFilters(object $object, string $path, UploadableField $uploadableField, string $fileProperty): array
105
    {
106
        $mediaObjects = [];
107
        if (!$this->filterService) {
108
            return $mediaObjects;
109
        }
110
111
        // Let the data loader which should be configured for imagine to know which adapter to use
112
        $this->flysystemDataLoader->setAdapter($uploadableField->adapter);
113
114
        $filters = $uploadableField->imagineFilters;
115
        if ($object instanceof ImagineFiltersInterface) {
116
            $request = $this->requestStack->getMainRequest();
117
            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

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