Passed
Push — master ( b8f18d...700c02 )
by Aimeos
33:19 queued 19:46
created

Base::getProcessors()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 28
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 14
nc 4
nop 1
dl 0
loc 28
rs 9.7998
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-2023
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
	 * @throws \LogicException If class can't be instantiated
30
	 */
31
	protected function getCache( string $type, string $name = null ) : \Aimeos\Controller\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/common/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\\Common\\Product\\Import\\Csv\\Cache\\' . ucfirst( $type ) . '\\' . $name;
47
		$interface = \Aimeos\Controller\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( ( $row = fgetcsv( $fh ) ) !== false && $count++ < $maxcnt ) {
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
	 *  ),
82
	 *  'text' => array(
83
	 *  	3 => 'text.type', // e.g. "short" for short description
84
	 *  	4 => 'text.content', // UTF-8 encoded text
85
	 *  ),
86
	 *  'media' => array(
87
	 *  	5 => 'media.url', // relative URL of the product image on the server
88
	 *  ),
89
	 *  'price' => array(
90
	 *		6 => 'price.currencyid',
91
	 *  	7 => 'price.value', // price with decimals separated by a dot, no thousand separator
92
	 *  	8 => 'price.taxrate', // tax rate with decimals separated by a dot
93
	 *  ),
94
	 *  'attribute' => array(
95
	 *  	9 => 'attribute.type', // e.g. "size", "length", "width", "color", etc.
96
	 *  	10 => 'attribute.code', // code of an existing attribute, new ones will be created automatically
97
	 *  ),
98
	 *  'product' => array(
99
	 *  	11 => 'product.code', // e.g. EAN code of another product
100
	 *  	12 => 'product.lists.type', // e.g. "suggestion" for suggested product
101
	 *  ),
102
	 *  'property' => array(
103
	 *  	13 => 'product.property.type', // e.g. "package-weight"
104
	 *  	14 => 'product.property.value', // arbitrary value for the corresponding type
105
	 *  ),
106
	 *  'catalog' => array(
107
	 *  	15 => 'catalog.code', // e.g. Unique category code
108
	 *  	16 => 'catalog.lists.type', // e.g. "promotion" for top seller products
109
	 *  ),
110
	 *
111
	 * @return array Associative list of domains as keys ("item" is special for the product itself) and a list of
112
	 * 	positions and the domain item keys as values.
113
	 */
114
	protected function getDefaultMapping() : array
115
	{
116
		return array(
117
			'item' => array(
118
				0 => 'product.code',
119
				1 => 'product.label',
120
				2 => 'product.type',
121
				3 => 'product.status',
122
			),
123
			'text' => array(
124
				4 => 'text.type',
125
				5 => 'text.content',
126
				6 => 'text.type',
127
				7 => 'text.content',
128
			),
129
			'media' => array(
130
				8 => 'media.url',
131
			),
132
			'price' => array(
133
				9 => 'price.currencyid',
134
				10 => 'price.quantity',
135
				11 => 'price.value',
136
				12 => 'price.taxrate',
137
			),
138
			'attribute' => array(
139
				13 => 'attribute.code',
140
				14 => 'attribute.type',
141
			),
142
			'product' => array(
143
				15 => 'product.code',
144
				16 => 'product.lists.type',
145
			),
146
			'property' => array(
147
				17 => 'product.property.value',
148
				18 => 'product.property.type',
149
			),
150
			'catalog' => array(
151
				19 => 'catalog.code',
152
				20 => 'catalog.lists.type',
153
			),
154
		);
155
	}
156
157
158
	/**
159
	 * Returns the mapped data from the CSV line
160
	 *
161
	 * @param array $data List of CSV fields with position as key and domain item key as value (mapped data is removed)
162
	 * @param array $mapping List of domain item keys with the CSV field position as key
163
	 * @return array List of associative arrays containing the chunked properties
164
	 */
165
	protected function getMappedChunk( array &$data, array $mapping ) : array
166
	{
167
		$idx = 0;
168
		$map = [];
169
170
		foreach( $mapping as $pos => $key )
171
		{
172
			if( isset( $map[$idx][$key] ) ) {
173
				$idx++;
174
			}
175
176
			if( isset( $data[$pos] ) ) {
177
				$map[$idx][$key] = $data[$pos];
178
			}
179
		}
180
181
		return $map;
182
	}
183
184
185
	/**
186
	 * Returns the processor object for saving the product related information
187
	 *
188
	 * @param array $mappings Associative list of processor types as keys and index/data mappings as values
189
	 * @return \Aimeos\Controller\Common\Product\Import\Csv\Processor\Iface Processor object
190
	 * @throws \LogicException If class can't be instantiated
191
	 */
192
	protected function getProcessors( array $mappings ) : \Aimeos\Controller\Common\Product\Import\Csv\Processor\Iface
193
	{
194
		unset( $mappings['item'] );
195
196
		$context = $this->context();
197
		$config = $context->config();
198
199
		$object = new \Aimeos\Controller\Common\Product\Import\Csv\Processor\Done( $context, [] );
200
		$interface = \Aimeos\Controller\Common\Product\Import\Csv\Processor\Iface::class;
201
202
		foreach( $mappings as $type => $mapping )
203
		{
204
			if( ctype_alnum( $type ) === false ) {
205
				throw new \LogicException( sprintf( 'Invalid characters in class name "%1$s"', $type ), 400 );
206
			}
207
208
			$name = $config->get( 'controller/common/product/import/csv/processor/' . $type . '/name', 'Standard' );
209
210
			if( ctype_alnum( $name ) === false ) {
211
				throw new \LogicException( sprintf( 'Invalid characters in class name "%1$s"', $name ), 400 );
212
			}
213
214
			$classname = '\\Aimeos\\Controller\\Common\\Product\\Import\\Csv\\Processor\\' . ucfirst( $type ) . '\\' . $name;
215
216
			$object = \Aimeos\Utils::create( $classname, [$context, $mapping, $object], $interface );
217
		}
218
219
		return $object;
220
	}
221
}
222