Passed
Push — master ( 179271...9dc280 )
by Aimeos
04:18
created

Preview::createPreviews()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 104
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 17
c 1
b 0
f 0
dl 0
loc 104
rs 9.7
cc 3
nc 3
nop 3

How to fix   Long Method   

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
/**
4
 * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2023
6
 * @package MShop
7
 * @subpackage Media
8
 */
9
10
11
namespace Aimeos\MShop\Media\Manager;
12
13
use \Intervention\Image\Interfaces\ImageInterface;
0 ignored issues
show
Bug introduced by
The type \Intervention\Image\Interfaces\ImageInterface 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...
14
15
16
/**
17
 * Media preview trait
18
 *
19
 * @package MShop
20
 * @subpackage Media
21
 */
22
trait Preview
23
{
24
	/**
25
	 * Creates scaled images according to the configuration settings
26
	 *
27
	 * @param \Intervention\Image\Interfaces\ImageInterface $image Media object
28
	 * @param string $domain Domain the item is from, e.g. product, catalog, etc.
29
	 * @param string $type Type of the item within the given domain, e.g. default, stage, etc.
30
	 * @return \Intervention\Image\Interfaces\ImageInterface[] Associative list of image width as keys and scaled media object as values
31
	 */
32
	protected function createPreviews( ImageInterface $image, string $domain, string $type ) : array
33
	{
34
		$list = [];
35
		$config = $this->context()->config();
0 ignored issues
show
Bug introduced by
It seems like context() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

35
		$config = $this->/** @scrutinizer ignore-call */ context()->config();
Loading history...
36
37
		/** mshop/media/manager/previews/common
38
		 * Scaling options for preview images
39
		 *
40
		 * For responsive images, several preview images of different sizes are
41
		 * generated. This setting controls how many preview images are generated,
42
		 * what's their maximum width and height and if the given width/height is
43
		 * enforced by cropping images that doesn't fit.
44
		 *
45
		 * The setting must consist of a list image size definitions like:
46
		 *
47
		 *  [
48
		 *    ['maxwidth' => 240, 'maxheight' => 320, 'force-size' => true],
49
		 *    ['maxwidth' => 720, 'maxheight' => 960, 'force-size' => false],
50
		 *    ['maxwidth' => 2160, 'maxheight' => 2880, 'force-size' => false],
51
		 *  ]
52
		 *
53
		 * "maxwidth" sets the maximum allowed width of the image whereas
54
		 * "maxheight" does the same for the maximum allowed height. If both
55
		 * values are given, the image is scaled proportionally so it fits into
56
		 * the box defined by both values. In case the image has different
57
		 * proportions than the specified ones and "force-size" is false, the
58
		 * image is resized to fit entirely into the specified box. One side of
59
		 * the image will be shorter than it would be possible by the specified
60
		 * box.
61
		 *
62
		 * If "force-size" is true, scaled images that doesn't fit into the
63
		 * given maximum width/height are centered and then cropped. By default,
64
		 * images aren't cropped.
65
		 *
66
		 * The values for "maxwidth" and "maxheight" can also be null or not
67
		 * used. In that case, the width or height or both is unbound. If none
68
		 * of the values are given, the image won't be scaled at all. If only
69
		 * one value is set, the image will be scaled exactly to the given width
70
		 * or height and the other side is scaled proportionally.
71
		 *
72
		 * You can also define different preview sizes for different domains (e.g.
73
		 * for catalog images) and for different types (e.g. catalog stage images).
74
		 * Use configuration settings like
75
		 *
76
		 *  mshop/media/manager/previews/previews/<domain>/
77
		 *  mshop/media/manager/previews/previews/<domain>/<type>/
78
		 *
79
		 * for example:
80
		 *
81
		 *  mshop/media/manager/previews/catalog/previews => [
82
		 *    ['maxwidth' => 240, 'maxheight' => 320, 'force-size' => true],
83
		 *  ]
84
		 *  mshop/media/manager/previews/catalog/previews => [
85
		 *    ['maxwidth' => 400, 'maxheight' => 300, 'force-size' => false]
86
		 *  ]
87
		 *  mshop/media/manager/previews/catalog/stage/previews => [
88
		 *    ['maxwidth' => 360, 'maxheight' => 320, 'force-size' => true],
89
		 *    ['maxwidth' => 720, 'maxheight' => 480, 'force-size' => true]
90
		 *  ]
91
		 *
92
		 * These settings will create two preview images for catalog stage images,
93
		 * one with a different size for all other catalog images and all images
94
		 * from other domains will be sized to 240x320px. The available domains
95
		 * which can have images are:
96
		 *
97
		 * * attribute
98
		 * * catalog
99
		 * * product
100
		 * * service
101
		 * * supplier
102
		 *
103
		 * There are a few image types included per domain ("default" is always
104
		 * available). You can also add your own types in the admin backend and
105
		 * extend the frontend to display them where you need them.
106
		 *
107
		 * @param array List of image size definitions
108
		 * @category Developer
109
		 * @category User
110
		 * @since 2019.07
111
		 */
112
		$previews = $config->get( 'mshop/media/manager/previews/common', [] );
113
		$previews = $config->get( 'mshop/media/manager/previews/' . $domain, $previews );
114
		$previews = $config->get( 'mshop/media/manager/previews/' . $domain . '/' . $type, $previews );
115
116
		foreach( $previews as $entry )
117
		{
118
			$force = $entry['force-size'] ?? 0;
119
			$maxwidth = $entry['maxwidth'] ?? null;
120
			$maxheight = $entry['maxheight'] ?? null;
121
			$bg = ltrim( $entry['background'] ?? 'ffffffff', '#' );
122
123
			if( $this->call( 'filterPreviews', $image, $domain, $type, $maxwidth, $maxheight, $force ) )
0 ignored issues
show
Bug introduced by
It seems like call() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

123
			if( $this->/** @scrutinizer ignore-call */ call( 'filterPreviews', $image, $domain, $type, $maxwidth, $maxheight, $force ) )
Loading history...
124
			{
125
				$file = match( $force ) {
126
					0 => $image->scaleDown( $maxwidth, $maxheight ),
127
					1 => $image->pad( $maxwidth, $maxheight, $bg, 'center' ),
128
					2 => $image->cover( $maxwidth, $maxheight )
129
				};
130
131
				$list[$file->width()] = $file;
132
			}
133
		}
134
135
		return $list;
136
	}
137
138
139
	/**
140
	 * Removes the previes images from the storage
141
	 *
142
	 * @param \Aimeos\MShop\Media\Item\Iface $item Media item which will contains the image URLs afterwards
143
	 * @param array List of preview paths to remove
144
	 * @return \Aimeos\MShop\Media\Item\Iface Media item with preview images removed
145
	 */
146
	protected function deletePreviews( \Aimeos\MShop\Media\Item\Iface $item, array $paths ) : \Aimeos\MShop\Media\Item\Iface
147
	{
148
		if( !empty( $paths = $this->call( 'removePreviews', $item, $paths ) ) )
149
		{
150
			$fs = $this->context()->fs( $item->getFileSystem() );
151
152
			foreach( $paths as $preview )
153
			{
154
				if( $preview && $fs->has( $preview ) ) {
155
					$fs->rm( $preview );
156
				}
157
			}
158
		}
159
160
		return $item;
161
	}
162
163
164
	/**
165
	 * Tests if the preview image should be created
166
	 *
167
	 * @param \Intervention\Image\Interfaces\ImageInterface $image Media object
168
	 * @param string $domain Domain the item is from, e.g. product, catalog, etc.
169
	 * @param string $type Type of the item within the given domain, e.g. default, stage, etc.
170
	 * @param int|null $width New width of the image or null for automatic calculation
171
	 * @param int|null $height New height of the image or null for automatic calculation
172
	 * @param int $fit "0" keeps image ratio, "1" adds padding while "2" crops image to enforce image size
173
	 */
174
	protected function filterPreviews( ImageInterface $image, string $domain, string $type,
175
		?int $maxwidth, ?int $maxheight, int $force ) : bool
176
	{
177
		return true;
178
	}
179
180
181
	/**
182
	 * Returns the preview images to be deleted
183
	 *
184
	 * @param \Aimeos\MShop\Media\Item\Iface $item Media item with new preview URLs
185
	 * @param array List of preview paths to remove
186
	 * @return iterable List of preview URLs to remove
187
	 */
188
	protected function removePreviews( \Aimeos\MShop\Media\Item\Iface $item, array $paths ) : iterable
189
	{
190
		$previews = $item->getPreviews();
191
192
		// don't delete first (smallest) image because it may be referenced in past orders
193
		if( $item->getDomain() === 'product' && in_array( key( $previews ), $paths ) ) {
194
			return array_slice( $paths, 1 );
195
		}
196
197
		return $paths;
198
	}
199
}
200