Completed
Push — master ( 2ef199...b6cd70 )
by Yaro
03:16
created

Image::autoOpen()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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