Passed
Push — master ( f5900b...04f158 )
by Aimeos
04:26
created

Base::getMappedChunk()   B

Complexity

Conditions 8
Paths 17

Size

Total Lines 29
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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