Passed
Push — master ( a2156e...434824 )
by Aimeos
03:54
created

Standard::converters()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 45
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 45
rs 10
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 Jobs
8
 */
9
10
11
namespace Aimeos\Controller\Jobs\Product\Import\Csv;
12
13
14
/**
15
 * Job controller for CSV product imports.
16
 *
17
 * @package Controller
18
 * @subpackage Jobs
19
 */
20
class Standard
21
	extends \Aimeos\Controller\Common\Product\Import\Csv\Base
22
	implements \Aimeos\Controller\Jobs\Iface
23
{
24
	/** controller/jobs/product/import/csv/name
25
	 * Class name of the used product suggestions scheduler controller implementation
26
	 *
27
	 * Each default job controller can be replace by an alternative imlementation.
28
	 * To use this implementation, you have to set the last part of the class
29
	 * name as configuration value so the controller factory knows which class it
30
	 * has to instantiate.
31
	 *
32
	 * For example, if the name of the default class is
33
	 *
34
	 *  \Aimeos\Controller\Jobs\Product\Import\Csv\Standard
35
	 *
36
	 * and you want to replace it with your own version named
37
	 *
38
	 *  \Aimeos\Controller\Jobs\Product\Import\Csv\Mycsv
39
	 *
40
	 * then you have to set the this configuration option:
41
	 *
42
	 *  controller/jobs/product/import/csv/name = Mycsv
43
	 *
44
	 * The value is the last part of your own class name and it's case sensitive,
45
	 * so take care that the configuration value is exactly named like the last
46
	 * part of the class name.
47
	 *
48
	 * The allowed characters of the class name are A-Z, a-z and 0-9. No other
49
	 * characters are possible! You should always start the last part of the class
50
	 * name with an upper case character and continue only with lower case characters
51
	 * or numbers. Avoid chamel case names like "MyCsv"!
52
	 *
53
	 * @param string Last part of the class name
54
	 * @since 2015.01
55
	 */
56
57
	/** controller/jobs/product/import/csv/decorators/excludes
58
	 * Excludes decorators added by the "common" option from the product import CSV job controller
59
	 *
60
	 * Decorators extend the functionality of a class by adding new aspects
61
	 * (e.g. log what is currently done), executing the methods of the underlying
62
	 * class only in certain conditions (e.g. only for logged in users) or
63
	 * modify what is returned to the caller.
64
	 *
65
	 * This option allows you to remove a decorator added via
66
	 * "controller/jobs/common/decorators/default" before they are wrapped
67
	 * around the job controller.
68
	 *
69
	 *  controller/jobs/product/import/csv/decorators/excludes = array( 'decorator1' )
70
	 *
71
	 * This would remove the decorator named "decorator1" from the list of
72
	 * common decorators ("\Aimeos\Controller\Jobs\Common\Decorator\*") added via
73
	 * "controller/jobs/common/decorators/default" to the job controller.
74
	 *
75
	 * @param array List of decorator names
76
	 * @since 2015.01
77
	 * @see controller/jobs/common/decorators/default
78
	 * @see controller/jobs/product/import/csv/decorators/global
79
	 * @see controller/jobs/product/import/csv/decorators/local
80
	 */
81
82
	/** controller/jobs/product/import/csv/decorators/global
83
	 * Adds a list of globally available decorators only to the product import CSV job controller
84
	 *
85
	 * Decorators extend the functionality of a class by adding new aspects
86
	 * (e.g. log what is currently done), executing the methods of the underlying
87
	 * class only in certain conditions (e.g. only for logged in users) or
88
	 * modify what is returned to the caller.
89
	 *
90
	 * This option allows you to wrap global decorators
91
	 * ("\Aimeos\Controller\Jobs\Common\Decorator\*") around the job controller.
92
	 *
93
	 *  controller/jobs/product/import/csv/decorators/global = array( 'decorator1' )
94
	 *
95
	 * This would add the decorator named "decorator1" defined by
96
	 * "\Aimeos\Controller\Jobs\Common\Decorator\Decorator1" only to the job controller.
97
	 *
98
	 * @param array List of decorator names
99
	 * @since 2015.01
100
	 * @see controller/jobs/common/decorators/default
101
	 * @see controller/jobs/product/import/csv/decorators/excludes
102
	 * @see controller/jobs/product/import/csv/decorators/local
103
	 */
104
105
	/** controller/jobs/product/import/csv/decorators/local
106
	 * Adds a list of local decorators only to the product import CSV job controller
107
	 *
108
	 * Decorators extend the functionality of a class by adding new aspects
109
	 * (e.g. log what is currently done), executing the methods of the underlying
110
	 * class only in certain conditions (e.g. only for logged in users) or
111
	 * modify what is returned to the caller.
112
	 *
113
	 * This option allows you to wrap local decorators
114
	 * ("\Aimeos\Controller\Jobs\Product\Import\Csv\Decorator\*") around the job
115
	 * controller.
116
	 *
117
	 *  controller/jobs/product/import/csv/decorators/local = array( 'decorator2' )
118
	 *
119
	 * This would add the decorator named "decorator2" defined by
120
	 * "\Aimeos\Controller\Jobs\Product\Import\Csv\Decorator\Decorator2"
121
	 * only to the job controller.
122
	 *
123
	 * @param array List of decorator names
124
	 * @since 2015.01
125
	 * @see controller/jobs/common/decorators/default
126
	 * @see controller/jobs/product/import/csv/decorators/excludes
127
	 * @see controller/jobs/product/import/csv/decorators/global
128
	 */
129
130
131
	private $types;
132
133
134
	/**
135
	 * Returns the localized name of the job.
136
	 *
137
	 * @return string Name of the job
138
	 */
139
	public function getName() : string
140
	{
141
		return $this->context()->translate( 'controller/jobs', 'Product import CSV' );
142
	}
143
144
145
	/**
146
	 * Returns the localized description of the job.
147
	 *
148
	 * @return string Description of the job
149
	 */
150
	public function getDescription() : string
151
	{
152
		return $this->context()->translate( 'controller/jobs', 'Imports new and updates existing products from CSV files' );
153
	}
154
155
156
	/**
157
	 * Executes the job.
158
	 *
159
	 * @throws \Aimeos\Controller\Jobs\Exception If an error occurs
160
	 */
161
	public function run()
162
	{
163
		if( file_exists( $this->location() ) === false ) {
164
			return;
165
		}
166
167
		$mappings = $this->mapping();
168
169
		if( !isset( $mappings['item'] ) || !is_array( $mappings['item'] ) )
170
		{
171
			$msg = sprintf( 'Required mapping key "%1$s" is missing or contains no array', 'item' );
172
			throw new \Aimeos\Controller\Jobs\Exception( $msg );
173
		}
174
175
		try
176
		{
177
			$procMappings = $mappings;
178
			unset( $procMappings['item'] );
179
180
			$total = $errors = 0;
181
			$logger = $this->context()->logger();
182
183
			$codePos = $this->getCodePosition( $mappings['item'] );
184
			$convlist = $this->getConverterList( $this->converters() );
185
			$processor = $this->getProcessors( $procMappings );
186
			$container = $this->getContainer();
187
			$path = $container->getName();
188
189
			$maxcnt = $this->max();
190
			$strict = $this->strict();
0 ignored issues
show
Unused Code introduced by
The assignment to $strict is dead and can be removed.
Loading history...
191
			$skiplines = $this->skip();
192
			$domains = $this->domains();
193
194
			$msg = sprintf( 'Started product import from "%1$s" (%2$s)', $path, __CLASS__ );
195
			$logger->notice( $msg, 'import/csv/product' );
196
197
			foreach( $container as $content )
198
			{
199
				$name = $content->getName();
200
201
				for( $i = 0; $i < $skiplines; $i++ ) {
202
					$content->next();
203
				}
204
205
				while( ( $data = $this->getData( $content, $maxcnt, $codePos ) ) !== [] )
206
				{
207
					$chunkcnt = count( $data );
208
					$data = $this->convertData( $convlist, $data );
209
					$products = $this->getProducts( array_keys( $data ), $domains );
210
					$errcnt = $this->import( $products, $data, $mappings['item'], [], $processor );
211
212
					$str = 'Imported product lines from "%1$s": %2$d/%3$d (%4$s)';
213
					$msg = sprintf( $str, $name, $chunkcnt - $errcnt, $chunkcnt, __CLASS__ );
214
					$logger->info( $msg, 'import/csv/product' );
215
216
					$errors += $errcnt;
217
					$total += $chunkcnt;
218
					unset( $products, $data );
219
				}
220
			}
221
222
			$container->close();
223
		}
224
		catch( \Exception $e )
225
		{
226
			$logger->error( 'Product import error: ' . $e->getMessage() . "\n" . $e->getTraceAsString(), 'import/csv/product' );
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $logger does not seem to be defined for all execution paths leading up to this point.
Loading history...
227
			$this->mail( 'Product CSV import error', $e->getMessage() . "\n" . $e->getTraceAsString() );
228
			throw new \Aimeos\Controller\Jobs\Exception( $e->getMessage() );
229
		}
230
231
		$processor->finish();
232
233
		$msg = 'Finished product import from "%1$s": %2$d successful, %3$s errors, %4$s total (%5$s)';
234
		$logger->notice( sprintf( $msg, $path, $total - $errors, $errors, $total, __CLASS__ ), 'import/csv/product' );
235
236
		if( $errors > 0 )
237
		{
238
			$msg = sprintf( 'Invalid product lines in "%1$s": %2$d/%3$d', $path, $errors, $total );
239
			$this->mail( 'Product CSV import', $msg );
240
		}
241
242
		if( !empty( $backup = $this->backup() ) && @rename( $path, $backup = \Aimeos\Base\Str::strtime( $backup ) ) === false )
243
		{
244
			$msg = sprintf( 'Unable to move imported file "%1$s" to "%2$s"', $path, $backup );
245
			throw new \Aimeos\Controller\Jobs\Exception( $msg );
246
		}
247
	}
248
249
250
	/**
251
	 * Returns the directory for storing imported files
252
	 *
253
	 * @return string Directory for storing imported files
254
	 */
255
	protected function backup() : string
256
	{
257
		/** controller/jobs/product/import/csv/backup
258
		 * Name of the backup for sucessfully imported files
259
		 *
260
		 * After a CSV file was imported successfully, you can move it to another
261
		 * location, so it won't be imported again and isn't overwritten by the
262
		 * next file that is stored at the same location in the file system.
263
		 *
264
		 * You should use an absolute path to be sure but can be relative path
265
		 * if you absolutely know from where the job will be executed from. The
266
		 * name of the new backup location can contain placeholders understood
267
		 * by the PHP DateTime::format() method (with percent signs prefix) to
268
		 * create dynamic paths, e.g. "backup/%Y-%m-%d" which would create
269
		 * "backup/2000-01-01". For more information about the date() placeholders,
270
		 * please have a look  into the PHP documentation of the
271
		 * {@link https://www.php.net/manual/en/datetime.format.php format() method}.
272
		 *
273
		 * **Note:** If no backup name is configured, the file or directory
274
		 * won't be moved away. Please make also sure that the parent directory
275
		 * and the new directory are writable so the file or directory could be
276
		 * moved.
277
		 *
278
		 * @param integer Name of the backup file, optionally with date/time placeholders
279
		 * @since 2018.04
280
		 * @see controller/jobs/product/import/csv/converter
281
		 * @see controller/jobs/product/import/csv/domains
282
		 * @see controller/jobs/product/import/csv/location
283
		 * @see controller/jobs/product/import/csv/mapping
284
		 * @see controller/jobs/product/import/csv/max-size
285
		 * @see controller/jobs/product/import/csv/skip-lines
286
		 * @see controller/jobs/product/import/csv/strict
287
		 */
288
		return (string) $this->context()->config()->get( 'controller/jobs/product/import/csv/backup' );
289
	}
290
291
292
	/**
293
	 * Checks the given product type for validity
294
	 *
295
	 * @param string|null $type Product type or null for no type
296
	 * @return string New product type
297
	 */
298
	protected function checkType( string $type = null ) : string
299
	{
300
		if( !isset( $this->types ) )
301
		{
302
			$this->types = [];
303
304
			$manager = \Aimeos\MShop::create( $this->context(), 'product/type' );
305
			$search = $manager->filter()->slice( 0, 10000 );
306
307
			foreach( $manager->search( $search ) as $item ) {
308
				$this->types[$item->getCode()] = $item->getCode();
309
			}
310
		}
311
312
		return ( isset( $this->types[$type] ) ? $this->types[$type] : 'default' );
313
	}
314
315
316
	/**
317
	 * Returns the list of converter names for the values at the position in the CSV file
318
	 *
319
	 * @return array List of converter names for the values at the position in the CSV file
320
	 */
321
	protected function converters() : array
322
	{
323
		/** controller/jobs/product/import/csv/converter
324
		 * List of converter names for the values at the position in the CSV file
325
		 *
326
		 * Not all data in the CSV file is already in the required format. Maybe
327
		 * the text encoding isn't UTF-8, the date is not in ISO format or something
328
		 * similar. In order to convert the data before it's imported, you can
329
		 * specify a list of converter objects that should be applied to the data
330
		 * from the CSV file.
331
		 *
332
		 * To each field in the CSV file, you can apply one or more converters,
333
		 * e.g. to encode a Latin text to UTF8 for the second CSV field:
334
		 *
335
		 *  [1 => 'Text/LatinUTF8']
336
		 *
337
		 * Similarly, you can also apply several converters at once to the same
338
		 * field:
339
		 *
340
		 *  [1 => ['Text/LatinUTF8', 'DateTime/EnglishISO']]
341
		 *
342
		 * It would convert the data of the second CSV field first to UTF-8 and
343
		 * afterwards try to translate it to an ISO date format.
344
		 *
345
		 * The available converter objects are named "\Aimeos\MW\Convert\<type>_<conversion>"
346
		 * where <type> is the data type and <conversion> the way of the conversion.
347
		 * In the configuration, the type and conversion must be separated by a
348
		 * slash (<type>/<conversion>).
349
		 *
350
		 * **Note:** Keep in mind that the position of the CSV fields start at
351
		 * zero (0). If you only need to convert a few fields, you don't have to
352
		 * configure all fields. Only specify the positions in the array you
353
		 * really need!
354
		 *
355
		 * @param array Associative list of position/converter name (or list of names) pairs
356
		 * @since 2018.04
357
		 * @see controller/jobs/product/import/csv/backup
358
		 * @see controller/jobs/product/import/csv/domains
359
		 * @see controller/jobs/product/import/csv/location
360
		 * @see controller/jobs/product/import/csv/mapping
361
		 * @see controller/jobs/product/import/csv/max-size
362
		 * @see controller/jobs/product/import/csv/skip-lines
363
		 * @see controller/jobs/product/import/csv/strict
364
		 */
365
		return (array) $this->context()->config()->get( 'controller/jobs/product/import/csv/converter', [] );
366
	}
367
368
369
	/**
370
	 * Returns the list of domain names that should be retrieved along with the attribute items
371
	 *
372
	 * @return array List of domain names
373
	 */
374
	protected function domains() : array
375
	{
376
		/** controller/jobs/product/import/csv/domains
377
		 * List of item domain names that should be retrieved along with the product items
378
		 *
379
		 * For efficient processing, the items associated to the products can be
380
		 * fetched to, minimizing the number of database queries required. To be
381
		 * most effective, the list of item domain names should be used in the
382
		 * mapping configuration too, so the retrieved items will be used during
383
		 * the import.
384
		 *
385
		 * @param array Associative list of MShop item domain names
386
		 * @since 2018.04
387
		 * @see controller/jobs/product/import/csv/backup
388
		 * @see controller/jobs/product/import/csv/converter
389
		 * @see controller/jobs/product/import/csv/location
390
		 * @see controller/jobs/product/import/csv/mapping
391
		 * @see controller/jobs/product/import/csv/max-size
392
		 * @see controller/jobs/product/import/csv/skip-lines
393
		 * @see controller/jobs/product/import/csv/strict
394
		 */
395
		return $this->context()->config()->get( 'controller/jobs/product/import/csv/domains', ['media', 'text'] );
396
	}
397
398
399
	/**
400
	 * Returns the position of the "product.code" column from the product item mapping
401
	 *
402
	 * @param array $mapping Mapping of the "item" columns with position as key and code as value
403
	 * @return int Position of the "product.code" column
404
	 * @throws \Aimeos\Controller\Jobs\Exception If no mapping for "product.code" is found
405
	 */
406
	protected function getCodePosition( array $mapping ) : int
407
	{
408
		foreach( $mapping as $pos => $key )
409
		{
410
			if( $key === 'product.code' ) {
411
				return $pos;
412
			}
413
		}
414
415
		throw new \Aimeos\Controller\Jobs\Exception( sprintf( 'No "product.code" column in CSV mapping found' ) );
416
	}
417
418
419
	/**
420
	 * Opens and returns the container which includes the product data
421
	 *
422
	 * @return \Aimeos\MW\Container\Iface Container object
423
	 */
424
	protected function getContainer() : \Aimeos\MW\Container\Iface
425
	{
426
		$config = $this->context()->config();
427
428
		/** controller/jobs/product/import/csv/location
429
		 * File or directory where the content is stored which should be imported
430
		 *
431
		 * You need to configure the file or directory that acts as container
432
		 * for the CSV files that should be imported. It should be an absolute
433
		 * path to be sure but can be relative path if you absolutely know from
434
		 * where the job will be executed from.
435
		 *
436
		 * The path can point to any supported container format as long as the
437
		 * content is in CSV format, e.g.
438
		 *
439
		 * * Directory container / CSV file
440
		 * * Zip container / compressed CSV file
441
		 *
442
		 * @param string Absolute file or directory path
443
		 * @since 2015.05
444
		 * @category User
445
		 * @see controller/jobs/product/import/csv/container/type
446
		 * @see controller/jobs/product/import/csv/container/content
447
		 * @see controller/jobs/product/import/csv/container/options
448
		 */
449
		$location = $config->get( 'controller/jobs/product/import/csv/location' );
450
451
		/** controller/jobs/product/import/csv/container/type
452
		 * Nave of the container type to read the data from
453
		 *
454
		 * The container type tells the importer how it should retrieve the data.
455
		 * There are currently three container types that support the necessary
456
		 * CSV content:
457
		 *
458
		 * * Directory
459
		 * * Zip
460
		 *
461
		 * @param string Container type name
462
		 * @since 2015.05
463
		 * @category User
464
		 * @see controller/jobs/product/import/csv/location
465
		 * @see controller/jobs/product/import/csv/container/content
466
		 * @see controller/jobs/product/import/csv/container/options
467
		 */
468
		$container = $config->get( 'controller/jobs/product/import/csv/container/type', 'Directory' );
469
470
		/** controller/jobs/product/import/csv/container/content
471
		 * Name of the content type inside the container to read the data from
472
		 *
473
		 * The content type must always be a CSV-like format and there are
474
		 * currently two format types that are supported:
475
		 *
476
		 * * CSV
477
		 *
478
		 * @param array Content type name
479
		 * @since 2015.05
480
		 * @category User
481
		 * @see controller/jobs/product/import/csv/location
482
		 * @see controller/jobs/product/import/csv/container/type
483
		 * @see controller/jobs/product/import/csv/container/options
484
		 */
485
		$content = $config->get( 'controller/jobs/product/import/csv/container/content', 'CSV' );
486
487
		/** controller/jobs/product/import/csv/container/options
488
		 * List of file container options for the product import files
489
		 *
490
		 * Some container/content type allow you to hand over additional settings
491
		 * for configuration. Please have a look at the article about
492
		 * {@link http://aimeos.org/docs/Developers/Utility/Create_and_read_files container/content files}
493
		 * for more information.
494
		 *
495
		 * @param array Associative list of option name/value pairs
496
		 * @since 2015.05
497
		 * @category User
498
		 * @see controller/jobs/product/import/csv/location
499
		 * @see controller/jobs/product/import/csv/container/content
500
		 * @see controller/jobs/product/import/csv/container/type
501
		 */
502
		$options = $config->get( 'controller/jobs/product/import/csv/container/options', [] );
503
504
		if( $location === null )
505
		{
506
			$msg = sprintf( 'Required configuration for "%1$s" is missing', 'controller/jobs/product/import/csv/location' );
507
			throw new \Aimeos\Controller\Jobs\Exception( $msg );
508
		}
509
510
		return \Aimeos\MW\Container\Factory::getContainer( $location, $container, $content, $options );
511
	}
512
513
514
	/**
515
	 * Imports the CSV data and creates new products or updates existing ones
516
	 *
517
	 * @param \Aimeos\Map $products List of products items implementing \Aimeos\MShop\Product\Item\Iface
518
	 * @param array $data Associative list of import data as index/value pairs
519
	 * @param array $mapping Associative list of positions and domain item keys
520
	 * @param array $types List of allowed product type codes
521
	 * @param \Aimeos\Controller\Common\Product\Import\Csv\Processor\Iface $processor Processor object
522
	 * @return int Number of products that couldn't be imported
523
	 * @throws \Aimeos\Controller\Jobs\Exception
524
	 */
525
	protected function import( \Aimeos\Map $products, array $data, array $mapping, array $types,
526
		\Aimeos\Controller\Common\Product\Import\Csv\Processor\Iface $processor ) : int
527
	{
528
		$items = [];
529
		$errors = 0;
530
		$context = $this->context();
531
		$manager = \Aimeos\MShop::create( $context, 'index' );
532
533
		foreach( $data as $code => $list )
534
		{
535
			$manager->begin();
536
537
			try
538
			{
539
				$code = trim( $code );
540
				$product = $products[$code] ?? $manager->create();
541
				$map = current( $this->getMappedChunk( $list, $mapping ) ); // there can only be one chunk for the base product data
542
543
				if( $map )
544
				{
545
					$type = $this->checkType( $this->val( $map, 'product.type', $product->getType() ) );
546
547
					if( $config = $this->val( $map, 'product.config' ) ) {
548
						$map['product.config'] = json_decode( $config ) ?: [];
549
					}
550
551
					$product = $product->fromArray( $map, true );
552
					$product = $manager->save( $product->setType( $type ) );
553
554
					$processor->process( $product, $list );
0 ignored issues
show
Bug introduced by
It seems like $product can also be of type Aimeos\Map; however, parameter $product of Aimeos\Controller\Common...cessor\Iface::process() does only seem to accept Aimeos\MShop\Product\Item\Iface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

554
					$processor->process( /** @scrutinizer ignore-type */ $product, $list );
Loading history...
555
556
					$product = $manager->save( $product );
557
					$items[$product->getId()] = $product;
558
				}
559
560
				$manager->commit();
561
			}
562
			catch( \Exception $e )
563
			{
564
				$manager->rollback();
565
566
				$msg = sprintf( 'Unable to import product with code "%1$s": %2$s', $code, $e->getMessage() );
567
				$context->logger()->error( $msg, 'import/csv/product' );
568
569
				$errors++;
570
			}
571
		}
572
573
		return $errors;
574
	}
575
576
577
	/**
578
	 * Returns the path to the directory with the CSV file
579
	 *
580
	 * @return string Path to the directory with the CSV file
581
	 */
582
	protected function location() : string
583
	{
584
		/** controller/jobs/product/import/csv/location
585
		 * File or directory where the content is stored which should be imported
586
		 *
587
		 * You need to configure the CSV file or directory with the CSV files that
588
		 * should be imported. It should be an absolute path to be sure but can be
589
		 * relative path if you absolutely know from where the job will be executed
590
		 * from.
591
		 *
592
		 * @param string Relative path to the CSV files
593
		 * @since 2015.08
594
		 * @see controller/jobs/product/import/csv/backup
595
		 * @see controller/jobs/product/import/csv/converter
596
		 * @see controller/jobs/product/import/csv/domains
597
		 * @see controller/jobs/product/import/csv/location
598
		 * @see controller/jobs/product/import/csv/mapping
599
		 * @see controller/jobs/product/import/csv/max-size
600
		 * @see controller/jobs/product/import/csv/skip-lines
601
		 */
602
		return (string) $this->context()->config()->get( 'controller/jobs/product/import/csv/location', 'product' );
603
	}
604
605
606
	/**
607
	 * Returns the CSV column mapping
608
	 *
609
	 * @return array CSV column mapping
610
	 */
611
	protected function mapping() : array
612
	{
613
		/** controller/jobs/product/import/csv/mapping
614
		 * List of mappings between the position in the CSV file and item keys
615
		 *
616
		 * The importer have to know which data is at which position in the CSV
617
		 * file. Therefore, you need to specify a mapping between each position
618
		 * and the MShop domain item key (e.g. "product.code") it represents.
619
		 *
620
		 * You can use all domain item keys which are used in the fromArray()
621
		 * methods of the item classes.
622
		 *
623
		 * These mappings are grouped together by their processor names, which
624
		 * are responsible for importing the data, e.g. all mappings in "item"
625
		 * will be processed by the base product importer while the mappings in
626
		 * "text" will be imported by the text processor.
627
		 *
628
		 * @param array Associative list of processor names and lists of key/position pairs
629
		 * @since 2018.04
630
		 * @see controller/jobs/product/import/csv/backup
631
		 * @see controller/jobs/product/import/csv/converter
632
		 * @see controller/jobs/product/import/csv/domains
633
		 * @see controller/jobs/product/import/csv/location
634
		 * @see controller/jobs/product/import/csv/max-size
635
		 * @see controller/jobs/product/import/csv/skip-lines
636
		 * @see controller/jobs/product/import/csv/strict
637
		 */
638
		return (array) $this->context()->config()->get( 'controller/jobs/product/import/csv/mapping', $this->getDefaultMapping() );
639
	}
640
641
642
	/**
643
	 * Returns the maximum number of CSV rows to import at once
644
	 *
645
	 * @return int Maximum number of CSV rows to import at once
646
	 */
647
	protected function max() : int
648
	{
649
		/** controller/jobs/product/import/csv/max-size
650
		 * Maximum number of CSV rows to import at once
651
		 *
652
		 * It's more efficient to read and import more than one row at a time
653
		 * to speed up the import. Usually, the bigger the chunk that is imported
654
		 * at once, the less time the importer will need. The downside is that
655
		 * the amount of memory required by the import process will increase as
656
		 * well. Therefore, it's a trade-off between memory consumption and
657
		 * import speed.
658
		 *
659
		 * @param integer Number of rows
660
		 * @since 2018.04
661
		 * @see controller/jobs/product/import/csv/backup
662
		 * @see controller/jobs/product/import/csv/converter
663
		 * @see controller/jobs/product/import/csv/domains
664
		 * @see controller/jobs/product/import/csv/location
665
		 * @see controller/jobs/product/import/csv/mapping
666
		 * @see controller/jobs/product/import/csv/skip-lines
667
		 * @see controller/jobs/product/import/csv/strict
668
		 */
669
		return (int) $this->context()->config()->get( 'controller/jobs/product/import/csv/max-size', 1000 );
670
	}
671
672
673
	/**
674
	 * Returns the number of rows skipped in front of each CSV files
675
	 *
676
	 * @return int Number of rows skipped in front of each CSV files
677
	 */
678
	protected function skip() : int
679
	{
680
		/** controller/jobs/product/import/csv/skip-lines
681
		 * Number of rows skipped in front of each CSV files
682
		 *
683
		 * Some CSV files contain header information describing the content of
684
		 * the column values. These data is for informational purpose only and
685
		 * can't be imported into the database. Using this option, you can
686
		 * define the number of lines that should be left out before the import
687
		 * begins.
688
		 *
689
		 * @param integer Number of rows
690
		 * @since 2015.08
691
		 * @see controller/jobs/product/import/csv/backup
692
		 * @see controller/jobs/product/import/csv/converter
693
		 * @see controller/jobs/product/import/csv/domains
694
		 * @see controller/jobs/product/import/csv/location
695
		 * @see controller/jobs/product/import/csv/mapping
696
		 * @see controller/jobs/product/import/csv/max-size
697
		 * @see controller/jobs/product/import/csv/strict
698
		 */
699
		return (int) $this->context()->config()->get( 'controller/jobs/product/import/csv/skip-lines', 0 );
700
	}
701
702
703
	/**
704
	 * Returns if all columns from the file should be logged that are not mapped and therefore not imported
705
	 */
706
	protected function strict() : bool
707
	{
708
		/** controller/jobs/product/import/csv/strict
709
		 * Log all columns from the file that are not mapped and therefore not imported
710
		 *
711
		 * Depending on the mapping, there can be more columns in the CSV file
712
		 * than those which will be imported. This can be by purpose if you want
713
		 * to import only selected columns or if you've missed to configure one
714
		 * or more columns. This configuration option will log all columns that
715
		 * have not been imported if set to true. Otherwise, the left over fields
716
		 * in the imported line will be silently ignored.
717
		 *
718
		 * @param boolen True if not imported columns should be logged, false if not
719
		 * @since 2015.08
720
		 * @see controller/jobs/product/import/csv/backup
721
		 * @see controller/jobs/product/import/csv/converter
722
		 * @see controller/jobs/product/import/csv/domains
723
		 * @see controller/jobs/product/import/csv/location
724
		 * @see controller/jobs/product/import/csv/mapping
725
		 * @see controller/jobs/product/import/csv/max-size
726
		 * @see controller/jobs/product/import/csv/skip-lines
727
		 */
728
		return (bool) $this->context()->config()->get( 'controller/jobs/product/import/csv/strict', true );
729
	}
730
}
731