Completed
Push — master ( 6a7433...48eba7 )
by Aimeos
02:24
created

Traits   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 330
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
wmc 34
lcom 1
cbo 10
dl 0
loc 330
rs 9.68
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
getContext() 0 1 ?
A convertData() 0 14 4
B getCache() 0 33 7
A getConverterList() 0 10 2
A getData() 0 14 3
A getDefaultMapping() 0 42 1
A getMappedChunk() 0 20 4
B getProcessors() 0 35 7
A getProducts() 0 15 2
A getTypeId() 0 21 4
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2015-2017
6
 * @package Controller
7
 * @subpackage Common
8
 */
9
10
11
namespace Aimeos\Controller\Common\Product\Import\Csv;
12
13
14
/**
15
 * Common trait for CSV product import job controllers and processors.
16
 *
17
 * @package Controller
18
 * @subpackage Common
19
 */
20
trait Traits
21
{
22
	private static $types = [];
23
24
25
	/**
26
	 * Returns the context item
27
	 *
28
	 * @return \Aimeos\MShop\Context\Item\Iface Context object
29
	 */
30
	abstract protected function getContext();
31
32
33
	/**
34
	 * Converts the CSV field data using the available converter objects
35
	 *
36
	 * @param array $convlist Associative list of CSV field indexes and converter objects
37
	 * @param array $data Associative list of product codes and lists of CSV field indexes and their data
38
	 * @return array Associative list of CSV field indexes and their converted data
39
	 */
40
	protected function convertData( array $convlist, array $data )
41
	{
42
		foreach( $convlist as $idx => $converter )
43
		{
44
			foreach( $data as $code => $list )
45
			{
46
				if( isset( $list[$idx] ) ) {
47
					$data[$code][$idx] = $converter->translate( $list[$idx] );
48
				}
49
			}
50
		}
51
52
		return $data;
53
	}
54
55
56
	/**
57
	 * Returns the cache object for the given type
58
	 *
59
	 * @param string $type Type of the cached data
60
	 * @param string|null Name of the cache implementation
61
	 * @return \Aimeos\Controller\Common\Product\Import\Csv\Cache\Iface Cache object
62
	 */
63
	protected function getCache( $type, $name = null )
64
	{
65
		$context = $this->getContext();
66
		$config = $context->getConfig();
67
68
		if( ctype_alnum( $type ) === false )
69
		{
70
			$classname = is_string($name) ? '\\Aimeos\\Controller\\Common\\Product\\Import\\Csv\\Cache\\' . $type : '<not a string>';
71
			throw new \Aimeos\Controller\Jobs\Exception( sprintf( 'Invalid characters in class name "%1$s"', $classname ) );
72
		}
73
74
		if( $name === null ) {
75
			$name = $config->get( 'controller/common/product/import/csv/cache/' . $type . '/name', 'Standard' );
76
		}
77
78
		if( ctype_alnum( $name ) === false )
79
		{
80
			$classname = is_string($name) ? '\\Aimeos\\Controller\\Common\\Product\\Import\\Csv\\Cache\\' . $type . '\\' . $name : '<not a string>';
81
			throw new \Aimeos\Controller\Jobs\Exception( sprintf( 'Invalid characters in class name "%1$s"', $classname ) );
82
		}
83
84
		$classname = '\\Aimeos\\Controller\\Common\\Product\\Import\\Csv\\Cache\\' . ucfirst( $type ) . '\\' . $name;
85
86
		if( class_exists( $classname ) === false ) {
87
			throw new \Aimeos\Controller\Jobs\Exception( sprintf( 'Class "%1$s" not found', $classname ) );
88
		}
89
90
		$object = new $classname( $context );
91
92
		\Aimeos\MW\Common\Base::checkClass( '\\Aimeos\\Controller\\Common\\Product\\Import\\Csv\\Cache\\Iface', $object );
93
94
		return $object;
95
	}
96
97
98
	/**
99
	 * Returns the list of converter objects based on the given converter map
100
	 *
101
	 * @param array $convmap List of converter names for the values at the position in the CSV file
102
	 * @return array Associative list of positions and converter objects
103
	 */
104
	protected function getConverterList( array $convmap )
105
	{
106
		$convlist = [];
107
108
		foreach( $convmap as $idx => $name ) {
109
			$convlist[$idx] = \Aimeos\MW\Convert\Factory::createConverter( $name );
110
		}
111
112
		return $convlist;
113
	}
114
115
116
	/**
117
	 * Returns the rows from the CSV file up to the maximum count
118
	 *
119
	 * @param \Aimeos\MW\Container\Content\Iface $content CSV content object
120
	 * @param integer $maxcnt Maximum number of rows that should be retrieved at once
121
	 * @param integer $codePos Column position which contains the unique product code (starting from 0)
122
	 * @return array List of arrays with product codes as keys and list of values from the CSV file
123
	 */
124
	protected function getData( \Aimeos\MW\Container\Content\Iface $content, $maxcnt, $codePos )
125
	{
126
		$count = 0;
127
		$data = [];
128
129
		while( $content->valid() && $count++ < $maxcnt )
130
		{
131
			$row = $content->current();
132
			$data[ $row[$codePos] ] = $row;
133
			$content->next();
134
		}
135
136
		return $data;
137
	}
138
139
140
	/**
141
	 * Returns the default mapping for the CSV fields to the domain item keys
142
	 *
143
	 * Example:
144
	 *  'item' => array(
145
	 *  	0 => 'product.code', // e.g. unique EAN code
146
	 *  	1 => 'product.label', // UTF-8 encoded text, also used as product name
147
	 *  ),
148
	 *  'text' => array(
149
	 *  	3 => 'text.type', // e.g. "short" for short description
150
	 *  	4 => 'text.content', // UTF-8 encoded text
151
	 *  ),
152
	 *  'media' => array(
153
	 *  	5 => 'media.url', // relative URL of the product image on the server
154
	 *  ),
155
	 *  'price' => array(
156
	 *		6 => 'price.currencyid',
157
	 *  	7 => 'price.value', // price with decimals separated by a dot, no thousand separator
158
	 *  	8 => 'price.taxrate', // tax rate with decimals separated by a dot
159
	 *  ),
160
	 *  'attribute' => array(
161
	 *  	9 => 'attribute.type', // e.g. "size", "length", "width", "color", etc.
162
	 *  	10 => 'attribute.code', // code of an existing attribute, new ones will be created automatically
163
	 *  ),
164
	 *  'product' => array(
165
	 *  	11 => 'product.code', // e.g. EAN code of another product
166
	 *  	12 => 'product.lists.type', // e.g. "suggestion" for suggested product
167
	 *  ),
168
	 *  'property' => array(
169
	 *  	13 => 'product.property.type', // e.g. "package-weight"
170
	 *  	14 => 'product.property.value', // arbitrary value for the corresponding type
171
	 *  ),
172
	 *  'catalog' => array(
173
	 *  	15 => 'catalog.code', // e.g. Unique category code
174
	 *  	16 => 'catalog.lists.type', // e.g. "promotion" for top seller products
175
	 *  ),
176
	 *
177
	 * @return array Associative list of domains as keys ("item" is special for the product itself) and a list of
178
	 * 	positions and the domain item keys as values.
179
	 */
180
	protected function getDefaultMapping()
181
	{
182
		return array(
183
			'item' => array(
184
				0 => 'product.code',
185
				1 => 'product.label',
186
				2 => 'product.type',
187
				3 => 'product.status',
188
			),
189
			'text' => array(
190
				4 => 'text.type',
191
				5 => 'text.content',
192
				6 => 'text.type',
193
				7 => 'text.content',
194
			),
195
			'media' => array(
196
				8 => 'media.url',
197
			),
198
			'price' => array(
199
				9 => 'price.currencyid',
200
				10 => 'price.quantity',
201
				11 => 'price.value',
202
				12 => 'price.taxrate',
203
			),
204
			'attribute' => array(
205
				13 => 'attribute.code',
206
				14 => 'attribute.type',
207
			),
208
			'product' => array(
209
				15 => 'product.code',
210
				16 => 'product.lists.type',
211
			),
212
			'property' => array(
213
				17 => 'product.property.value',
214
				18 => 'product.property.type',
215
			),
216
			'catalog' => array(
217
				19 => 'catalog.code',
218
				20 => 'catalog.lists.type',
219
			),
220
		);
221
	}
222
223
224
	/**
225
	 * Returns the mapped data from the CSV line
226
	 *
227
	 * @param array $data List of CSV fields with position as key and domain item key as value (mapped data is removed)
228
	 * @param array $mapping List of domain item keys with the CSV field position as key
229
	 * @return array List of associative arrays containing the chunked properties
230
	 */
231
	protected function getMappedChunk( array &$data, array $mapping )
232
	{
233
		$idx = 0;
234
		$map = [];
235
236
		foreach( $mapping as $pos => $key )
237
		{
238
			if( isset( $map[$idx][$key] ) ) {
239
				$idx++;
240
			}
241
242
			if( isset( $data[$pos] ) )
243
			{
244
				$map[$idx][$key] = $data[$pos];
245
				unset( $data[$pos] );
246
			}
247
		}
248
249
		return $map;
250
	}
251
252
253
	/**
254
	 * Returns the processor object for saving the product related information
255
	 *
256
	 * @param array $mappings Associative list of processor types as keys and index/data mappings as values
257
	 * @return \Aimeos\Controller\Common\Product\Import\Csv\Processor\Iface Processor object
258
	 */
259
	protected function getProcessors( array $mappings )
260
	{
261
		$context = $this->getContext();
262
		$config = $context->getConfig();
263
		$object = new \Aimeos\Controller\Common\Product\Import\Csv\Processor\Done( $context, [] );
264
265
		foreach( $mappings as $type => $mapping )
266
		{
267
			if( ctype_alnum( $type ) === false )
268
			{
269
				$classname = is_string($type) ? '\\Aimeos\\Controller\\Common\\Product\\Import\\Csv\\Processor\\' . $type : '<not a string>';
270
				throw new \Aimeos\Controller\Jobs\Exception( sprintf( 'Invalid characters in class name "%1$s"', $classname ) );
271
			}
272
273
			$name = $config->get( 'controller/common/product/import/csv/processor/' . $type . '/name', 'Standard' );
274
275
			if( ctype_alnum( $name ) === false )
276
			{
277
				$classname = is_string($name) ? '\\Aimeos\\Controller\\Common\\Product\\Import\\Csv\\Processor\\' . $type . '\\' . $name : '<not a string>';
278
				throw new \Aimeos\Controller\Jobs\Exception( sprintf( 'Invalid characters in class name "%1$s"', $classname ) );
279
			}
280
281
			$classname = '\\Aimeos\\Controller\\Common\\Product\\Import\\Csv\\Processor\\' . ucfirst( $type ) . '\\' . $name;
282
283
			if( class_exists( $classname ) === false ) {
284
				throw new \Aimeos\Controller\Jobs\Exception( sprintf( 'Class "%1$s" not found', $classname ) );
285
			}
286
287
			$object = new $classname( $context, $mapping, $object );
288
289
			\Aimeos\MW\Common\Base::checkClass( '\\Aimeos\\Controller\\Common\\Product\\Import\\Csv\\Processor\\Iface', $object );
290
		}
291
292
		return $object;
293
	}
294
295
296
	/**
297
	 * Returns the product items for the given codes
298
	 *
299
	 * @param array $codes List of product codes
300
	 * @param array $domains List of domains whose items should be fetched too
301
	 * @return array Associative list of product codes as key and product items as value
302
	 */
303
	protected function getProducts( array $codes, array $domains )
304
	{
305
		$result = [];
306
		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'product' );
307
308
		$search = $manager->createSearch();
309
		$search->setConditions( $search->compare( '==', 'product.code', $codes ) );
310
		$search->setSlice( 0, count( $codes ) );
311
312
		foreach( $manager->searchItems( $search, $domains ) as $item ) {
313
			$result[ $item->getCode() ] = $item;
314
		}
315
316
		return $result;
317
	}
318
319
320
	/**
321
	 * Returns the ID of the type item with the given code
322
	 *
323
	 * @param string $path Item/manager path separated by slashes, e.g. "product/lists/type"
324
	 * @param string $domain Domain the type items needs to be from
325
	 * @param string $code Unique code of the type item
326
	 * @return string Unique ID of the type item
327
	 */
328
	protected function getTypeId( $path, $domain, $code )
329
	{
330
		if( !isset( self::$types[$path][$domain] ) )
331
		{
332
			$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), $path );
333
			$key = str_replace( '/', '.', $path );
334
335
			$search = $manager->createSearch();
336
			$search->setConditions( $search->compare( '==', $key . '.domain', $domain ) );
337
338
			foreach( $manager->searchItems( $search ) as $id => $item ) {
339
				self::$types[$path][$domain][ $item->getCode() ] = $id;
340
			}
341
		}
342
343
		if( !isset( self::$types[$path][$domain][$code] ) ) {
344
			throw new \Aimeos\Controller\Jobs\Exception( sprintf( 'No type item for "%1$s/%2$s" in "%3$s" found', $domain, $code, $path ) );
345
		}
346
347
		return self::$types[$path][$domain][$code];
348
	}
349
}
350