Passed
Push — master ( f2fe9c...c37bd3 )
by Philippe
02:30 queued 12s
created

UploadMedia   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 217
Duplicated Lines 0 %

Test Coverage

Coverage 91.35%

Importance

Changes 13
Bugs 7 Features 0
Metric Value
wmc 48
eloc 90
c 13
b 7
f 0
dl 0
loc 217
ccs 95
cts 104
cp 0.9135
rs 8.5599

11 Methods

Rating   Name   Duplication   Size   Complexity  
A validateFileUploads() 0 10 6
A sluggifyFilename() 0 7 1
A replaceFiles() 0 13 4
A addFiles() 0 12 4
A addFile() 0 26 6
A sanitizeFilesParameter() 0 19 5
A actionExists() 0 3 3
A sanitizeFilesOrderParameter() 0 11 3
A fromUploadComponent() 0 26 5
B validateParameters() 0 16 7
A removeFiles() 0 11 4

How to fix   Complexity   

Complex Class

Complex classes like UploadMedia often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use UploadMedia, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Thinktomorrow\Chief\Media;
4
5
use Exception;
6
use Illuminate\Support\Str;
7
use Illuminate\Http\UploadedFile;
8
use Thinktomorrow\AssetLibrary\Asset;
9
use Thinktomorrow\AssetLibrary\HasAsset;
10
use Thinktomorrow\AssetLibrary\Application\AddAsset;
11
use Thinktomorrow\AssetLibrary\Application\SortAssets;
12
use Thinktomorrow\Chief\Media\DuplicateAssetException;
13
use Thinktomorrow\AssetLibrary\Application\DetachAsset;
14
use Thinktomorrow\AssetLibrary\Application\ReplaceAsset;
15
use Thinktomorrow\AssetLibrary\Application\AssetUploader;
16
17
class UploadMedia
18
{
19
    /**
20
     * Upload from base64encoded files, usually
21
     * coming from slim upload component
22
     *
23
     * @param HasAsset $model
24
     * @param array $files_by_type
25
     * @param array $files_order_by_type
26
     * @throws \Spatie\MediaLibrary\Exceptions\FileCannotBeAdded
27
     */
28 87
    public function fromUploadComponent(HasAsset $model, array $files_by_type, array $files_order_by_type)
29
    {
30 87
        ini_set('memory_limit', '256M');
31 87
        $files_by_type = $this->sanitizeFilesParameter($files_by_type);
32 87
        $files_order_by_type = $this->sanitizeFilesOrderParameter($files_order_by_type);
33 87
        $this->validateParameters($files_by_type, $files_order_by_type);
34
35
        // When no files are uploaded, we still would like to sort our assets duh
36 87
        if (empty($files_by_type)) {
37 70
            foreach ($files_order_by_type as $type => $files) {
38 2
                app(SortAssets::class)->handle($model, $type, $files);
39
            }
40
41 70
            return;
42
        }
43 17
        foreach ($files_by_type as $type => $files_by_locale) {
44 17
            foreach ($files_by_locale as $locale => $files) {
45 17
                $this->validateFileUploads($files);
46
47 17
                $fileIdsCollection = $files_order_by_type[$type] ?? [];
48
49 17
                $this->addFiles($model, $type, $files, $fileIdsCollection, $locale);
50 16
                $this->replaceFiles($model, $files);
51 16
                $this->removeFiles($model, $files, $type, $locale);
52
            }
53 16
            app(SortAssets::class)->handle($model, $type, $fileIdsCollection ?? []);
54
        }
55 16
    }
56
57 17
    private function addFiles(HasAsset $model, string $type, array $files, array &$files_order, string $locale = null)
58
    {
59 17
        if (!$this->actionExists($files, 'new')) {
60 4
            return;
61
        }
62
63 15
        foreach ($files['new'] as $id => $file) {
64 15
            if (!$file) {
65
                continue;
66
            }
67
68 15
            $this->addFile($model, $type, $file, $files_order, $locale);
69
        }
70 14
    }
71
72
    /**
73
     * @param HasAsset $model
74
     * @param array $files
75
     * @throws \Spatie\MediaLibrary\Exceptions\FileCannotBeAdded
76
     */
77 16
    private function replaceFiles(HasAsset $model, array $files)
78
    {
79 16
        if (!$this->actionExists($files, 'replace')) {
80 15
            return;
81
        }
82
83 2
        foreach ($files['replace'] as $id => $file) {
84 2
            if (!$file) {
85
                continue;
86
            }
87
88 2
            $asset = AssetUploader::uploadFromBase64(json_decode($file)->output->image, json_decode($file)->output->name);
89 2
            app(ReplaceAsset::class)->handle($model, $id, $asset->id);
90
        }
91 2
    }
92
93
    /**
94
     * @param HasAsset $model
95
     * @param array $files
96
     */
97 16
    private function removeFiles(HasAsset $model, array $files, string $type, string $locale)
98
    {
99 16
        if (!$this->actionExists($files, 'delete')) {
100 14
            return;
101
        }
102
103 3
        foreach ($files['delete'] as $id => $file) {
104 3
            if (!$file) {
105
                continue;
106
            }
107 3
            app(DetachAsset::class)->detach($model, $file, $type, $locale);
108
        }
109 3
    }
110
111 17
    private function actionExists(array $files, string $action)
112
    {
113 17
        return (isset($files[$action]) && is_array($files[$action]) && !empty($files[$action]));
114
    }
115
116 15
    private function addFile(HasAsset $model, string $type, $file, array &$files_order, $locale = null)
117
    {
118 15
        if (isset(json_decode($file)->output)) {
119 8
            $image_name = json_decode($file)->output->name;
120 8
            $asset      = app(AddAsset::class)->add($model, json_decode($file)->output->image, $type, $locale, $this->sluggifyFilename($image_name));
0 ignored issues
show
Unused Code introduced by
The assignment to $asset is dead and can be removed.
Loading history...
121
        } else {
122 8
            if ($file instanceof UploadedFile) {
123 4
                $image_name = $file->getClientOriginalName();
124 4
                $asset      = app(AddAsset::class)->add($model, $file, $type, $locale, $this->sluggifyFilename($image_name));
125
126
                // New files are passed with their filename (instead of their id)
127
                // For new files we will replace the filename with the id.
128 4
                if (false !== ($key = array_search($image_name, $files_order))) {
129 4
                    $files_order[$key] = (string) $asset->id;
130
                }
131
            } else {
132 4
                $file   = Asset::find($file);
133 4
                if (!$file) {
134
                    return;
135
                }
136
137 4
                if ($model->assetRelation()->where('asset_pivots.type', $type)->where('asset_pivots.locale', $locale)->get()->contains($file)) {
138 1
                    throw new DuplicateAssetException();
139
                }
140
141 4
                $asset  = app(AddAsset::class)->add($model, $file, $type, $locale);
142
            }
143
        }
144 15
    }
145
146
    /**
147
     * @param $filename
148
     * @return string
149
     */
150 11
    private function sluggifyFilename($filename): string
151
    {
152 11
        $extension = substr($filename, strrpos($filename, '.') + 1);
153 11
        $filename  = substr($filename, 0, strrpos($filename, '.'));
154 11
        $filename  = Str::slug($filename) . '.' . $extension;
155
156 11
        return $filename;
157
    }
158
159
    /**
160
     * @param $files
161
     * @throws FileTooBigException
162
     */
163 17
    private function validateFileUploads($files): void
164
    {
165 17
        foreach ($files as $_files) {
166 17
            foreach ($_files as $file) {
167 17
                if ($file instanceof UploadedFile && !$file->isValid()) {
168
                    if ($file->getError() == UPLOAD_ERR_INI_SIZE) {
169
                        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...
170
                            'Cannot upload file because it exceeded the allowed upload_max_filesize: upload_max_filesize is smaller than post size. ' .
171
                            'upload_max_filesize: ' . (int)ini_get('upload_max_filesize') . 'MB, ' .
172 17
                            'post_max_size: ' . (int)(ini_get('post_max_size')) . 'MB'
173
                        );
174
                    }
175
                }
176
            }
177
        }
178 17
    }
179
180 87
    private function validateParameters(array $files_by_type, array $files_order_by_type)
0 ignored issues
show
Unused Code introduced by
The parameter $files_order_by_type is not used and could be removed. ( Ignorable by Annotation )

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

180
    private function validateParameters(array $files_by_type, /** @scrutinizer ignore-unused */ array $files_order_by_type)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
181
    {
182 87
        $actions = ['new', 'replace', 'delete'];
183 87
        foreach ($files_by_type as $type => $files) {
184 17
            foreach ($files as $locale => $_files) {
185 17
                if (!in_array($locale, config('translatable.locales'))) {
186
                    throw new \InvalidArgumentException('Corrupt file payload. key is expected to be a valid locale [' . implode(',', config('translatable.locales', [])). ']. Instead [' . $locale . '] is given.');
187
                }
188
189 17
                if (!is_array($_files)) {
190
                    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.');
191
                }
192
193 17
                foreach ($_files as $action => $file) {
194 17
                    if (!in_array($action, $actions)) {
195 17
                        throw new \InvalidArgumentException('A valid files entry should have a key of either ['.implode(',', $actions).']. Instead ' . $action . ' is given.');
196
                    }
197
                }
198
            }
199
        }
200 87
    }
201
202 87
    private function sanitizeFilesParameter(array $files_by_type): array
203
    {
204 87
        $defaultLocale = config('app.fallback_locale');
205
206 87
        foreach ($files_by_type as $type => $files) {
207 17
            foreach ($files as $locale => $_files) {
208 17
                if (!in_array($locale, config('translatable.locales'))) {
209 11
                    unset($files_by_type[$type][$locale]);
210
211 11
                    if (!isset($files_by_type[$type][$defaultLocale])) {
212 11
                        $files_by_type[$type][$defaultLocale] = [];
213
                    }
214
215 17
                    $files_by_type[$type][$defaultLocale][$locale] = $_files;
216
                }
217
            }
218
        }
219
220 87
        return $files_by_type;
221
    }
222
223 87
    private function sanitizeFilesOrderParameter(array $files_order_by_locale): array
224
    {
225 87
        foreach ($files_order_by_locale as $locale => $fileIdsCollection) {
226 2
            foreach ($fileIdsCollection as $type => $commaSeparatedFileIds) {
227 2
                $type = str_replace("files-", "", $type);
228 2
                $files_order_by_type[$type][] = explode(',', $commaSeparatedFileIds);
229 2
                $files_order_by_type[$type] = collect($files_order_by_type)->flatten()->unique()->toArray();
230
            }
231
        }
232
233 87
        return $files_order_by_type ?? $files_order_by_locale;
234
    }
235
}
236