Passed
Push — dependabot/npm_and_yarn/@vue/t... ( 1a0b23...9776fc )
by
unknown
84:45 queued 64:29
created

UploadMedia::validateParameters()   B

Complexity

Conditions 10
Paths 19

Size

Total Lines 29
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 0
Metric Value
eloc 14
c 0
b 0
f 0
dl 0
loc 29
ccs 0
cts 0
cp 0
rs 7.6666
cc 10
nc 19
nop 2
crap 110

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 50
     * @param HasMedia $model
17
     * @param array $files_by_type
18
     * @param array $files_order_by_type
19 50
     */
20 41
    public function fromUploadComponent(HasMedia $model, array $files_by_type, array $files_order_by_type)
21 1
    {
22
        $files_by_type = $this->sanitizeFilesParameter($files_by_type);
23
        $files_order_by_type = $this->sanitizeFilesOrderParameter($files_order_by_type);
24 41
        $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
        if (empty($files_by_type)) {
28 9
            foreach($files_order_by_type as $type => $fileIdsCollection){
29 9
                $this->sortFiles($model, $type, $fileIdsCollection);
30 9
            }
31
32 9
            return;
33 9
        }
34 9
35 9
        foreach ($files_by_type as $type => $files) {
36
37 9
            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 9
                $this->validateFileUploads($files);
40
41 9
                $fileIdsCollection = $files_order_by_type[$type] ?? [];
42
43 9
                $this->addFiles($model, $type, $files, $fileIdsCollection, $locale);
44 7
                $this->replaceFiles($model, $files);
45
                $this->removeFiles($model, $files);
46 7
47
                $this->sortFiles($model, $type, $fileIdsCollection);
48
            }
49
        }
50 7
    }
51
52
    private function addFile(HasMedia $model, string $type, array &$files_order, $file, $locale = null)
53 9
    {
54
        if (is_string($file)) {
55 7
            $image_name = json_decode($file)->output->name;
56
            $asset      = $this->addAsset(json_decode($file)->output->image, $type, $locale, $image_name, $model);
57 7
        } else {
58 4
            $image_name = $file->getClientOriginalName();
59 4
            $asset      = $this->addAsset($file, $type, $locale, $image_name, $model);
60
        }
61 4
62 4
        // 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
            $files_order[$key] = $asset->id;
66
        }
67 7
    }
68
69
    /**
70 7
     * 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
     * currently not available via the AssetTrait.
73
     */
74
    private function addAsset($file, $type = '', $locale = null, $filename = null, HasMedia $model)
75
    {
76
        $filename = $this->sluggifyFilename($filename);
77 7
78
        if (is_string($file)) {
79 7
            $asset = AssetUploader::uploadFromBase64($file, $filename);
80
        } else {
81 7
            $asset = AssetUploader::upload($file, $filename);
82 4
        }
83
84 4
        if ($asset instanceof Asset) {
85
            $asset->attachToModel($model, $type, $locale);
86
        }
87 7
88 7
        return $asset;
89
    }
90
91 7
    private function addFiles(HasMedia $model, string $type, array $files, array &$files_order, string $locale = null)
92
    {
93
        $this->handleFiles('new', $model, $type, $files, $files_order, $locale);
94
    }
95
96
    /**
97
     * @param HasMedia $model
98
     * @param array $files
99 9
     */
100
    private function replaceFiles(HasMedia $model, array $files)
101 9
    {
102 1
        $this->handleFiles('replace', $model, null, $files, []);
103
    }
104 1
105
    /**
106
     * @param HasMedia $model
107
     * @param array $files
108 1
     */
109 1
    private function removeFiles(HasMedia $model, array $files)
110
    {
111
        $this->handleFiles('delete', $model, null, $files, []);
112 9
    }
113
114 9
    private function handleFiles(string $action, HasMedia $model, string $type = null, array $files, array $files_order = [], string $locale = null)
115
    {
116 9
        if (isset($files[$action]) && is_array($files[$action]) && !empty($files[$action])) {
117 2
            if($action == 'delete')
118 2
            {
119
                foreach ($model->assets()->whereIn('id', $files[$action])->get() as $asset) {
120
                    $asset->delete();
121 9
                }
122
            }
123
124
            foreach ($files[$action] as $id => $file) {
125
126
                if (!$file) {
127 7
                    continue;
128
                }
129 7
130 7
                if($action == 'new')
131 7
                {
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 7
                }elseif($action == 'replace')
134
                {
135
                    $asset = AssetUploader::uploadFromBase64(json_decode($file)->output->image, json_decode($file)->output->name);
136
                    $model->replaceAsset($id, $asset->id);
137
                }
138
            }
139
        }
140 9
    }
141
142 9
    /**
143 9
     * @param $filename
144 9
     * @return string
145
     */
146
    private function sluggifyFilename($filename): string
147
    {
148
        $extension = substr($filename, strrpos($filename, '.') + 1);
149 9
        $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
    }
154
155 9
    /**
156
     * @param $files
157
     * @throws FileTooBigException
158
     */
159
    private function validateFileUploads($files): void
160
    {
161
        foreach ($files as $_files) {
162
            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
                            'post_max_size: ' . (int)(ini_get('post_max_size')) . 'MB'
169
                        );
170
                    }
171
                }
172
            }
173
        }
174
    }
175
176
    private function validateParameters(array $files_by_type, array $files_order_by_type)
177
    {
178
        $actions = ['new', 'replace', 'delete'];
179
180
        foreach($files_by_type as $type => $files)
181
        {
182
            foreach($files as $locale => $_files){
183
                if(!in_array($locale, config('translatable.locales'))) {
184
                    throw new \InvalidArgumentException('Corrupt file payload. key is expected to be a valid locale [' . implode(',', config('translatable.locales', [])). ']. Instead [' . $locale . '] is given.');
185
                }
186
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
                    if(!in_array($action, $actions)) {
193
                        throw new \InvalidArgumentException('A valid files entry should have a key of either ['.implode(',', $actions).']. Instead ' . $action . ' is given.');
194
                    }
195
                }
196
            }
197
        }
198
199
        foreach($files_order_by_type as $type => $fileIdsCollection)
200
        {
201
            foreach($fileIdsCollection as $locale => $commaSeparatedFileIds){
202
203
                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
                }
206
            }
207
        }
208
    }
209
210
    private function sanitizeFilesParameter(array $files_by_type): array
211
    {
212
        $defaultLocale = config('app.fallback_locale');
213
214
        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
220
                    if(!isset($files_by_type[$type][$defaultLocale])) {
221
                        $files_by_type[$type][$defaultLocale] = [];
222
                    }
223
224
                    $files_by_type[$type][$defaultLocale][$locale] = $_files;
225
                }
226
            }
227
        }
228
229
        return $files_by_type;
230
    }
231
232
    private function sanitizeFilesOrderParameter(array $files_order_by_type): array
233
    {
234
        $defaultLocale = config('app.fallback_locale');
235
236
        foreach($files_order_by_type as $type => $fileIdsCollection)
237
        {
238
            if(!is_array($fileIdsCollection)) {
239
                $fileIdsCollection = [$defaultLocale => $fileIdsCollection];
240
                $files_order_by_type[$type] = $fileIdsCollection;
241
            }
242
243
            foreach($fileIdsCollection as $locale => $commaSeparatedFileIds){
244
                $files_order_by_type[$type][$locale] = explode(',', $commaSeparatedFileIds);
245
            }
246
        }
247
248
        return $files_order_by_type;
249
    }
250
251
    private function sortFiles(HasMedia $model, string $type, array $fileIdsCollection)
252
    {
253
        $sortedFileIds = [];
254
255
        foreach($fileIdsCollection as $locale => $fileIds) {
256
            $sortedFileIds = array_merge($sortedFileIds, $fileIds);
257
        }
258
259
        $this->sortingAssetsByType($model, $type, $sortedFileIds);
260
    }
261
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