Completed
Pull Request — master (#15)
by Yaro
08:09 queued 01:44
created

Image::getImagesPack()   B

Complexity

Conditions 6
Paths 17

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
dl 0
loc 24
rs 8.9137
c 0
b 0
f 0
ccs 0
cts 0
cp 0
cc 6
nc 17
nop 1
crap 42
1
<?php
2
3
namespace Yaro\Jarboe\Table\Fields;
4
5
use Illuminate\Http\Request;
6
use Illuminate\Http\UploadedFile;
7
use Illuminate\Support\Arr;
8
use Illuminate\Support\Str;
9
use Yaro\Jarboe\Table\Fields\Traits\Filename;
10
use Yaro\Jarboe\Table\Fields\Traits\Nullable;
11
use Yaro\Jarboe\Table\Fields\Traits\Placeholder;
12
use Yaro\Jarboe\Table\Fields\Traits\Storage;
13
use Illuminate\Support\Facades\Storage as IlluminateStorage;
14
use Intervention\Image\ImageManagerStatic as InterventionImage;
15
16
class Image extends AbstractField
17
{
18
    use Storage;
19
    use Placeholder;
20
    use Nullable;
21
    use Filename;
22
23
    protected $encode = false;
24
    protected $crop = false;
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
    public function __construct()
50
    {
51
        $this->disk = config('filesystems.default');
52
    }
53
54
    public function isEncode()
55
    {
56
        return $this->encode;
57
    }
58
59
    /**
60
     * Encode image as data-url.
61
     *
62
     * @param bool $encode
63
     * @return $this
64
     */
65
    public function encode(bool $encode = true)
66
    {
67
        $this->encode = $encode;
68
69
        return $this;
70
    }
71
72
    protected function storeFile($filepath, $filename, $width = null, $height = null, $x = null, $y = null, $rotate = null, $rotateBackgroundColor = null)
73
    {
74
        $image = InterventionImage::make($filepath);
75
        $rotateBackgroundColor = $rotateBackgroundColor ?: 'rgba(255, 255, 255, 0)';
76
77
        if ($rotate) {
78
            // because js plugin and php library rotating in different directions.
79
            $angle = $rotate * -1;
80
            $image->rotate($angle, $rotateBackgroundColor);
81
        }
82
        $hasCropProperties = !is_null($width) && !is_null($height) && !is_null($x) && !is_null($y);
83
        if ($this->isCrop() && $hasCropProperties) {
84
            $image->crop(round($width), round($height), round($x), round($y));
85
        }
86
87
        if ($this->isEncode()) {
88
            return (string) $image->encode('data-url');
89
        }
90
91
        $format = '';
92
        if ($this->isTransparentColor($rotateBackgroundColor)) {
93
            $format = 'png';
94
        }
95
        $path = trim($this->getPath() .'/'. $filename, '/');
96
        IlluminateStorage::disk($this->getDisk())->put(
97
            $path,
98
            (string) $image->encode($format)
99
        );
100
101
        return $path;
102
    }
103
104
    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...
105
    {
106
        $images = $request->all($this->name());
107
        $images = $images[$this->name()];
108
109
        $data = [];
110
        foreach ($images as $image) {
111
            $imageData = $this->defaultImageDataStructure;
112
            $imageData['storage'] = [
113
                'disk' => $this->getDisk(),
114
                'is_encoded' => $this->isEncode(),
115
            ];
116
117
            $imageData['crop'] = $image['crop'];
118
            $imageData['sources'] = $image['sources'];
119
            /** @var UploadedFile|null $file */
120
            $file = $image['file'] ?? null;
121
            if ($file) {
122
                $imageData['sources']['original'] = $this->storeFile(
123
                    $file->getRealPath(),
124
                    $this->generateFilename(
125
                        $request,
126
                        $file,
127
                        $image,
128
                        $this->isTransparentColor((string) $image['crop']['rotate_background']),
129
                        true
130
                    )
131
                );
132
                $imageData['sources']['cropped'] = $this->storeFile(
133
                    $file->getRealPath(),
134
                    $this->generateFilename(
135
                        $request,
136
                        $file,
137
                        $image,
138
                        $this->isTransparentColor((string) $image['crop']['rotate_background']),
139
                        false
140
                    ),
141
                    $image['crop']['width'],
142
                    $image['crop']['height'],
143
                    $image['crop']['x'],
144
                    $image['crop']['y'],
145
                    $image['crop']['rotate'],
146
                    $image['crop']['rotate_background']
147
                );
148
            } elseif (!$image['sources']['original']) {
149
                continue;
150
            } elseif ($this->isCrop() && !$image['sources']['cropped']) {
151
                $imageData['sources']['cropped'] = $this->storeFile(
152
                    IlluminateStorage::disk($this->getDisk())->path($imageData['sources']['original']),
153
                    $this->generateFilename(
154
                        $request,
155
                        null,
156
                        $image,
157
                        $this->isTransparentColor((string) $image['crop']['rotate_background']),
158
                        false
159
                    ),
160
                    $image['crop']['width'],
161
                    $image['crop']['height'],
162
                    $image['crop']['x'],
163
                    $image['crop']['y'],
164
                    $image['crop']['rotate'],
165
                    $image['crop']['rotate_background']
166
                );
167
            }
168
169
            $data[] = $imageData;
170
        }
171
172
        if (!$this->isMultiple()) {
173
            $data = array_pop($data);
174
        }
175
176
        $data = $data ?: [];
177
        if ($this->isNullable()) {
178
            $data = $data ?: null;
179
        }
180
181
        return $data;
182
    }
183
184
    public function crop(bool $enabled = true)
185
    {
186
        $this->crop = $enabled;
187
188
        return $this;
189
    }
190
191
    public function ratio(int $width, int $height)
192
    {
193
        $this->ratio['width'] = $width;
194
        $this->ratio['height'] = $height;
195
196
        return $this;
197
    }
198
199
    public function isCrop()
200
    {
201
        return $this->crop;
202
    }
203
204 View Code Duplication
    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...
205
    {
206
        $type = strtolower($type);
207
208
        switch ($type) {
209
            case 'width':
210
            case 'height':
211
                return $this->ratio[$type];
212
            default:
213
                return false;
214
        }
215
    }
216
217
    public function getListView($model)
218
    {
219
        return view('jarboe::crud.fields.image.list', [
220
            'model' => $model,
221
            'field' => $this,
222
        ]);
223
    }
224
225
    public function getEditFormView($model)
226
    {
227
        $template = $this->isReadonly() ? 'readonly' : 'edit';
228
229
        return view('jarboe::crud.fields.image.'. $template, [
230
            'model' => $model,
231
            'field' => $this,
232
        ]);
233
    }
234
235
    public function getCreateFormView()
236
    {
237
        return view('jarboe::crud.fields.image.create', [
238
            'model' => null,
239
            'field' => $this,
240
        ]);
241
    }
242
243
    public function getImage($data = []): \Yaro\Jarboe\Pack\Image
244
    {
245
        return new \Yaro\Jarboe\Pack\Image($data);
246
    }
247
248
    private function isTransparentColor(string $rgbaColor)
249
    {
250
        $segmentsString = preg_replace('~rgba\(|\)~', '', $rgbaColor);
251
        $segments = explode(',', $segmentsString);
252
253
        $opacity = $segments[3] ?? 0;
254
255
        return $opacity < 1;
256
    }
257
258
    /**
259
     * @param Request $request
260
     * @param UploadedFile|null $file
261
     * @param array $imageData
262
     * @param bool $hasTransparentColor
263
     * @param bool $isOriginalImage
264
     * @return string
265
     */
266
    private function generateFilename(Request $request, UploadedFile $file = null, array $imageData, bool $hasTransparentColor, bool $isOriginalImage): string
267
    {
268
        $isRecropFromOriginal = !$file && !$isOriginalImage;
269
        $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...
270
        $filename = Str::random(40) .'.'. $extension;
271
272
        $closure = $this->filenameClosure;
273
        if (is_callable($closure)) {
274
            $filename = $closure($file, $request, $imageData, $isOriginalImage);
275
        }
276
277
        if ($hasTransparentColor) {
278
            $regexp = '~'. preg_quote(pathinfo($filename, PATHINFO_EXTENSION)) .'$~';
279
            $filename = preg_replace($regexp, 'png', $filename);
280
        }
281
282
        return (string) $filename;
283
    }
284
285
    public function getImagesPack($model): array
286
    {
287
        $defaultImage = $this->getImage();
288
        if (!$model) {
289
            return [$defaultImage];
290
        }
291
292
        $imagesData = $this->getAttribute($model);
293
        if (!is_array($imagesData)) {
294
            $imagesData = [];
295
        }
296
297
        if (!$this->isMultiple()) {
298
            $imagesData = [$imagesData];
299
        }
300
301
302
        $pack = [];
303
        foreach ($imagesData as $imageData) {
304
            $pack[] = $this->getImage($imageData);
305
        }
306
307
        return $pack ?: [$defaultImage];
308
    }
309
}
310