Completed
Push — dependabot/npm_and_yarn/vue-se... ( e78fac...845b60 )
by
unknown
358:07 queued 338:52
created

UploadMedia::handleFiles()   B

Complexity

Conditions 10
Paths 11

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 10.0454

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 12
c 1
b 1
f 0
dl 0
loc 23
ccs 12
cts 13
cp 0.9231
rs 7.6666
cc 10
nc 11
nop 6
crap 10.0454

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Thinktomorrow\Chief\Media;
4
5
use Illuminate\Http\UploadedFile;
6
use Spatie\MediaLibrary\HasMedia\HasMedia;
7
use Thinktomorrow\AssetLibrary\Models\Asset;
8
use Thinktomorrow\AssetLibrary\Models\AssetUploader;
9
10
class UploadMedia
11
{
12
    /**
13
     * Upload from base64encoded files, usually
14
     * coming from slim upload component
15
     *
16
     * @param HasMedia $model
17
     * @param array $files_by_type
18
     * @param array $files_order_by_type
19
     */
20 76
    public function fromUploadComponent(HasMedia $model, array $files_by_type, array $files_order_by_type)
21
    {
22 76
        $files_by_type = $this->sanitizeFilesParameter($files_by_type);
23 76
        $files_order_by_type = $this->sanitizeFilesOrderParameter($files_order_by_type);
24 76
        $this->validateParameters($files_by_type, $files_order_by_type);
25
26
        // When no files are uploaded, we still would like to sort our assets duh
27 76
        if (empty($files_by_type)) {
28 64
            foreach($files_order_by_type as $type => $fileIdsCollection){
29 2
                $this->sortFiles($model, $type, $fileIdsCollection);
30
            }
31
32 64
            return;
33
        }
34
35 12
        foreach ($files_by_type as $type => $files) {
36 12
37 12
            foreach($files as $locale => $files)
0 ignored issues
show
Comprehensibility Bug introduced by
$files is overwriting a variable from outer foreach loop.
Loading history...
38
            {
39 12
                $this->validateFileUploads($files);
40
41 12
                $fileIdsCollection = $files_order_by_type[$type] ?? [];
42 12
43 12
                $this->addFiles($model, $type, $files, $fileIdsCollection, $locale);
44
                $this->replaceFiles($model, $files);
45 12
                $this->removeFiles($model, $files);
46
47
                $this->sortFiles($model, $type, $fileIdsCollection);
48 12
            }
49
        }
50 10
    }
51
52 10
    private function addFile(HasMedia $model, string $type, array &$files_order, $file, $locale = null)
53 7
    {
54 7
        if (is_string($file)) {
55
            $image_name = json_decode($file)->output->name;
56 4
            $asset      = $this->addAsset(json_decode($file)->output->image, $type, $locale, $image_name, $model);
57 4
        } else {
58
            $image_name = $file->getClientOriginalName();
59
            $asset      = $this->addAsset($file, $type, $locale, $image_name, $model);
60
        }
61
62 10
        // New files are passed with their filename (instead of their id)
63
        // For new files we will replace the filename with the id.
64
        if (false !== ($key = array_search($image_name, $files_order))) {
65 10
            $files_order[$key] = $asset->id;
66
        }
67
    }
68
69
    /**
70
     * Note: this is a replication of the AssetTrait::addFile() with the exception
71
     * that we want to return the asset in order to retrieve the id. This is
72 10
     * currently not available via the AssetTrait.
73
     */
74 10
    private function addAsset($file, $type = '', $locale = null, $filename = null, HasMedia $model)
75
    {
76 10
        $filename = $this->sluggifyFilename($filename);
77 7
78
        if (is_string($file)) {
79 4
            $asset = AssetUploader::uploadFromBase64($file, $filename);
80
        } else {
81
            $asset = AssetUploader::upload($file, $filename);
82 10
        }
83 10
84
        if ($asset instanceof Asset) {
85
            $asset->attachToModel($model, $type, $locale);
86 10
        }
87
88
        return $asset;
89 12
    }
90
91 12
    private function addFiles(HasMedia $model, string $type, array $files, array &$files_order, string $locale = null)
92 12
    {
93
        $this->handleFiles('new', $model, $type, $files, $files_order, $locale);
94
    }
95
96
    /**
97
     * @param HasMedia $model
98 12
     * @param array $files
99
     */
100 12
    private function replaceFiles(HasMedia $model, array $files)
101 12
    {
102
        $this->handleFiles('replace', $model, null, $files, []);
103
    }
104
105
    /**
106
     * @param HasMedia $model
107 12
     * @param array $files
108
     */
109 12
    private function removeFiles(HasMedia $model, array $files)
110 12
    {
111
        $this->handleFiles('delete', $model, null, $files, []);
112 12
    }
113
114 12
    private function handleFiles(string $action, HasMedia $model, string $type = null, array $files, array $files_order = [], string $locale = null)
115 12
    {
116 3
        if (isset($files[$action]) && is_array($files[$action]) && !empty($files[$action])) {
117 3
            if($action == 'delete')
118
            {
119
                foreach ($model->assets()->whereIn('id', $files[$action])->get() as $asset) {
120
                    $asset->delete();
121 12
                }
122 12
            }
123
124
            foreach ($files[$action] as $id => $file) {
125
126 12
                if (!$file) {
127 10
                    continue;
128 5
                }
129 2
130 12
                if($action == 'new')
131
                {
132
                    $this->addFile($model, $type, $files_order, $file, $locale);
0 ignored issues
show
Bug introduced by
It seems like $type can also be of type null; however, parameter $type of Thinktomorrow\Chief\Media\UploadMedia::addFile() 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

132
                    $this->addFile($model, /** @scrutinizer ignore-type */ $type, $files_order, $file, $locale);
Loading history...
133
                }elseif($action == 'replace')
134 12
                {
135
                    $asset = AssetUploader::uploadFromBase64(json_decode($file)->output->image, json_decode($file)->output->name);
136
                    $model->replaceAsset($id, $asset->id);
137
                }
138
            }
139
        }
140 10
    }
141
142 10
    /**
143 10
     * @param $filename
144 10
     * @return string
145
     */
146 10
    private function sluggifyFilename($filename): string
147
    {
148
        $extension = substr($filename, strrpos($filename, '.') + 1);
149
        $filename  = substr($filename, 0, strrpos($filename, '.'));
150
        $filename  = str_slug($filename) . '.' . $extension;
0 ignored issues
show
Deprecated Code introduced by
The function str_slug() has been deprecated: Str::slug() should be used directly instead. Will be removed in Laravel 5.9. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

150
        $filename  = /** @scrutinizer ignore-deprecated */ str_slug($filename) . '.' . $extension;

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
151
152
        return $filename;
153 12
    }
154
155 12
    /**
156 12
     * @param $files
157 12
     * @throws FileTooBigException
158
     */
159
    private function validateFileUploads($files): void
160
    {
161
        foreach ($files as $_files) {
162 12
            foreach ($_files as $file) {
163
                if ($file instanceof UploadedFile && !$file->isValid()) {
164
                    if ($file->getError() == UPLOAD_ERR_INI_SIZE) {
165
                        throw new FileTooBigException(
0 ignored issues
show
Bug introduced by
The type Thinktomorrow\Chief\Media\FileTooBigException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
166
                            'Cannot upload file because it exceeded the allowed upload_max_filesize: upload_max_filesize is smaller than post size. ' .
167
                            'upload_max_filesize: ' . (int)ini_get('upload_max_filesize') . 'MB, ' .
168 12
                            'post_max_size: ' . (int)(ini_get('post_max_size')) . 'MB'
169
                        );
170 76
                    }
171
                }
172 76
            }
173
        }
174 76
    }
175 12
176 12
    private function validateParameters(array $files_by_type, array $files_order_by_type)
177
    {
178
        $actions = ['new', 'replace', 'delete'];
179
180 12
        foreach($files_by_type as $type => $files)
181
        {
182
            foreach($files as $locale => $_files){
183
                if(!in_array($locale, config('translatable.locales'))) {
184 12
                    throw new \InvalidArgumentException('Corrupt file payload. key is expected to be a valid locale [' . implode(',', config('translatable.locales', [])). ']. Instead [' . $locale . '] is given.');
185 12
                }
186 12
187
                if(!is_array($_files)) {
188
                    throw new \InvalidArgumentException('A valid files entry should be an array of files, key with either [new, replace or delete]. Instead a ' . gettype($_files) . ' is given.');
189
                }
190
191
                foreach($_files as $action => $file) {
192 76
                    if(!in_array($action, $actions)) {
193 2
                        throw new \InvalidArgumentException('A valid files entry should have a key of either ['.implode(',', $actions).']. Instead ' . $action . ' is given.');
194 2
                    }
195 2
                }
196
            }
197
        }
198
199 76
        foreach($files_order_by_type as $type => $fileIdsCollection)
200
        {
201 76
            foreach($fileIdsCollection as $locale => $commaSeparatedFileIds){
202
203 76
                if(!in_array($locale, config('translatable.locales'))) {
204
                    throw new \InvalidArgumentException('Corrupt file payload. key for the file order is expected to be a valid locale [' . implode(',', config('translatable.locales', [])). ']. Instead [' . $locale . '] is given.');
205 76
                }
206 12
            }
207 12
        }
208 9
    }
209
210 9
    private function sanitizeFilesParameter(array $files_by_type): array
211 9
    {
212
        $defaultLocale = config('app.fallback_locale');
213
214 12
        foreach($files_by_type as $type => $files)
215
        {
216
            foreach($files as $locale => $_files){
217
                if(!in_array($locale, config('translatable.locales'))) {
218
                    unset($files_by_type[$type][$locale]);
219 76
220
                    if(!isset($files_by_type[$type][$defaultLocale])) {
221
                        $files_by_type[$type][$defaultLocale] = [];
222 76
                    }
223
224 76
                    $files_by_type[$type][$defaultLocale][$locale] = $_files;
225
                }
226 76
            }
227 2
        }
228 1
229 1
        return $files_by_type;
230
    }
231
232 2
    private function sanitizeFilesOrderParameter(array $files_order_by_type): array
233 2
    {
234
        $defaultLocale = config('app.fallback_locale');
235
236
        foreach($files_order_by_type as $type => $fileIdsCollection)
237 76
        {
238
            if(!is_array($fileIdsCollection)) {
239
                $fileIdsCollection = [$defaultLocale => $fileIdsCollection];
240 14
                $files_order_by_type[$type] = $fileIdsCollection;
241
            }
242 14
243
            foreach($fileIdsCollection as $locale => $commaSeparatedFileIds){
244 14
                $files_order_by_type[$type][$locale] = explode(',', $commaSeparatedFileIds);
245 2
            }
246
        }
247
248 14
        return $files_order_by_type;
249 14
    }
250
251 14
    private function sortFiles(HasMedia $model, string $type, array $fileIdsCollection)
252
    {
253 14
        $sortedFileIds = [];
254
255 14
        foreach($fileIdsCollection as $locale => $fileIds) {
256 13
            $sortedFileIds = array_merge($sortedFileIds, $fileIds);
257 13
        }
258
259 13
        $this->sortingAssetsByType($model, $type, $sortedFileIds);
260
    }
261 14
262
    private function sortingAssetsByType(HasMedia $model, $type, array $sortedAssetIds)
263
    {
264
        $assets = $model->assets()->where('asset_pivots.type', $type)->get();
265
266
        foreach($assets as $asset)
267
        {
268
            $pivot = $asset->pivot;
269
            $pivot->order = array_search($asset->id, $sortedAssetIds);
270
271
            $pivot->save();
272
        }
273
    }
274
}
275