Passed
Pull Request — main (#161)
by Daniel
05:38 queued 01:35
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 TemporaryUrlGenerator) {
75
//                // $filesystem->temporaryUrl();
76
//            }
77
78
            $urlGenerator = $this->urlGenerators->get($fieldConfiguration->urlGenerator);
79
            if (!$urlGenerator instanceof UploadableUrlGeneratorInterface) {
80
                throw new InvalidArgumentException(sprintf('The url generator provided must implement %s', UploadableUrlGeneratorInterface::class));
81
            }
82
            $contentUrl = $urlGenerator->generateUrl($object, $fileProperty, $filesystem, $path);
83
84
            // Populate the primary MediaObject
85
            try {
86
                $propertyMediaObjects[] = $this->create($filesystem, $path, $contentUrl);
87
            } catch (UnableToReadFile $exception) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
88
            }
89
90
            array_push($propertyMediaObjects, ...$this->getMediaObjectsForImagineFilters($object, $path, $fieldConfiguration, $fileProperty));
91
92
            $collection->set($fileProperty, $propertyMediaObjects);
93
        }
94
95
        return $collection->count() ? $collection : null;
96
    }
97
98
    /**
99
     * @return MediaObject[]
100
     */
101
    private function getMediaObjectsForImagineFilters(object $object, string $path, UploadableField $uploadableField, string $fileProperty): array
102
    {
103
        $mediaObjects = [];
104
        if (!$this->filterService) {
105
            return $mediaObjects;
106
        }
107
108
        // Let the data loader which should be configured for imagine to know which adapter to use
109
        $this->flysystemDataLoader->setAdapter($uploadableField->adapter);
110
111
        $filters = $uploadableField->imagineFilters;
112
        if ($object instanceof ImagineFiltersInterface) {
113
            $request = $this->requestStack->getMainRequest();
114
            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

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