Completed
Push — master ( 984043...a9b00f )
by Aimeos
02:00
created

Standard::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
cc 2
nc 2
nop 2
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2015-2018
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
	private $types = [];
25
26
27
	/**
28
	 * Initializes the object.
29
	 *
30
	 * @param \Aimeos\MShop\Context\Item\Iface $context MShop context object
31
	 * @param \Aimeos\Bootstrap $aimeos \Aimeos\Bootstrap main object
32
	 */
33
	public function __construct( \Aimeos\MShop\Context\Item\Iface $context, \Aimeos\Bootstrap $aimeos )
34
	{
35
		parent::__construct( $context, $aimeos );
36
37
		$manager = \Aimeos\MShop\Factory::createManager( $context, 'product/type' );
38
		$manager->createSearch()->setSlice( 0, 0x7fffffff );
39
40
		foreach( $manager->searchItems( $search ) as $item ) {
0 ignored issues
show
Bug introduced by
The variable $search does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
41
			$this->types[$item->getCode()] = $item->getCode();
42
		}
43
	}
44
45
46
	/**
47
	 * Returns the localized name of the job.
48
	 *
49
	 * @return string Name of the job
50
	 */
51
	public function getName()
52
	{
53
		return $this->getContext()->getI18n()->dt( 'controller/jobs', 'Product import CSV' );
54
	}
55
56
57
	/**
58
	 * Returns the localized description of the job.
59
	 *
60
	 * @return string Description of the job
61
	 */
62
	public function getDescription()
63
	{
64
		return $this->getContext()->getI18n()->dt( 'controller/jobs', 'Imports new and updates existing products from CSV files' );
65
	}
66
67
68
	/**
69
	 * Executes the job.
70
	 *
71
	 * @throws \Aimeos\Controller\Jobs\Exception If an error occurs
72
	 */
73
	public function run()
74
	{
75
		$total = $errors = 0;
76
		$context = $this->getContext();
77
		$config = $context->getConfig();
78
		$logger = $context->getLogger();
79
		$domains = array( 'attribute', 'media', 'price', 'product', 'product/property', 'text' );
80
		$mappings = $this->getDefaultMapping();
81
82
83
		/** controller/common/product/import/csv/domains
84
		 * List of item domain names that should be retrieved along with the product items
85
		 *
86
		 * For efficient processing, the items associated to the products can be
87
		 * fetched to, minimizing the number of database queries required. To be
88
		 * most effective, the list of item domain names should be used in the
89
		 * mapping configuration too, so the retrieved items will be used during
90
		 * the import.
91
		 *
92
		 * @param array Associative list of MShop item domain names
93
		 * @since 2015.05
94
		 * @category Developer
95
		 * @see controller/common/product/import/csv/mapping
96
		 * @see controller/common/product/import/csv/converter
97
		 * @see controller/common/product/import/csv/max-size
98
		 */
99
		$domains = $config->get( 'controller/common/product/import/csv/domains', $domains );
100
101
		/** controller/jobs/product/import/csv/domains
102
		 * List of item domain names that should be retrieved along with the product items
103
		 *
104
		 * This configuration setting overwrites the shared option
105
		 * "controller/common/product/import/csv/domains" if you need a
106
		 * specific setting for the job controller. Otherwise, you should
107
		 * use the shared option for consistency.
108
		 *
109
		 * @param array Associative list of MShop item domain names
110
		 * @since 2015.05
111
		 * @category Developer
112
		 * @see controller/jobs/product/import/csv/mapping
113
		 * @see controller/jobs/product/import/csv/skip-lines
114
		 * @see controller/jobs/product/import/csv/converter
115
		 * @see controller/jobs/product/import/csv/strict
116
		 * @see controller/jobs/product/import/csv/backup
117
		 * @see controller/common/product/import/csv/max-size
118
		 */
119
		$domains = $config->get( 'controller/jobs/product/import/csv/domains', $domains );
120
121
122
		/** controller/common/product/import/csv/mapping
123
		 * List of mappings between the position in the CSV file and item keys
124
		 *
125
		 * The importer have to know which data is at which position in the CSV
126
		 * file. Therefore, you need to specify a mapping between each position
127
		 * and the MShop domain item key (e.g. "product.code") it represents.
128
		 *
129
		 * You can use all domain item keys which are used in the fromArray()
130
		 * methods of the item classes.
131
		 *
132
		 * These mappings are grouped together by their processor names, which
133
		 * are responsible for importing the data, e.g. all mappings in "item"
134
		 * will be processed by the base product importer while the mappings in
135
		 * "text" will be imported by the text processor.
136
		 *
137
		 * @param array Associative list of processor names and lists of key/position pairs
138
		 * @since 2015.05
139
		 * @category Developer
140
		 * @see controller/common/product/import/csv/domains
141
		 * @see controller/common/product/import/csv/converter
142
		 * @see controller/common/product/import/csv/max-size
143
		 */
144
		$mappings = $config->get( 'controller/common/product/import/csv/mapping', $mappings );
145
146
		/** controller/jobs/product/import/csv/mapping
147
		 * List of mappings between the position in the CSV file and item keys
148
		 *
149
		 * This configuration setting overwrites the shared option
150
		 * "controller/common/product/import/csv/mapping" if you need a
151
		 * specific setting for the job controller. Otherwise, you should
152
		 * use the shared option for consistency.
153
		 *
154
		 * @param array Associative list of processor names and lists of key/position pairs
155
		 * @since 2015.05
156
		 * @category Developer
157
		 * @see controller/jobs/product/import/csv/domains
158
		 * @see controller/jobs/product/import/csv/skip-lines
159
		 * @see controller/jobs/product/import/csv/converter
160
		 * @see controller/jobs/product/import/csv/strict
161
		 * @see controller/jobs/product/import/csv/backup
162
		 * @see controller/common/product/import/csv/max-size
163
		 */
164
		$mappings = $config->get( 'controller/jobs/product/import/csv/mapping', $mappings );
165
166
167
		/** controller/common/product/import/csv/converter
168
		 * List of converter names for the values at the position in the CSV file
169
		 *
170
		 * Not all data in the CSV file is already in the required format. Maybe
171
		 * the text encoding isn't UTF-8, the date is not in ISO format or something
172
		 * similar. In order to convert the data before it's imported, you can
173
		 * specify a list of converter objects that should be applied to the data
174
		 * from the CSV file.
175
		 *
176
		 * To each field in the CSV file, you can apply one or more converters,
177
		 * e.g. to encode a Latin text to UTF8 for the second CSV field:
178
		 *
179
		 *  array( 1 => 'Text/LatinUTF8' )
180
		 *
181
		 * Similarly, you can also apply several converters at once to the same
182
		 * field:
183
		 *
184
		 *  array( 1 => array( 'Text/LatinUTF8', 'DateTime/EnglishISO' ) )
185
		 *
186
		 * It would convert the data of the second CSV field first to UTF-8 and
187
		 * afterwards try to translate it to an ISO date format.
188
		 *
189
		 * The available converter objects are named "\Aimeos\MW\Convert\<type>_<conversion>"
190
		 * where <type> is the data type and <conversion> the way of the conversion.
191
		 * In the configuration, the type and conversion must be separated by a
192
		 * slash (<type>/<conversion>).
193
		 *
194
		 * '''Note:''' Keep in mind that the position of the CSV fields start at
195
		 * zero (0). If you only need to convert a few fields, you don't have to
196
		 * configure all fields. Only specify the positions in the array you
197
		 * really need!
198
		 *
199
		 * @param array Associative list of position/converter name (or list of names) pairs
200
		 * @since 2015.05
201
		 * @category Developer
202
		 * @see controller/common/product/import/csv/domains
203
		 * @see controller/common/product/import/csv/mapping
204
		 * @see controller/common/product/import/csv/max-size
205
		 */
206
		$converters = $config->get( 'controller/common/product/import/csv/converter', [] );
207
208
		/** controller/jobs/product/import/csv/converter
209
		 * List of converter names for the values at the position in the CSV file
210
		 *
211
		 * This configuration setting overwrites the shared option
212
		 * "controller/common/product/import/csv/converter" if you need a
213
		 * specific setting for the job controller. Otherwise, you should
214
		 * use the shared option for consistency.
215
		 *
216
		 * @param array Associative list of position/converter name (or list of names) pairs
217
		 * @since 2015.05
218
		 * @category Developer
219
		 * @see controller/jobs/product/import/csv/domains
220
		 * @see controller/jobs/product/import/csv/mapping
221
		 * @see controller/jobs/product/import/csv/skip-lines
222
		 * @see controller/jobs/product/import/csv/strict
223
		 * @see controller/jobs/product/import/csv/backup
224
		 * @see controller/common/product/import/csv/max-size
225
		 */
226
		$converters = $config->get( 'controller/jobs/product/import/csv/converter', $converters );
227
228
229
		/** controller/common/product/import/csv/max-size
230
		 * Maximum number of CSV rows to import at once
231
		 *
232
		 * It's more efficient to read and import more than one row at a time
233
		 * to speed up the import. Usually, the bigger the chunk that is imported
234
		 * at once, the less time the importer will need. The downside is that
235
		 * the amount of memory required by the import process will increase as
236
		 * well. Therefore, it's a trade-off between memory consumption and
237
		 * import speed.
238
		 *
239
		 * @param integer Number of rows
240
		 * @since 2015.05
241
		 * @category Developer
242
		 * @see controller/common/product/import/csv/domains
243
		 * @see controller/common/product/import/csv/mapping
244
		 * @see controller/common/product/import/csv/converter
245
		 */
246
		$maxcnt = (int) $config->get( 'controller/common/product/import/csv/max-size', 1000 );
247
248
249
		/** controller/jobs/product/import/csv/skip-lines
250
		 * Number of rows skipped in front of each CSV files
251
		 *
252
		 * Some CSV files contain header information describing the content of
253
		 * the column values. These data is for informational purpose only and
254
		 * can't be imported into the database. Using this option, you can
255
		 * define the number of lines that should be left out before the import
256
		 * begins.
257
		 *
258
		 * @param integer Number of rows
259
		 * @since 2015.08
260
		 * @category Developer
261
		 * @see controller/jobs/product/import/csv/domains
262
		 * @see controller/jobs/product/import/csv/mapping
263
		 * @see controller/jobs/product/import/csv/converter
264
		 * @see controller/jobs/product/import/csv/strict
265
		 * @see controller/jobs/product/import/csv/backup
266
		 * @see controller/common/product/import/csv/max-size
267
		 */
268
		$skiplines = (int) $config->get( 'controller/jobs/product/import/csv/skip-lines', 0 );
269
270
271
		/** controller/jobs/product/import/csv/strict
272
		 * Log all columns from the file that are not mapped and therefore not imported
273
		 *
274
		 * Depending on the mapping, there can be more columns in the CSV file
275
		 * than those which will be imported. This can be by purpose if you want
276
		 * to import only selected columns or if you've missed to configure one
277
		 * or more columns. This configuration option will log all columns that
278
		 * have not been imported if set to true. Otherwise, the left over fields
279
		 * in the imported line will be silently ignored.
280
		 *
281
		 * @param boolen True if not imported columns should be logged, false if not
282
		 * @since 2015.08
283
		 * @category User
284
		 * @category Developer
285
		 * @see controller/jobs/product/import/csv/domains
286
		 * @see controller/jobs/product/import/csv/mapping
287
		 * @see controller/jobs/product/import/csv/skip-lines
288
		 * @see controller/jobs/product/import/csv/converter
289
		 * @see controller/jobs/product/import/csv/backup
290
		 * @see controller/common/product/import/csv/max-size
291
		 */
292
		$strict = (bool) $config->get( 'controller/jobs/product/import/csv/strict', true );
293
294
295
		/** controller/jobs/product/import/csv/backup
296
		 * Name of the backup for sucessfully imported files
297
		 *
298
		 * After a CSV file was imported successfully, you can move it to another
299
		 * location, so it won't be imported again and isn't overwritten by the
300
		 * next file that is stored at the same location in the file system.
301
		 *
302
		 * You should use an absolute path to be sure but can be relative path
303
		 * if you absolutely know from where the job will be executed from. The
304
		 * name of the new backup location can contain placeholders understood
305
		 * by the PHP strftime() function to create dynamic paths, e.g. "backup/%Y-%m-%d"
306
		 * which would create "backup/2000-01-01". For more information about the
307
		 * strftime() placeholders, please have a look into the PHP documentation of
308
		 * the {@link http://php.net/manual/en/function.strftime.php strftime() function}.
309
		 *
310
		 * '''Note:''' If no backup name is configured, the file or directory
311
		 * won't be moved away. Please make also sure that the parent directory
312
		 * and the new directory are writable so the file or directory could be
313
		 * moved.
314
		 *
315
		 * @param integer Name of the backup file, optionally with date/time placeholders
316
		 * @since 2015.05
317
		 * @category Developer
318
		 * @see controller/jobs/product/import/csv/domains
319
		 * @see controller/jobs/product/import/csv/mapping
320
		 * @see controller/jobs/product/import/csv/skip-lines
321
		 * @see controller/jobs/product/import/csv/converter
322
		 * @see controller/jobs/product/import/csv/strict
323
		 * @see controller/common/product/import/csv/max-size
324
		 */
325
		$backup = $config->get( 'controller/jobs/product/import/csv/backup' );
326
327
328
		if( !isset( $mappings['item'] ) || !is_array( $mappings['item'] ) )
329
		{
330
			$msg = sprintf( 'Required mapping key "%1$s" is missing or contains no array', 'item' );
331
			throw new \Aimeos\Controller\Jobs\Exception( $msg );
332
		}
333
334
		try
335
		{
336
			$procMappings = $mappings;
337
			unset( $procMappings['item'] );
338
339
			$codePos = $this->getCodePosition( $mappings['item'] );
340
			$convlist = $this->getConverterList( $converters );
341
			$processor = $this->getProcessors( $procMappings );
342
			$container = $this->getContainer();
343
			$path = $container->getName();
344
345
			$msg = sprintf( 'Started product import from "%1$s" (%2$s)', $path, __CLASS__ );
346
			$logger->log( $msg, \Aimeos\MW\Logger\Base::NOTICE );
347
348
			foreach( $container as $content )
349
			{
350
				$name = $content->getName();
351
352
				for( $i = 0; $i < $skiplines; $i++ ) {
353
					$content->next();
354
				}
355
356
				while( ( $data = $this->getData( $content, $maxcnt, $codePos ) ) !== [] )
357
				{
358
					$data = $this->convertData( $convlist, $data );
359
					$products = $this->getProducts( array_keys( $data ), $domains );
360
					$errcnt = $this->import( $products, $data, $mappings['item'], $processor, $strict );
361
					$chunkcnt = count( $data );
362
363
					$msg = 'Imported product lines from "%1$s": %2$d/%3$d (%4$s)';
364
					$logger->log( sprintf( $msg, $name, $chunkcnt - $errcnt, $chunkcnt, __CLASS__ ), \Aimeos\MW\Logger\Base::NOTICE );
365
366
					$errors += $errcnt;
367
					$total += $chunkcnt;
368
					unset( $products, $data );
369
				}
370
			}
371
372
			$container->close();
373
		}
374
		catch( \Exception $e )
375
		{
376
			$logger->log( 'Product import error: ' . $e->getMessage() );
377
			$logger->log( $e->getTraceAsString() );
378
379
			throw new \Aimeos\Controller\Jobs\Exception( $e->getMessage() );
380
		}
381
382
		$msg = 'Finished product import from "%1$s": %2$d successful, %3$s errors, %4$s total (%5$s)';
383
		$logger->log( sprintf( $msg, $path, $total - $errors, $errors, $total, __CLASS__ ), \Aimeos\MW\Logger\Base::NOTICE );
384
385
		if( $errors > 0 )
386
		{
387
			$msg = sprintf( 'Invalid product lines in "%1$s": %2$d/%3$d', $path, $errors, $total );
388
			throw new \Aimeos\Controller\Jobs\Exception( $msg );
389
		}
390
391
		if( !empty( $backup ) && @rename( $path, strftime( $backup ) ) === false ) {
392
			throw new \Aimeos\Controller\Jobs\Exception( sprintf( 'Unable to move imported file' ) );
393
		}
394
	}
395
396
397
	/**
398
	 * Returns the position of the "product.code" column from the product item mapping
399
	 *
400
	 * @param array $mapping Mapping of the "item" columns with position as key and code as value
401
	 * @return integer Position of the "product.code" column
402
	 * @throws \Aimeos\Controller\Jobs\Exception If no mapping for "product.code" is found
403
	 */
404
	protected function getCodePosition( array $mapping )
405
	{
406
		foreach( $mapping as $pos => $key )
407
		{
408
			if( $key === 'product.code' ) {
409
				return $pos;
410
			}
411
		}
412
413
		throw new \Aimeos\Controller\Jobs\Exception( sprintf( 'No "product.code" column in CSV mapping found' ) );
414
	}
415
416
417
	/**
418
	 * Opens and returns the container which includes the product data
419
	 *
420
	 * @return \Aimeos\MW\Container\Iface Container object
421
	 */
422
	protected function getContainer()
423
	{
424
		$config = $this->getContext()->getConfig();
425
426
		/** controller/jobs/product/import/csv/location
427
		 * File or directory where the content is stored which should be imported
428
		 *
429
		 * You need to configure the file or directory that acts as container
430
		 * for the CSV files that should be imported. It should be an absolute
431
		 * path to be sure but can be relative path if you absolutely know from
432
		 * where the job will be executed from.
433
		 *
434
		 * The path can point to any supported container format as long as the
435
		 * content is in CSV format, e.g.
436
		 * * Directory container / CSV file
437
		 * * Zip container / compressed CSV file
438
		 * * PHPExcel container / PHPExcel sheet
439
		 *
440
		 * @param string Absolute file or directory path
441
		 * @since 2015.05
442
		 * @category Developer
443
		 * @category User
444
		 * @see controller/jobs/product/import/csv/container/type
445
		 * @see controller/jobs/product/import/csv/container/content
446
		 * @see controller/jobs/product/import/csv/container/options
447
		 */
448
		$location = $config->get( 'controller/jobs/product/import/csv/location' );
449
450
		/** controller/jobs/product/import/csv/container/type
451
		 * Nave of the container type to read the data from
452
		 *
453
		 * The container type tells the importer how it should retrieve the data.
454
		 * There are currently three container types that support the necessary
455
		 * CSV content:
456
		 * * Directory
457
		 * * Zip
458
		 * * PHPExcel
459
		 *
460
		 * '''Note:''' For the PHPExcel container, you need to install the
461
		 * "ai-container" extension.
462
		 *
463
		 * @param string Container type name
464
		 * @since 2015.05
465
		 * @category Developer
466
		 * @category User
467
		 * @see controller/jobs/product/import/csv/location
468
		 * @see controller/jobs/product/import/csv/container/content
469
		 * @see controller/jobs/product/import/csv/container/options
470
		 */
471
		$container = $config->get( 'controller/jobs/product/import/csv/container/type', 'Directory' );
472
473
		/** controller/jobs/product/import/csv/container/content
474
		 * Name of the content type inside the container to read the data from
475
		 *
476
		 * The content type must always be a CSV-like format and there are
477
		 * currently two format types that are supported:
478
		 * * CSV
479
		 * * PHPExcel
480
		 *
481
		 * '''Note:''' for the PHPExcel content type, you need to install the
482
		 * "ai-container" extension.
483
		 *
484
		 * @param array Content type name
485
		 * @since 2015.05
486
		 * @category Developer
487
		 * @category User
488
		 * @see controller/jobs/product/import/csv/location
489
		 * @see controller/jobs/product/import/csv/container/type
490
		 * @see controller/jobs/product/import/csv/container/options
491
		 */
492
		$content = $config->get( 'controller/jobs/product/import/csv/container/content', 'CSV' );
493
494
		/** controller/jobs/product/import/csv/container/options
495
		 * List of file container options for the product import files
496
		 *
497
		 * Some container/content type allow you to hand over additional settings
498
		 * for configuration. Please have a look at the article about
499
		 * {@link http://aimeos.org/docs/Developers/Utility/Create_and_read_files container/content files}
500
		 * for more information.
501
		 *
502
		 * @param array Associative list of option name/value pairs
503
		 * @since 2015.05
504
		 * @category Developer
505
		 * @category User
506
		 * @see controller/jobs/product/import/csv/location
507
		 * @see controller/jobs/product/import/csv/container/content
508
		 * @see controller/jobs/product/import/csv/container/type
509
		 */
510
		$options = $config->get( 'controller/jobs/product/import/csv/container/options', [] );
511
512
		if( $location === null )
513
		{
514
			$msg = sprintf( 'Required configuration for "%1$s" is missing', 'controller/jobs/product/import/csv/location' );
515
			throw new \Aimeos\Controller\Jobs\Exception( $msg );
516
		}
517
518
		return \Aimeos\MW\Container\Factory::getContainer( $location, $container, $content, $options );
519
	}
520
521
522
	/**
523
	 * Imports the CSV data and creates new products or updates existing ones
524
	 *
525
	 * @param array $products List of products items implementing \Aimeos\MShop\Product\Item\Iface
526
	 * @param array $data Associative list of import data as index/value pairs
527
	 * @param array $mapping Associative list of positions and domain item keys
528
	 * @param \Aimeos\Controller\Common\Product\Import\Csv\Processor\Iface $processor Processor object
529
	 * @param boolean $strict Log columns not mapped or silently ignore them
530
	 * @return integer Number of products that couldn't be imported
531
	 * @throws \Aimeos\Controller\Jobs\Exception
532
	 */
533
	protected function import( array $products, array $data, array $mapping,
534
		\Aimeos\Controller\Common\Product\Import\Csv\Processor\Iface $processor, $strict )
535
	{
536
		$items = [];
537
		$errors = 0;
538
		$context = $this->getContext();
539
		$manager = \Aimeos\MShop\Factory::createManager( $context, 'product' );
540
		$indexManager = \Aimeos\MShop\Factory::createManager( $context, 'index' );
541
542
		foreach( $data as $code => $list )
543
		{
544
			$manager->begin();
545
546
			try
547
			{
548
				$code = trim( $code );
549
550
				if( isset( $products[$code] ) ) {
551
					$product = $products[$code];
552
				} else {
553
					$product = $manager->createItem();
554
				}
555
556
				$map = $this->getMappedChunk( $list, $mapping );
557
558
				if( isset( $map[0] ) )
559
				{
560
					$map = $map[0]; // there can only be one chunk for the base product data
561
					$map['product.type'] = trim( $this->getValue( $map, 'product.type', 'default' ) );
0 ignored issues
show
Documentation Bug introduced by
The method getValue does not exist on object<Aimeos\Controller...ct\Import\Csv\Standard>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
562
563
					if( !in_array( $map['product.type'], $this->types ) )
564
					{
565
						$msg = sprintf( 'Invalid product type "%1$s"', $map['product.type'] );
566
						throw new \Aimeos\Controller\Jobs\Exception( $msg );
567
					}
568
569
					$product->fromArray( $this->addItemDefaults( $map ) );
570
					$product = $manager->saveItem( $product );
571
572
					$list = $processor->process( $product, $list );
573
574
					$product = $manager->saveItem( $product );
575
					$items[$product->getId()] = $product;
576
				}
577
578
				$manager->commit();
579
			}
580
			catch( \Exception $e )
581
			{
582
				$manager->rollback();
583
584
				$msg = sprintf( 'Unable to import product with code "%1$s": %2$s', $code, $e->getMessage() );
585
				$context->getLogger()->log( $msg );
586
587
				$errors++;
588
			}
589
590
			if( $strict && !empty( $list ) ) {
591
				$context->getLogger()->log( 'Not imported: ' . print_r( $list, true ) );
592
			}
593
		}
594
595
		$indexManager->rebuildIndex( $items );
596
597
		return $errors;
598
	}
599
600
601
	/**
602
	 * Adds the product item default values and returns the resulting array
603
	 *
604
	 * @param array $list Associative list of domain item keys and their values, e.g. "product.status" => 1
605
	 * @return array Given associative list enriched by default values if they were not already set
606
	 */
607
	protected function addItemDefaults( array $list )
608
	{
609
		if( !isset( $list['product.status'] ) ) {
610
			$list['product.status'] = 1;
611
		}
612
613
		return $list;
614
	}
615
}
616