Passed
Push — master ( f0002b...c777f5 )
by Aimeos
08:04 queued 04:27
created

Standard   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 220
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 87
dl 0
loc 220
rs 10
c 0
b 0
f 0
wmc 24

4 Methods

Rating   Name   Duplication   Size   Complexity  
A checkEntry() 0 19 6
B process() 0 67 8
A update() 0 23 6
A __construct() 0 55 4
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2015-2023
6
 * @package Controller
7
 * @subpackage Common
8
 */
9
10
11
namespace Aimeos\Controller\Jobs\Common\Product\Import\Csv\Processor\Media;
12
13
14
/**
15
 * Media processor for CSV imports
16
 *
17
 * @package Controller
18
 * @subpackage Common
19
 */
20
class Standard
21
	extends \Aimeos\Controller\Jobs\Common\Product\Import\Csv\Processor\Base
22
	implements \Aimeos\Controller\Jobs\Common\Product\Import\Csv\Processor\Iface
23
{
24
	/** controller/jobs/product/import/csv/processor/media/name
25
	 * Name of the media processor implementation
26
	 *
27
	 * Use "Myname" if your class is named "\Aimeos\Controller\Jobs\Common\Product\Import\Csv\Processor\Media\Myname".
28
	 * The name is case-sensitive and you should avoid camel case names like "MyName".
29
	 *
30
	 * @param string Last part of the processor class name
31
	 * @since 2015.10
32
	 */
33
34
	private ?array $listTypes = null;
35
	private array $types = [];
36
	private array $mimes = [];
37
38
39
	/**
40
	 * Initializes the object
41
	 *
42
	 * @param \Aimeos\MShop\ContextIface $context Context object
43
	 * @param array $mapping Associative list of field position in CSV as key and domain item key as value
44
	 * @param \Aimeos\Controller\Jobs\Common\Product\Import\Csv\Processor\Iface $object Decorated processor
45
	 */
46
	public function __construct( \Aimeos\MShop\ContextIface $context, array $mapping,
47
			\Aimeos\Controller\Jobs\Common\Product\Import\Csv\Processor\Iface $object = null )
48
	{
49
		parent::__construct( $context, $mapping, $object );
50
51
		$this->mimes = array_flip( $context->config()->get( 'controller/jobs/media/extensions', [] ) );
52
		$config = $context->config();
53
54
		/** controller/jobs/product/import/csv/media/listtypes
55
		 * Names of the product list types for media that are updated or removed
56
		 *
57
		 * If you want to associate media items manually via the administration
58
		 * interface to products and don't want these to be touched during the
59
		 * import, you can specify the product list types for these media
60
		 * that shouldn't be updated or removed.
61
		 *
62
		 * @param array|null List of product list type names or null for all
63
		 * @since 2015.05
64
		 * @see controller/jobs/product/import/csv/domains
65
		 * @see controller/jobs/product/import/csv/separator
66
		 * @see controller/jobs/product/import/csv/attribute/listtypes
67
		 * @see controller/jobs/product/import/csv/catalog/listtypes
68
		 * @see controller/jobs/product/import/csv/product/listtypes
69
		 * @see controller/jobs/product/import/csv/price/listtypes
70
		 * @see controller/jobs/product/import/csv/supplier/listtypes
71
		 * @see controller/jobs/product/import/csv/text/listtypes
72
		 */
73
		$default = $config->get( 'controller/jobs/product/import/csv/processor/media/listtypes' );
74
		$this->listTypes = $config->get( 'controller/jobs/product/import/csv/media/listtypes', $default );
75
76
		if( $this->listTypes === null )
77
		{
78
			$this->listTypes = [];
79
			$manager = \Aimeos\MShop::create( $context, 'product/lists/type' );
80
81
			$search = $manager->filter()->slice( 0, 0x7fffffff );
82
			$search->setConditions( $search->compare( '==', 'product.lists.type.domain', 'media' ) );
83
84
			foreach( $manager->search( $search ) as $item ) {
85
				$this->listTypes[$item->getCode()] = $item->getCode();
86
			}
87
		}
88
		else
89
		{
90
			$this->listTypes = array_combine( $this->listTypes, $this->listTypes );
91
		}
92
93
94
		$manager = \Aimeos\MShop::create( $context, 'media/type' );
95
96
		$search = $manager->filter()->slice( 0, 0x7fffffff );
97
		$search->setConditions( $search->compare( '==', 'media.type.domain', 'product' ) );
98
99
		foreach( $manager->search( $search ) as $item ) {
100
			$this->types[$item->getCode()] = $item->getCode();
101
		}
102
	}
103
104
105
	/**
106
	 * Saves the product related data to the storage
107
	 *
108
	 * @param \Aimeos\MShop\Product\Item\Iface $product Product item with associated items
109
	 * @param array $data List of CSV fields with position as key and data as value
110
	 * @return array List of data which hasn't been imported
111
	 */
112
	public function process( \Aimeos\MShop\Product\Item\Iface $product, array $data ) : array
113
	{
114
		$context = $this->context();
115
		$manager = \Aimeos\MShop::create( $context, 'product' );
116
		$refManager = \Aimeos\MShop::create( $context, 'media' );
117
		$separator = $context->config()->get( 'controller/jobs/product/import/csv/separator', "\n" );
118
119
		$listMap = [];
120
		$map = $this->getMappedChunk( $data, $this->getMapping() );
121
		$listItems = $product->getListItems( 'media', $this->listTypes, null, false );
122
123
		foreach( $listItems as $listItem )
124
		{
125
			if( ( $refItem = $listItem->getRefItem() ) !== null ) {
126
				$listMap[$refItem->getUrl()][$refItem->getType()][$refItem->getLanguageId()][$listItem->getType()] = $listItem;
127
			}
128
		}
129
130
		foreach( $map as $pos => $list )
131
		{
132
			if( $this->checkEntry( $list ) === false ) {
133
				continue;
134
			}
135
136
			$type = trim( $this->val( $list, 'media.type', 'default' ) );
137
			$langId = trim( $this->val( $list, 'media.languageid', '' ) );
138
			$listtype = trim( $this->val( $list, 'product.lists.type', 'default' ) );
139
			$listConfig = $this->getListConfig( trim( $this->val( $list, 'product.lists.config', '' ) ) );
140
141
			$urls = explode( $separator, trim( $this->val( $list, 'media.url', '' ) ) );
142
			unset( $list['media.url'] );
143
144
			$this->addType( 'product/lists/type', 'media', $listtype );
145
			$this->addType( 'media/type', 'product', $type );
146
147
			foreach( $urls as $idx => $url )
148
			{
149
				$url = trim( $url );
150
151
				if( isset( $listMap[$url][$type][$langId][$listtype] ) )
152
				{
153
					$listItem = $listMap[$url][$type][$langId][$listtype];
154
					$refItem = $listItem->getRefItem();
155
					unset( $listItems[$listItem->getId()] );
156
				}
157
				else
158
				{
159
					$listItem = $manager->createListItem()->setType( $listtype );
0 ignored issues
show
Bug introduced by
The method createListItem() does not exist on Aimeos\MShop\Common\Manager\Iface. Did you maybe mean create()? ( Ignorable by Annotation )

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

159
					$listItem = $manager->/** @scrutinizer ignore-call */ createListItem()->setType( $listtype );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
160
					$refItem = $refManager->create()->setType( $type );
161
				}
162
163
				$ext = strtolower( pathinfo( $url, PATHINFO_EXTENSION ) );
0 ignored issues
show
Bug introduced by
It seems like pathinfo($url, Aimeos\Co...dia\PATHINFO_EXTENSION) can also be of type array; however, parameter $string of strtolower() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

163
				$ext = strtolower( /** @scrutinizer ignore-type */ pathinfo( $url, PATHINFO_EXTENSION ) );
Loading history...
164
				if( isset( $this->mimes[$ext] ) ) {
165
					$refItem->setMimeType( $this->mimes[$ext] );
166
				}
167
168
				$refItem->setDomain( 'product' );
169
				$refItem = $this->update( $refItem, $list, $url );
170
				$listItem = $listItem->setPosition( $pos++ )->fromArray( $list )->setConfig( $listConfig );
171
172
				$product->addListItem( 'media', $listItem, $refItem );
173
			}
174
		}
175
176
		$product->deleteListItems( $listItems->toArray(), true );
177
178
		return $this->object()->process( $product, $data );
179
	}
180
181
182
	/**
183
	 * Checks if an entry can be used for updating a media item
184
	 *
185
	 * @param array $list Associative list of key/value pairs from the mapping
186
	 * @return bool True if valid, false if not
187
	 */
188
	protected function checkEntry( array $list ) : bool
189
	{
190
		if( $this->val( $list, 'media.url' ) === null ) {
191
			return false;
192
		}
193
194
		if( ( $type = trim( $this->val( $list, 'product.lists.type', '' ) ) ) && !isset( $this->listTypes[$type] ) )
195
		{
196
			$msg = sprintf( 'Invalid type "%1$s" (%2$s)', $type, 'product list' );
197
			throw new \Aimeos\Controller\Jobs\Exception( $msg );
198
		}
199
200
		if( ( $type = trim( $this->val( $list, 'media.type', '' ) ) ) && !isset( $this->types[$type] ) )
201
		{
202
			$msg = sprintf( 'Invalid type "%1$s" (%2$s)', $type, 'media' );
203
			throw new \Aimeos\Controller\Jobs\Exception( $msg );
204
		}
205
206
		return true;
207
	}
208
209
210
	/**
211
	 * Updates the media item with the given key/value pairs
212
	 *
213
	 * @param \Aimeos\MShop\Media\Item\Iface $refItem Media item to update
214
	 * @param array &$list Associative list of key/value pairs, matching pairs are removed
215
	 * @return \Aimeos\MShop\Media\Item\Iface Updated media item
216
	 */
217
	protected function update( \Aimeos\MShop\Media\Item\Iface $refItem, array &$list, string $url ) : \Aimeos\MShop\Media\Item\Iface
218
	{
219
		try
220
		{
221
			if( isset( $list['media.previews'] ) && ( $map = json_decode( $list['media.previews'], true ) ) !== null ) {
222
				$refItem->setPreviews( $map )->setUrl( $url );
223
			} elseif( isset( $list['media.preview'] ) ) {
224
				$refItem->setPreview( $list['media.preview'] )->setUrl( $url );
225
			} elseif( $refItem->getUrl() !== $url ) {
226
				$refItem = \Aimeos\MShop::create( $this->context(), 'media' )->scale( $refItem->setUrl( $url ), true );
227
			} else {
228
				$refItem = \Aimeos\MShop::create( $this->context(), 'media' )->scale( $refItem->setUrl( $url ) );
229
			}
230
231
			unset( $list['media.previews'], $list['media.preview'] );
232
		}
233
		catch( \Exception $e )
234
		{
235
			$msg = sprintf( 'Scaling image "%1$s" failed: %2$s', $url, $e->getMessage() );
236
			$this->context()->logger()->error( $msg, 'import/csv/product' );
237
		}
238
239
		return $refItem->fromArray( $list );
240
	}
241
}
242