Passed
Push — master ( 2e8fc6...fa4386 )
by Aimeos
03:53
created

Base   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 253
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 92
c 1
b 0
f 0
dl 0
loc 253
rs 10
wmc 16

6 Methods

Rating   Name   Duplication   Size   Complexity  
A check() 0 10 3
B getDefaultMapping() 0 59 1
A getMappedChunk() 0 17 4
A getData() 0 10 3
A __construct() 0 5 1
A getProcessors() 0 28 4
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2025
6
 * @package Controller
7
 * @subpackage Common
8
 */
9
10
11
namespace Aimeos\Controller\Jobs\Common\Customer\Import\Csv;
12
13
14
/**
15
 * Common class for CSV customer import job controllers and processors.
16
 *
17
 * @package Controller
18
 * @subpackage Common
19
 */
20
class Base
21
	extends \Aimeos\Controller\Jobs\Base
22
{
23
	private array $checks = [];
24
25
26
	/**
27
	 * Initializes the object.
28
	 *
29
	 * @param \Aimeos\MShop\ContextIface $context MShop context object
30
	 * @param \Aimeos\Bootstrap $aimeos \Aimeos\Bootstrap main object
31
	 */
32
	public function __construct( \Aimeos\MShop\ContextIface $context, \Aimeos\Bootstrap $aimeos )
33
	{
34
		parent::__construct( $context, $aimeos );
35
36
		$this->checks = $context->config()->get( 'controller/jobs/customer/import/csv/checks', [] );
37
	}
38
39
40
	/**
41
	 * Checks if an entry can be used for updating the item
42
	 *
43
	 * @param array $entry Associative list of key/value pairs from the mapping
44
	 * @throws \Aimeos\Controller\Jobs\Exception If the check fails
45
	 */
46
	protected function check( array $entry )
47
	{
48
		foreach( $this->checks as $code => $regex )
49
		{
50
			$value = $this->val( $entry, $code );
51
52
			if( preg_match( $regex, (string) $value ) !== 1 )
53
			{
54
				$msg = sprintf( 'Checking "%1$s" value "%2$s" against "%3$s" failed', $code, $value, $regex );
55
				throw new \Aimeos\Controller\Jobs\Exception( $msg );
56
			}
57
		}
58
	}
59
60
61
	/**
62
	 * Returns the rows from the CSV file up to the maximum count
63
	 *
64
	 * @param resource $fh File handle to CSV file
65
	 * @param int $maxcnt Maximum number of rows that should be retrieved at once
66
	 * @param int $codePos Column position which contains the unique customer code (starting from 0)
67
	 * @return array List of arrays with customer codes as keys and list of values from the CSV file
68
	 */
69
	protected function getData( $fh, int $maxcnt, int $codePos ) : array
70
	{
71
		$data = [];
72
		$count = 0;
73
74
		while( $count++ < $maxcnt && ( $row = fgetcsv( $fh, null, ',', '"', '' ) ) !== false ) {
75
			$data[$row[$codePos]] = $row;
76
		}
77
78
		return $data;
79
	}
80
81
82
	/**
83
	 * Returns the default mapping for the CSV fields to the domain item keys
84
	 *
85
	 * Example:
86
	 *  'item' => array(
87
	 *      0 => 'customer.code', // e.g. unique EAN code
88
	 *      1 => 'customer.label', // UTF-8 encoded text, also used as customer name
89
	 *      2 => 'customer.salutation',
90
	 *      3 => 'customer.company',
91
	 *      4 => 'customer.vatid',
92
	 *      5 => 'customer.title',
93
	 *      6 => 'customer.firstname',
94
	 *      7 => 'customer.lastname',
95
	 *      8 => 'customer.address1',
96
	 *      9 => 'customer.address2',
97
	 *      10 => 'customer.address3',
98
	 *      11 => 'customer.postal',
99
	 *      12 => 'customer.city',
100
	 *      13 => 'customer.state',
101
	 *      14 => 'customer.languageid',
102
	 *      15 => 'customer.countryid',
103
	 *      16 => 'customer.telephone',
104
	 *      17 => 'customer.telefax',
105
	 *      18 => 'customer.mobile',
106
	 *      19 => 'customer.email',
107
	 *      20 => 'customer.website',
108
	 *      21 => 'customer.longitude',
109
	 *      22 => 'customer.latitude',
110
	 *      23 => 'customer.birthday',
111
	 *      24 => 'customer.status', // Status value (-2, -1, 0, 1)
112
	 *      25 => 'customer.groups',
113
	 *  ),
114
	 *	'address' => array(
115
	 *      26 => 'customer.address.salutation',
116
	 *      27 => 'customer.address.company',
117
	 *      28 => 'customer.address.vatid',
118
	 *      29 => 'customer.address.title',
119
	 *      30 => 'customer.address.firstname',
120
	 *      31 => 'customer.address.lastname',
121
	 *      32 => 'customer.address.address1',
122
	 *      33 => 'customer.address.address2',
123
	 *      34 => 'customer.address.address3',
124
	 *      35 => 'customer.address.postal',
125
	 *      36 => 'customer.address.city',
126
	 *      37 => 'customer.address.state',
127
	 *      38 => 'customer.address.languageid',
128
	 *      39 => 'customer.address.countryid',
129
	 *      40 => 'customer.address.telephone',
130
	 *      41 => 'customer.address.telefax',
131
	 *      42 => 'customer.address.mobile',
132
	 *      43 => 'customer.address.email',
133
	 *      44 => 'customer.address.website',
134
	 *      45 => 'customer.address.longitude',
135
	 *      46 => 'customer.address.latitude',
136
	 *      47 => 'customer.address.birthday',
137
	 *	),
138
	 *  'property' => array(
139
	 *      48 => 'customer.property.type',
140
	 *      49 => 'customer.property.languageid',
141
	 *      50 => 'customer.property.value',
142
	 *  ),
143
	 *
144
	 * @return array Associative list of domains as keys ("item" is special for the customer itself) and a list of
145
	 * 	positions and the domain item keys as values.
146
	 */
147
	protected function getDefaultMapping() : array
148
	{
149
		return array(
150
			'item' => array(
151
				0 => 'customer.code', // e.g. unique EAN code
152
				1 => 'customer.label', // UTF-8 encoded text, also used as customer name
153
				2 => 'customer.salutation',
154
				3 => 'customer.company',
155
				4 => 'customer.vatid',
156
				5 => 'customer.title',
157
				6 => 'customer.firstname',
158
				7 => 'customer.lastname',
159
				8 => 'customer.address1',
160
				9 => 'customer.address2',
161
				10 => 'customer.address3',
162
				11 => 'customer.postal',
163
				12 => 'customer.city',
164
				13 => 'customer.state',
165
				14 => 'customer.languageid',
166
				15 => 'customer.countryid',
167
				16 => 'customer.telephone',
168
				17 => 'customer.telefax',
169
				18 => 'customer.mobile',
170
				19 => 'customer.email',
171
				20 => 'customer.website',
172
				21 => 'customer.longitude',
173
				22 => 'customer.latitude',
174
				23 => 'customer.birthday',
175
				24 => 'customer.status', // Status value (-2, -1, 0, 1)
176
				25 => 'customer.groups',
177
			),
178
			'address' => array(
179
				26 => 'customer.address.salutation',
180
				27 => 'customer.address.company',
181
				28 => 'customer.address.vatid',
182
				29 => 'customer.address.title',
183
				30 => 'customer.address.firstname',
184
				31 => 'customer.address.lastname',
185
				32 => 'customer.address.address1',
186
				33 => 'customer.address.address2',
187
				34 => 'customer.address.address3',
188
				35 => 'customer.address.postal',
189
				36 => 'customer.address.city',
190
				37 => 'customer.address.state',
191
				38 => 'customer.address.languageid',
192
				39 => 'customer.address.countryid',
193
				40 => 'customer.address.telephone',
194
				41 => 'customer.address.telefax',
195
				42 => 'customer.address.mobile',
196
				43 => 'customer.address.email',
197
				44 => 'customer.address.website',
198
				45 => 'customer.address.longitude',
199
				46 => 'customer.address.latitude',
200
				47 => 'customer.address.birthday',
201
			),
202
			'property' => array(
203
				48 => 'customer.property.type',
204
				49 => 'customer.property.languageid',
205
				50 => 'customer.property.value',
206
			),
207
		);
208
	}
209
210
211
	/**
212
	 * Returns the mapped data from the CSV line
213
	 *
214
	 * @param array $data List of CSV fields with position as key and domain item key as value (mapped data is removed)
215
	 * @param array $mapping List of domain item keys with the CSV field position as key
216
	 * @return array List of associative arrays containing the chunked properties
217
	 */
218
	protected function getMappedChunk( array &$data, array $mapping ) : array
219
	{
220
		$idx = 0;
221
		$map = [];
222
223
		foreach( $mapping as $pos => $key )
224
		{
225
			if( isset( $map[$idx][$key] ) ) {
226
				$idx++;
227
			}
228
229
			if( isset( $data[$pos] ) ) {
230
				$map[$idx][$key] = $data[$pos];
231
			}
232
		}
233
234
		return $map;
235
	}
236
237
238
	/**
239
	 * Returns the processor object for saving the customer related information
240
	 *
241
	 * @param array $mappings Associative list of processor types as keys and index/data mappings as values
242
	 * @return \Aimeos\Controller\Jobs\Common\Customer\Import\Csv\Processor\Iface Processor object
243
	 * @throws \LogicException If class can't be instantiated
244
	 */
245
	protected function getProcessors( array $mappings ) : \Aimeos\Controller\Jobs\Common\Customer\Import\Csv\Processor\Iface
246
	{
247
		unset( $mappings['item'] );
248
249
		$context = $this->context();
250
		$config = $context->config();
251
252
		$object = new \Aimeos\Controller\Jobs\Common\Customer\Import\Csv\Processor\Done( $context, [] );
253
		$interface = \Aimeos\Controller\Jobs\Common\Customer\Import\Csv\Processor\Iface::class;
254
255
		foreach( $mappings as $type => $mapping )
256
		{
257
			if( ctype_alnum( $type ) === false ) {
258
				throw new \LogicException( sprintf( 'Invalid characters in class name "%1$s"', $type ), 400 );
259
			}
260
261
			$name = $config->get( 'controller/jobs/customer/import/csv/processor/' . $type . '/name', 'Standard' );
262
263
			if( ctype_alnum( $name ) === false ) {
264
				throw new \LogicException( sprintf( 'Invalid characters in class name "%1$s"', $name ), 400 );
265
			}
266
267
			$classname = '\\Aimeos\\Controller\\Jobs\\Common\\Customer\\Import\\Csv\\Processor\\' . ucfirst( $type ) . '\\' . $name;
268
269
			$object = \Aimeos\Utils::create( $classname, [$context, $mapping, $object], $interface );
270
		}
271
272
		return $object;
273
	}
274
}
275