Passed
Pull Request — master (#58)
by Daniel
06:24
created

UploadableHelper   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 161
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 4
Bugs 1 Features 0
Metric Value
eloc 82
c 4
b 1
f 0
dl 0
loc 161
ccs 0
cts 85
cp 0
rs 10
wmc 22

7 Methods

Rating   Name   Duplication   Size   Complexity  
A deleteFiles() 0 9 3
A persistFiles() 0 31 4
A storeFilesMetadata() 0 17 5
A removeFilepath() 0 12 3
A __construct() 0 16 1
A setUploadedFilesFromFileBag() 0 11 3
A getFileResponse() 0 34 3
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\Uploadable;
15
16
use Doctrine\Persistence\ManagerRegistry;
17
use Liip\ImagineBundle\Service\FilterService;
18
use Silverback\ApiComponentsBundle\Annotation\UploadableField;
19
use Silverback\ApiComponentsBundle\AnnotationReader\UploadableAnnotationReader;
20
use Silverback\ApiComponentsBundle\Entity\Utility\ImagineFiltersInterface;
21
use Silverback\ApiComponentsBundle\Flysystem\FilesystemProvider;
22
use Silverback\ApiComponentsBundle\Imagine\CacheManager;
23
use Silverback\ApiComponentsBundle\Imagine\FlysystemDataLoader;
24
use Silverback\ApiComponentsBundle\Model\Uploadable\UploadedDataUriFile;
25
use Silverback\ApiComponentsBundle\Utility\ClassMetadataTrait;
26
use Symfony\Component\HttpFoundation\FileBag;
27
use Symfony\Component\HttpFoundation\HeaderUtils;
28
use Symfony\Component\HttpFoundation\Response;
29
use Symfony\Component\HttpFoundation\StreamedResponse;
30
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
31
use Symfony\Component\PropertyAccess\PropertyAccess;
32
33
/**
34
 * @author Daniel West <[email protected]>
35
 */
36
class UploadableHelper
37
{
38
    use ClassMetadataTrait;
39
40
    private UploadableAnnotationReader $annotationReader;
41
    private FilesystemProvider $filesystemProvider;
42
    private FlysystemDataLoader $flysystemDataLoader;
43
    private FileInfoCacheHelper $fileInfoCacheHelper;
44
    private ?CacheManager $imagineCacheManager;
45
    private ?FilterService $filterService;
46
47
    public function __construct(
48
        ManagerRegistry $registry,
49
        UploadableAnnotationReader $annotationReader,
50
        FilesystemProvider $filesystemProvider,
51
        FlysystemDataLoader $flysystemDataLoader,
52
        FileInfoCacheHelper $fileInfoCacheHelper,
53
        ?CacheManager $imagineCacheManager,
54
        ?FilterService $filterService = null
55
    ) {
56
        $this->initRegistry($registry);
57
        $this->annotationReader = $annotationReader;
58
        $this->filesystemProvider = $filesystemProvider;
59
        $this->flysystemDataLoader = $flysystemDataLoader;
60
        $this->fileInfoCacheHelper = $fileInfoCacheHelper;
61
        $this->imagineCacheManager = $imagineCacheManager;
62
        $this->filterService = $filterService;
63
    }
64
65
    public function setUploadedFilesFromFileBag(object $object, FileBag $fileBag): void
66
    {
67
        $propertyAccessor = PropertyAccess::createPropertyAccessor();
68
        $configuredProperties = $this->annotationReader->getConfiguredProperties($object, false);
69
70
        /**
71
         * @var UploadableField[] $configuredProperties
72
         */
73
        foreach ($configuredProperties as $fileProperty => $fieldConfiguration) {
74
            if ($file = $fileBag->get($fileProperty, null)) {
75
                $propertyAccessor->setValue($object, $fileProperty, $file);
76
            }
77
        }
78
    }
79
80
    public function storeFilesMetadata(object $object): void
81
    {
82
        $configuredProperties = $this->annotationReader->getConfiguredProperties($object, true);
83
        $classMetadata = $this->getClassMetadata($object);
84
85
        foreach ($configuredProperties as $fileProperty => $fieldConfiguration) {
86
            // Let the data loader which should be configured for imagine to know which adapter to use
87
            $this->flysystemDataLoader->setAdapter($fieldConfiguration->adapter);
88
89
            $filename = $classMetadata->getFieldValue($object, $fieldConfiguration->property);
90
91
            if ($object instanceof ImagineFiltersInterface && $this->filterService) {
92
                $filters = $object->getImagineFilters($fileProperty, null);
93
                foreach ($filters as $filter) {
94
                    // This will trigger the cached file to be store
95
                    // When cached files are store we save the file info
96
                    $this->filterService->getUrlOfFilteredImage($filename, $filter);
97
                }
98
            }
99
        }
100
    }
101
102
    public function persistFiles(object $object): void
103
    {
104
        $propertyAccessor = PropertyAccess::createPropertyAccessor();
105
        $classMetadata = $this->getClassMetadata($object);
106
107
        $configuredProperties = $this->annotationReader->getConfiguredProperties($object, true);
108
        /**
109
         * @var UploadableField[] $configuredProperties
110
         */
111
        foreach ($configuredProperties as $fileProperty => $fieldConfiguration) {
112
            $currentFilepath = $classMetadata->getFieldValue($object, $fieldConfiguration->property);
113
            if ($currentFilepath) {
114
                $this->removeFilepath($object, $fieldConfiguration);
115
            }
116
            /** @var UploadedDataUriFile|null $file */
117
            $file = $propertyAccessor->getValue($object, $fileProperty);
118
            if (!$file) {
119
                $classMetadata->setFieldValue($object, $fieldConfiguration->property, null);
120
                continue;
121
            }
122
123
            $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

123
            $filesystem = $this->filesystemProvider->getFilesystem(/** @scrutinizer ignore-type */ $fieldConfiguration->adapter);
Loading history...
124
125
            $path = $fieldConfiguration->prefix ?? '';
126
            $path .= $file->getFilename();
127
            $stream = fopen($file->getRealPath(), 'r');
128
            $filesystem->writeStream($path, $stream, [
129
                'mimetype' => $file->getMimeType(),
130
            ]);
131
            $classMetadata->setFieldValue($object, $fieldConfiguration->property, $path);
132
            $propertyAccessor->setValue($object, $fileProperty, null);
133
        }
134
    }
135
136
    public function deleteFiles(object $object): void
137
    {
138
        $classMetadata = $this->getClassMetadata($object);
139
140
        $configuredProperties = $this->annotationReader->getConfiguredProperties($object, true);
141
        foreach ($configuredProperties as $fileProperty => $fieldConfiguration) {
142
            $currentFilepath = $classMetadata->getFieldValue($object, $fieldConfiguration->property);
143
            if ($currentFilepath) {
144
                $this->removeFilepath($object, $fieldConfiguration);
145
            }
146
        }
147
    }
148
149
    public function getFileResponse(object $object, string $property, bool $forceDownload = false): Response
150
    {
151
        $reflectionProperty = new \ReflectionProperty($object, $property);
152
        if (!$this->annotationReader->isFieldConfigured($reflectionProperty)) {
153
            throw new NotFoundHttpException(sprintf('field configuration not found for %s', $property));
154
        }
155
156
        $propertyConfiguration = $this->annotationReader->getPropertyConfiguration($reflectionProperty);
157
158
        $filesystem = $this->filesystemProvider->getFilesystem($propertyConfiguration->adapter);
0 ignored issues
show
Bug introduced by
It seems like $propertyConfiguration->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

158
        $filesystem = $this->filesystemProvider->getFilesystem(/** @scrutinizer ignore-type */ $propertyConfiguration->adapter);
Loading history...
159
160
        $classMetadata = $this->getClassMetadata($object);
161
162
        $filePath = $classMetadata->getFieldValue($object, $propertyConfiguration->property);
163
164
        $response = new StreamedResponse();
165
        $response->setCallback(
166
            static function () use ($filesystem, $filePath) {
167
                $outputStream = fopen('php://output', 'w');
168
                $fileStream = $filesystem->readStream($filePath);
169
                stream_copy_to_stream($fileStream, $outputStream);
0 ignored issues
show
Bug introduced by
It seems like $outputStream can also be of type false; however, parameter $dest of stream_copy_to_stream() does only seem to accept resource, 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

169
                stream_copy_to_stream($fileStream, /** @scrutinizer ignore-type */ $outputStream);
Loading history...
170
            }
171
        );
172
        $response->headers->set('Content-Type', $filesystem->mimeType($filePath));
173
174
        if ($forceDownload) {
175
            $disposition = HeaderUtils::makeDisposition(
176
                HeaderUtils::DISPOSITION_ATTACHMENT,
177
                $filePath
178
            );
179
            $response->headers->set('Content-Disposition', $disposition);
180
        }
181
182
        return $response;
183
    }
184
185
    private function removeFilepath(object $object, UploadableField $fieldConfiguration): void
186
    {
187
        $classMetadata = $this->getClassMetadata($object);
188
189
        $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

189
        $filesystem = $this->filesystemProvider->getFilesystem(/** @scrutinizer ignore-type */ $fieldConfiguration->adapter);
Loading history...
190
        $currentFilepath = $classMetadata->getFieldValue($object, $fieldConfiguration->property);
191
        $this->fileInfoCacheHelper->deleteCaches([$currentFilepath], [null]);
192
        if ($this->imagineCacheManager) {
193
            $this->imagineCacheManager->remove([$currentFilepath], null);
194
        }
195
        if ($filesystem->fileExists($currentFilepath)) {
196
            $filesystem->delete($currentFilepath);
197
        }
198
    }
199
}
200