Passed
Push — master ( b6337b...c70fa1 )
by Yaro
14:32
created

Image::storeFile()   B

Complexity

Conditions 10
Paths 96

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 110

Importance

Changes 0
Metric Value
dl 0
loc 31
ccs 0
cts 26
cp 0
rs 7.6666
c 0
b 0
f 0
cc 10
nc 96
nop 8
crap 110

How to fix   Complexity    Many Parameters   

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:

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
namespace Yaro\Jarboe\Table\Fields;
4
5
use Illuminate\Http\Request;
6
use Illuminate\Http\UploadedFile;
7
use Illuminate\Support\Facades\Storage as IlluminateStorage;
8
use Illuminate\Support\Str;
9
use Intervention\Image\ImageManagerStatic as InterventionImage;
10
use Yaro\Jarboe\Table\Fields\Traits\Filename;
11
use Yaro\Jarboe\Table\Fields\Traits\Nullable;
12
use Yaro\Jarboe\Table\Fields\Traits\Placeholder;
13
use Yaro\Jarboe\Table\Fields\Traits\Storage;
14
15
class Image extends AbstractField
16
{
17
    use Storage;
18
    use Placeholder;
19
    use Nullable;
20
    use Filename;
21
22
    protected $encode = false;
23
    protected $crop = false;
24
    protected $shouldAutoOpen = null;
25
    protected $ratio = [
26
        'width'  => false,
27
        'height' => false,
28
    ];
29
    private $originalImageData;
30
    private $defaultImageDataStructure =  [
31
        'storage' => [
32
            'disk' => null,
33
            'is_encoded' => false,
34
        ],
35
        'crop' => [
36
            'width' => null,
37
            'height' => null,
38
            'x' => null,
39
            'y' => null,
40
            'rotate' => null,
41
            'rotate_background' => null,
42
        ],
43
        'sources' => [
44
            'original' => null,
45
            'cropped' => null,
46
        ],
47
    ];
48
49
    /**
50
     * @var int
51
     */
52
    private $quality = 100;
53
54
    public function __construct()
55
    {
56
        $this->disk = config('filesystems.default');
57
    }
58
59
    public function isEncode()
60
    {
61
        return $this->encode;
62
    }
63
64
    /**
65
     * Encode image as data-url.
66
     *
67
     * @param bool $encode
68
     * @return $this
69
     */
70
    public function encode(bool $encode = true)
71
    {
72
        $this->encode = $encode;
73
74
        return $this;
75
    }
76
77
    protected function storeFile($filepath, $filename, $width = null, $height = null, $x = null, $y = null, $rotate = null, $rotateBackgroundColor = null)
78
    {
79
        $image = InterventionImage::make($filepath);
80
        $rotateBackgroundColor = $rotateBackgroundColor ?: 'rgba(255, 255, 255, 0)';
81
82
        if ($rotate) {
83
            // because js plugin and php library rotating in different directions.
84
            $angle = $rotate * -1;
85
            $image->rotate($angle, $rotateBackgroundColor);
86
        }
87
        $hasCropProperties = !is_null($width) && !is_null($height) && !is_null($x) && !is_null($y);
88
        if ($this->isCrop() && $hasCropProperties) {
89
            $image->crop(round($width), round($height), round($x), round($y));
90
        }
91
92
        if ($this->isEncode()) {
93
            return (string) $image->encode('data-url', $this->getQuality());
94
        }
95
96
        $format = '';
97
        if ($this->isTransparentColor($rotateBackgroundColor)) {
98
            $format = 'png';
99
        }
100
        $path = trim($this->getPath() .'/'. $filename, '/');
101
        IlluminateStorage::disk($this->getDisk())->put(
102
            $path,
103
            (string) $image->encode($format, $this->getQuality())
104
        );
105
106
        return $path;
107
    }
108
109
    public function value(Request $request)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
110
    {
111
        $images = $request->all($this->name());
112
        $images = $images[$this->name()];
113
114
        $data = [];
115
        foreach ($images as $image) {
116
            $imageData = $this->defaultImageDataStructure;
117
            $imageData['storage'] = [
118
                'disk' => $this->getDisk(),
119
                'is_encoded' => $this->isEncode(),
120
            ];
121
122
            $imageData['crop'] = $image['crop'];
123
            $imageData['sources'] = $image['sources'];
124
            /** @var UploadedFile|null $file */
125
            $file = $image['file'] ?? null;
126
            if ($file) {
127
                $imageData['sources']['original'] = $this->storeFile(
128
                    $file->getRealPath(),
129
                    $this->generateFilename(
130
                        $request,
131
                        $file,
132
                        $image,
133
                        $this->isTransparentColor((string) $image['crop']['rotate_background']),
134
                        true
135
                    )
136
                );
137
                $imageData['sources']['cropped'] = $this->storeFile(
138
                    $file->getRealPath(),
139
                    $this->generateFilename(
140
                        $request,
141
                        $file,
142
                        $image,
143
                        $this->isTransparentColor((string) $image['crop']['rotate_background']),
144
                        false
145
                    ),
146
                    $image['crop']['width'],
147
                    $image['crop']['height'],
148
                    $image['crop']['x'],
149
                    $image['crop']['y'],
150
                    $image['crop']['rotate'],
151
                    $image['crop']['rotate_background']
152
                );
153
            } elseif (!$image['sources']['original']) {
154
                continue;
155
            } elseif ($this->isCrop() && !$image['sources']['cropped']) {
156
                $imageData['sources']['cropped'] = $this->storeFile(
157
                    IlluminateStorage::disk($this->getDisk())->path($imageData['sources']['original']),
158
                    $this->generateFilename(
159
                        $request,
160
                        null,
161
                        $image,
162
                        $this->isTransparentColor((string) $image['crop']['rotate_background']),
163
                        false
164
                    ),
165
                    $image['crop']['width'],
166
                    $image['crop']['height'],
167
                    $image['crop']['x'],
168
                    $image['crop']['y'],
169
                    $image['crop']['rotate'],
170
                    $image['crop']['rotate_background']
171
                );
172
            }
173
174
            $data[] = $imageData;
175
        }
176
177
        if (!$this->isMultiple()) {
178
            $data = array_pop($data);
179
        }
180
181
        $data = $data ?: [];
182
        if ($this->isNullable()) {
183
            $data = $data ?: null;
184
        }
185
186
        return $data;
187
    }
188
189
    public function crop(bool $enabled = true)
190
    {
191
        $this->crop = $enabled;
192
193
        return $this;
194
    }
195
196
    public function ratio(int $width, int $height)
197
    {
198
        $this->ratio['width'] = $width;
199
        $this->ratio['height'] = $height;
200
201
        return $this;
202
    }
203
204
    public function isCrop()
205
    {
206
        return $this->crop;
207
    }
208
209
    public function getRatio(string $type)
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
210
    {
211
        $type = strtolower($type);
212
213
        switch ($type) {
214
            case 'width':
215
            case 'height':
216
                return $this->ratio[$type];
217
            default:
218
                return false;
219
        }
220
    }
221
222
    public function getListView($model)
223
    {
224
        return view('jarboe::crud.fields.image.list', [
225
            'model' => $model,
226
            'field' => $this,
227
        ]);
228
    }
229
230 View Code Duplication
    public function getEditFormView($model)
231
    {
232
        $template = $this->isReadonly() ? 'readonly' : 'edit';
233
234
        return view('jarboe::crud.fields.image.'. $template, [
235
            'model' => $model,
236
            'field' => $this,
237
        ]);
238
    }
239
240
    public function getCreateFormView()
241
    {
242
        return view('jarboe::crud.fields.image.create', [
243
            'model' => null,
244
            'field' => $this,
245
        ]);
246
    }
247
248
    public function getImage($data = []): \Yaro\Jarboe\Pack\Image
249
    {
250
        return new \Yaro\Jarboe\Pack\Image($data);
251
    }
252
253
    private function isTransparentColor(string $rgbaColor)
254
    {
255
        if (!$rgbaColor) {
256
            return false;
257
        }
258
259
        $segmentsString = preg_replace('~rgba\(|\)~', '', $rgbaColor);
260
        $segments = explode(',', $segmentsString);
261
262
        $opacity = $segments[3] ?? 0;
263
264
        return $opacity < 1;
265
    }
266
267
    /**
268
     * @param Request $request
269
     * @param UploadedFile|null $file
270
     * @param array $imageData
271
     * @param bool $hasTransparentColor
272
     * @param bool $isOriginalImage
273
     * @return string
274
     */
275
    private function generateFilename(Request $request, UploadedFile $file = null, array $imageData, bool $hasTransparentColor, bool $isOriginalImage): string
276
    {
277
        $isRecropFromOriginal = !$file && !$isOriginalImage;
278
        $extension = $isRecropFromOriginal ? pathinfo($imageData['sources']['original'], PATHINFO_EXTENSION) : $file->extension();
0 ignored issues
show
Bug introduced by
It seems like $file is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
279
        $filename = Str::random(40) .'.'. $extension;
280
281
        $closure = $this->filenameClosure;
282
        if (is_callable($closure)) {
283
            $filename = $closure($file, $request, $imageData, $isOriginalImage);
284
        }
285
286
        if ($hasTransparentColor) {
287
            $regexp = '~'. preg_quote(pathinfo($filename, PATHINFO_EXTENSION)) .'$~';
288
            $filename = preg_replace($regexp, 'png', $filename);
289
        }
290
291
        return (string) $filename;
292
    }
293
294
    public function getImagesPack($model): array
295
    {
296
        $defaultImage = $this->getImage();
297
        if (!$model) {
298
            return [$defaultImage];
299
        }
300
301
        $imagesData = $this->getAttribute($model);
302
        if (!is_array($imagesData)) {
303
            $imagesData = [];
304
        }
305
306
        if (!$this->isMultiple()) {
307
            $imagesData = [$imagesData];
308
        }
309
310
311
        $pack = [];
312
        foreach ($imagesData as $imageData) {
313
            $pack[] = $this->getImage($imageData);
314
        }
315
316
        return $pack ?: [$defaultImage];
317
    }
318
319
    public function autoOpen(bool $shouldAutoOpen = true)
320
    {
321
        $this->shouldAutoOpen = $shouldAutoOpen;
322
323
        return $this;
324
    }
325
326
    public function shouldAutoOpenModal(): bool
327
    {
328
        if (!is_null($this->shouldAutoOpen)) {
329
            return $this->shouldAutoOpen;
330
        }
331
332
        return $this->isCrop();
333
    }
334
335
    public function quality(int $quality)
336
    {
337
        if ($quality < 0) {
338
            $quality = 0;
339
        }
340
        if ($quality > 100) {
341
            $quality = 100;
342
        }
343
344
        $this->quality = $quality;
345
346
        return $this;
347
    }
348
349
    /**
350
     * @return int
351
     */
352
    public function getQuality(): int
353
    {
354
        return $this->quality;
355
    }
356
}
357