Completed
Push — master ( 618dc8...d5c26d )
by Aimeos
02:29
created

Base::getMappedChunk()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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