Passed
Push — master ( ce780a...b27fbc )
by Aimeos
03:18
created

Base::convertData()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 13
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 5
nc 4
nop 2
dl 0
loc 13
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2015-2022
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
22
{
23
	/**
24
	 * Returns the cache object for the given type
25
	 *
26
	 * @param string $type Type of the cached data
27
	 * @param string|null $name Name of the cache implementation
28
	 * @return \Aimeos\Controller\Common\Product\Import\Csv\Cache\Iface Cache object
29
	 */
30
	protected function getCache( string $type, string $name = null ) : \Aimeos\Controller\Common\Product\Import\Csv\Cache\Iface
31
	{
32
		$context = $this->context();
33
		$config = $context->config();
34
35
		if( ctype_alnum( $type ) === false )
36
		{
37
			$classname = is_string( $name ) ? '\\Aimeos\\Controller\\Common\\Product\\Import\\Csv\\Cache\\' . $type : '<not a string>';
38
			throw new \Aimeos\Controller\Common\Exception( sprintf( 'Invalid characters in class name "%1$s"', $classname ) );
39
		}
40
41
		if( $name === null ) {
42
			$name = $config->get( 'controller/common/product/import/csv/cache/' . $type . '/name', 'Standard' );
43
		}
44
45
		if( ctype_alnum( $name ) === false )
46
		{
47
			$classname = is_string( $name ) ? '\\Aimeos\\Controller\\Common\\Product\\Import\\Csv\\Cache\\' . $type . '\\' . $name : '<not a string>';
48
			throw new \Aimeos\Controller\Common\Exception( sprintf( 'Invalid characters in class name "%1$s"', $classname ) );
49
		}
50
51
		$classname = '\\Aimeos\\Controller\\Common\\Product\\Import\\Csv\\Cache\\' . ucfirst( $type ) . '\\' . $name;
52
53
		if( class_exists( $classname ) === false ) {
54
			throw new \Aimeos\Controller\Common\Exception( sprintf( 'Class "%1$s" not found', $classname ) );
55
		}
56
57
		$object = new $classname( $context );
58
59
		\Aimeos\MW\Common\Base::checkClass( '\\Aimeos\\Controller\\Common\\Product\\Import\\Csv\\Cache\\Iface', $object );
60
61
		return $object;
62
	}
63
64
65
	/**
66
	 * Returns the rows from the CSV file up to the maximum count
67
	 *
68
	 * @param resource $fh File handle to CSV file
69
	 * @param int $maxcnt Maximum number of rows that should be retrieved at once
70
	 * @param int $codePos Column position which contains the unique product code (starting from 0)
71
	 * @return array List of arrays with product codes as keys and list of values from the CSV file
72
	 */
73
	protected function getData( $fh, int $maxcnt, int $codePos ) : array
74
	{
75
		$count = 0;
76
		$data = [];
77
78
		while( ( $row = fgetcsv( $fh ) ) !== false && $count++ < $maxcnt ) {
79
			$data[$row[$codePos]] = $row;
80
		}
81
82
		return $data;
83
	}
84
85
86
	/**
87
	 * Returns the default mapping for the CSV fields to the domain item keys
88
	 *
89
	 * Example:
90
	 *  'item' => array(
91
	 *  	0 => 'product.code', // e.g. unique EAN code
92
	 *  	1 => 'product.label', // UTF-8 encoded text, also used as product name
93
	 *  ),
94
	 *  'text' => array(
95
	 *  	3 => 'text.type', // e.g. "short" for short description
96
	 *  	4 => 'text.content', // UTF-8 encoded text
97
	 *  ),
98
	 *  'media' => array(
99
	 *  	5 => 'media.url', // relative URL of the product image on the server
100
	 *  ),
101
	 *  'price' => array(
102
	 *		6 => 'price.currencyid',
103
	 *  	7 => 'price.value', // price with decimals separated by a dot, no thousand separator
104
	 *  	8 => 'price.taxrate', // tax rate with decimals separated by a dot
105
	 *  ),
106
	 *  'attribute' => array(
107
	 *  	9 => 'attribute.type', // e.g. "size", "length", "width", "color", etc.
108
	 *  	10 => 'attribute.code', // code of an existing attribute, new ones will be created automatically
109
	 *  ),
110
	 *  'product' => array(
111
	 *  	11 => 'product.code', // e.g. EAN code of another product
112
	 *  	12 => 'product.lists.type', // e.g. "suggestion" for suggested product
113
	 *  ),
114
	 *  'property' => array(
115
	 *  	13 => 'product.property.type', // e.g. "package-weight"
116
	 *  	14 => 'product.property.value', // arbitrary value for the corresponding type
117
	 *  ),
118
	 *  'catalog' => array(
119
	 *  	15 => 'catalog.code', // e.g. Unique category code
120
	 *  	16 => 'catalog.lists.type', // e.g. "promotion" for top seller products
121
	 *  ),
122
	 *
123
	 * @return array Associative list of domains as keys ("item" is special for the product itself) and a list of
124
	 * 	positions and the domain item keys as values.
125
	 */
126
	protected function getDefaultMapping() : array
127
	{
128
		return array(
129
			'item' => array(
130
				0 => 'product.code',
131
				1 => 'product.label',
132
				2 => 'product.type',
133
				3 => 'product.status',
134
			),
135
			'text' => array(
136
				4 => 'text.type',
137
				5 => 'text.content',
138
				6 => 'text.type',
139
				7 => 'text.content',
140
			),
141
			'media' => array(
142
				8 => 'media.url',
143
			),
144
			'price' => array(
145
				9 => 'price.currencyid',
146
				10 => 'price.quantity',
147
				11 => 'price.value',
148
				12 => 'price.taxrate',
149
			),
150
			'attribute' => array(
151
				13 => 'attribute.code',
152
				14 => 'attribute.type',
153
			),
154
			'product' => array(
155
				15 => 'product.code',
156
				16 => 'product.lists.type',
157
			),
158
			'property' => array(
159
				17 => 'product.property.value',
160
				18 => 'product.property.type',
161
			),
162
			'catalog' => array(
163
				19 => 'catalog.code',
164
				20 => 'catalog.lists.type',
165
			),
166
		);
167
	}
168
169
170
	/**
171
	 * Returns the mapped data from the CSV line
172
	 *
173
	 * @param array $data List of CSV fields with position as key and domain item key as value (mapped data is removed)
174
	 * @param array $mapping List of domain item keys with the CSV field position as key
175
	 * @return array List of associative arrays containing the chunked properties
176
	 */
177
	protected function getMappedChunk( array &$data, array $mapping ) : array
178
	{
179
		$idx = 0;
180
		$map = [];
181
182
		foreach( $mapping as $pos => $key )
183
		{
184
			if( isset( $map[$idx][$key] ) ) {
185
				$idx++;
186
			}
187
188
			if( isset( $data[$pos] ) ) {
189
				$map[$idx][$key] = $data[$pos];
190
			}
191
		}
192
193
		return $map;
194
	}
195
196
197
	/**
198
	 * Returns the processor object for saving the product related information
199
	 *
200
	 * @param array $mappings Associative list of processor types as keys and index/data mappings as values
201
	 * @return \Aimeos\Controller\Common\Product\Import\Csv\Processor\Iface Processor object
202
	 */
203
	protected function getProcessors( array $mappings ) : \Aimeos\Controller\Common\Product\Import\Csv\Processor\Iface
204
	{
205
		unset( $mappings['item'] );
206
207
		$context = $this->context();
208
		$config = $context->config();
209
		$object = new \Aimeos\Controller\Common\Product\Import\Csv\Processor\Done( $context, [] );
210
211
		foreach( $mappings as $type => $mapping )
212
		{
213
			if( ctype_alnum( $type ) === false )
214
			{
215
				$classname = is_string( $type ) ? '\\Aimeos\\Controller\\Common\\Product\\Import\\Csv\\Processor\\' . $type : '<not a string>';
216
				throw new \Aimeos\Controller\Common\Exception( sprintf( 'Invalid characters in class name "%1$s"', $classname ) );
217
			}
218
219
			$name = $config->get( 'controller/common/product/import/csv/processor/' . $type . '/name', 'Standard' );
220
221
			if( ctype_alnum( $name ) === false )
222
			{
223
				$classname = is_string( $name ) ? '\\Aimeos\\Controller\\Common\\Product\\Import\\Csv\\Processor\\' . $type . '\\' . $name : '<not a string>';
224
				throw new \Aimeos\Controller\Common\Exception( sprintf( 'Invalid characters in class name "%1$s"', $classname ) );
225
			}
226
227
			$classname = '\\Aimeos\\Controller\\Common\\Product\\Import\\Csv\\Processor\\' . ucfirst( $type ) . '\\' . $name;
228
229
			if( class_exists( $classname ) === false ) {
230
				throw new \Aimeos\Controller\Common\Exception( sprintf( 'Class "%1$s" not found', $classname ) );
231
			}
232
233
			$object = new $classname( $context, $mapping, $object );
234
235
			\Aimeos\MW\Common\Base::checkClass( '\\Aimeos\\Controller\\Common\\Product\\Import\\Csv\\Processor\\Iface', $object );
236
		}
237
238
		return $object;
239
	}
240
241
242
	/**
243
	 * Returns the product items for the given codes
244
	 *
245
	 * @param array $codes List of product codes
246
	 * @param array $domains List of domains whose items should be fetched too
247
	 * @return \Aimeos\Map Associative list of product codes as key and product items as value
248
	 */
249
	protected function getProducts( array $codes, array $domains ) : \Aimeos\Map
250
	{
251
		$manager = \Aimeos\MShop::create( $this->context(), 'product' );
252
		$search = $manager->filter()->add( ['product.code' => $codes] )->slice( 0, count( $codes ) );
253
254
		return $manager->search( $search, $domains )->col( null, 'product.code' );
255
	}
256
}
257