Standard   D
last analyzed

Complexity

Total Complexity 59

Size/Duplication

Total Lines 1179
Duplicated Lines 0 %

Importance

Changes 8
Bugs 0 Features 0
Metric Value
wmc 59
eloc 321
dl 0
loc 1179
rs 4.08
c 8
b 0
f 0

22 Methods

Rating   Name   Duplication   Size   Complexity  
A delete() 0 30 6
A move() 0 27 2
A saveChildren() 0 26 5
A get() 0 3 1
A updateUsage() 0 130 4
A getSubManager() 0 3 1
A __construct() 0 38 5
A search() 0 172 3
A saveItem() 0 16 2
A lock() 0 28 2
A filter() 0 3 1
A insert() 0 28 2
A find() 0 4 1
A getPath() 0 33 6
A clear() 0 56 2
A commit() 0 8 1
B getTree() 0 58 9
A unlock() 0 29 2
A getSearchAttributes() 0 22 1
A getResourceType() 0 4 1
A save() 0 7 1
A create() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Standard often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Standard, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0
5
 * @copyright Metaways Infosystems GmbH, 2011
6
 * @copyright Aimeos (aimeos.org), 2015-2024
7
 * @package MShop
8
 * @subpackage Catalog
9
 */
10
11
12
namespace Aimeos\MShop\Catalog\Manager;
13
14
15
/**
16
 * Catalog manager with methods for managing categories products, text, media.
17
 *
18
 * @package MShop
19
 * @subpackage Catalog
20
 */
21
class Standard extends Base
22
	implements \Aimeos\MShop\Catalog\Manager\Iface, \Aimeos\MShop\Common\Manager\Factory\Iface
23
{
24
	/** mshop/catalog/manager/name
25
	 * Class name of the used catalog manager implementation
26
	 *
27
	 * Each default manager 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 manager factory knows which class it
30
	 * has to instantiate.
31
	 *
32
	 * For example, if the name of the default class is
33
	 *
34
	 *  \Aimeos\MShop\Catalog\Manager\Standard
35
	 *
36
	 * and you want to replace it with your own version named
37
	 *
38
	 *  \Aimeos\MShop\Catalog\Manager\Mymanager
39
	 *
40
	 * then you have to set the this configuration option:
41
	 *
42
	 *  mshop/catalog/manager/name = Mymanager
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 "MyManager"!
52
	 *
53
	 * @param string Last part of the class name
54
	 * @since 2014.03
55
	 * @category Developer
56
	 */
57
58
	/** mshop/catalog/manager/decorators/excludes
59
	 * Excludes decorators added by the "common" option from the catalog manager
60
	 *
61
	 * Decorators extend the functionality of a class by adding new aspects
62
	 * (e.g. log what is currently done), executing the methods of the underlying
63
	 * class only in certain conditions (e.g. only for logged in users) or
64
	 * modify what is returned to the caller.
65
	 *
66
	 * This option allows you to remove a decorator added via
67
	 * "mshop/common/manager/decorators/default" before they are wrapped
68
	 * around the catalog manager.
69
	 *
70
	 *  mshop/catalog/manager/decorators/excludes = array( 'decorator1' )
71
	 *
72
	 * This would remove the decorator named "decorator1" from the list of
73
	 * common decorators ("\Aimeos\MShop\Common\Manager\Decorator\*") added via
74
	 * "mshop/common/manager/decorators/default" for the catalog manager.
75
	 *
76
	 * @param array List of decorator names
77
	 * @since 2014.03
78
	 * @category Developer
79
	 * @see mshop/common/manager/decorators/default
80
	 * @see mshop/catalog/manager/decorators/global
81
	 * @see mshop/catalog/manager/decorators/local
82
	 */
83
84
	/** mshop/catalog/manager/decorators/global
85
	 * Adds a list of globally available decorators only to the catalog manager
86
	 *
87
	 * Decorators extend the functionality of a class by adding new aspects
88
	 * (e.g. log what is currently done), executing the methods of the underlying
89
	 * class only in certain conditions (e.g. only for logged in users) or
90
	 * modify what is returned to the caller.
91
	 *
92
	 * This option allows you to wrap global decorators
93
	 * ("\Aimeos\MShop\Common\Manager\Decorator\*") around the catalog manager.
94
	 *
95
	 *  mshop/catalog/manager/decorators/global = array( 'decorator1' )
96
	 *
97
	 * This would add the decorator named "decorator1" defined by
98
	 * "\Aimeos\MShop\Common\Manager\Decorator\Decorator1" only to the catalog
99
	 * manager.
100
	 *
101
	 * @param array List of decorator names
102
	 * @since 2014.03
103
	 * @category Developer
104
	 * @see mshop/common/manager/decorators/default
105
	 * @see mshop/catalog/manager/decorators/excludes
106
	 * @see mshop/catalog/manager/decorators/local
107
	 */
108
109
	/** mshop/catalog/manager/decorators/local
110
	 * Adds a list of local decorators only to the catalog manager
111
	 *
112
	 * Decorators extend the functionality of a class by adding new aspects
113
	 * (e.g. log what is currently done), executing the methods of the underlying
114
	 * class only in certain conditions (e.g. only for logged in users) or
115
	 * modify what is returned to the caller.
116
	 *
117
	 * This option allows you to wrap local decorators
118
	 * ("\Aimeos\MShop\Catalog\Manager\Decorator\*") around the catalog manager.
119
	 *
120
	 *  mshop/catalog/manager/decorators/local = array( 'decorator2' )
121
	 *
122
	 * This would add the decorator named "decorator2" defined by
123
	 * "\Aimeos\MShop\Catalog\Manager\Decorator\Decorator2" only to the catalog
124
	 * manager.
125
	 *
126
	 * @param array List of decorator names
127
	 * @since 2014.03
128
	 * @category Developer
129
	 * @see mshop/common/manager/decorators/default
130
	 * @see mshop/catalog/manager/decorators/excludes
131
	 * @see mshop/catalog/manager/decorators/global
132
	 */
133
134
135
	private array $searchConfig = array(
136
		'id' => array(
137
			'code' => 'catalog.id',
138
			'internalcode' => 'mcat."id"',
139
			'label' => 'ID',
140
			'type' => 'int',
141
			'public' => false,
142
		),
143
		'catalog.siteid' => array(
144
			'code' => 'catalog.siteid',
145
			'internalcode' => 'mcat."siteid"',
146
			'label' => 'Site ID',
147
			'type' => 'string',
148
			'public' => false,
149
		),
150
		'parentid' => array(
151
			'code' => 'catalog.parentid',
152
			'internalcode' => 'mcat."parentid"',
153
			'label' => 'Parent ID',
154
			'type' => 'int',
155
			'public' => false,
156
		),
157
		'level' => array(
158
			'code' => 'catalog.level',
159
			'internalcode' => 'mcat."level"',
160
			'label' => 'Tree level',
161
			'type' => 'int',
162
			'public' => false,
163
		),
164
		'left' => array(
165
			'code' => 'catalog.left',
166
			'internalcode' => 'mcat."nleft"',
167
			'label' => 'Left value',
168
			'type' => 'int',
169
			'public' => false,
170
		),
171
		'right' => array(
172
			'code' => 'catalog.right',
173
			'internalcode' => 'mcat."nright"',
174
			'label' => 'Right value',
175
			'type' => 'int',
176
			'public' => false,
177
		),
178
		'label' => array(
179
			'code' => 'catalog.label',
180
			'internalcode' => 'mcat."label"',
181
			'label' => 'Label',
182
			'type' => 'string',
183
		),
184
		'code' => array(
185
			'code' => 'catalog.code',
186
			'internalcode' => 'mcat."code"',
187
			'label' => 'Code',
188
			'type' => 'string',
189
		),
190
		'status' => array(
191
			'code' => 'catalog.status',
192
			'internalcode' => 'mcat."status"',
193
			'label' => 'Status',
194
			'type' => 'int',
195
		),
196
		'catalog.url' => array(
197
			'code' => 'catalog.url',
198
			'internalcode' => 'mcat."url"',
199
			'label' => 'URL segment',
200
			'type' => 'string',
201
		),
202
		'catalog.target' => array(
203
			'code' => 'catalog.target',
204
			'internalcode' => 'mcat."target"',
205
			'label' => 'URL target',
206
			'type' => 'string',
207
		),
208
		'catalog.config' => array(
209
			'code' => 'catalog.config',
210
			'internalcode' => 'mcat."config"',
211
			'label' => 'Config',
212
			'type' => 'json',
213
			'public' => false,
214
		),
215
		'catalog.ctime' => array(
216
			'label' => 'Create date/time',
217
			'code' => 'catalog.ctime',
218
			'internalcode' => 'mcat."ctime"',
219
			'type' => 'datetime',
220
			'public' => false,
221
		),
222
		'catalog.mtime' => array(
223
			'label' => 'Modify date/time',
224
			'code' => 'catalog.mtime',
225
			'internalcode' => 'mcat."mtime"',
226
			'type' => 'datetime',
227
			'public' => false,
228
		),
229
		'catalog.editor' => array(
230
			'code' => 'catalog.editor',
231
			'internalcode' => 'mcat."editor"',
232
			'label' => 'Editor',
233
			'type' => 'string',
234
			'public' => false,
235
		),
236
		'catalog:has' => array(
237
			'code' => 'catalog:has()',
238
			'internalcode' => ':site AND :key AND mcatli."id"',
239
			'internaldeps' => ['LEFT JOIN "mshop_catalog_list" AS mcatli ON ( mcatli."parentid" = mcat."id" )'],
240
			'label' => 'Catalog has list item, parameter(<domain>[,<list type>[,<reference ID>)]]',
241
			'type' => 'null',
242
			'public' => false,
243
		),
244
		'sort:catalog:position' => array(
245
			'code' => 'sort:catalog:position',
246
			'internalcode' => 'mcat."nleft"',
247
			'label' => 'Category position',
248
			'type' => 'int',
249
			'public' => false,
250
		),
251
	);
252
253
	private array $cacheTags = [];
254
255
256
	/**
257
	 * Initializes the object.
258
	 *
259
	 * @param \Aimeos\MShop\ContextIface $context Context object
260
	 */
261
	public function __construct( \Aimeos\MShop\ContextIface $context )
262
	{
263
		$level = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
264
		$level = $context->config()->get( 'mshop/catalog/manager/sitemode', $level );
265
266
267
		$this->searchConfig['catalog:has']['function'] = function( &$source, array $params ) use ( $level ) {
268
269
			$keys = [];
270
271
			foreach( (array) ( $params[1] ?? '' ) as $type ) {
272
				foreach( (array) ( $params[2] ?? '' ) as $id ) {
273
					$keys[] = $params[0] . '|' . ( $type ? $type . '|' : '' ) . $id;
274
				}
275
			}
276
277
			$sitestr = $this->siteString( 'mcatli."siteid"', $level );
278
			$keystr = $this->toExpression( 'mcatli."key"', $keys, ( $params[2] ?? null ) ? '==' : '=~' );
279
			$source = str_replace( [':site', ':key'], [$sitestr, $keystr], $source );
280
281
			return $params;
282
		};
283
284
285
		parent::__construct( $context, $this->searchConfig );
286
287
		/** mshop/catalog/manager/resource
288
		 * Name of the database connection resource to use
289
		 *
290
		 * You can configure a different database connection for each data domain
291
		 * and if no such connection name exists, the "db" connection will be used.
292
		 * It's also possible to use the same database connection for different
293
		 * data domains by configuring the same connection name using this setting.
294
		 *
295
		 * @param string Database connection name
296
		 * @since 2023.04
297
		 */
298
		$this->setResourceName( $context->config()->get( 'mshop/catalog/manager/resource', 'db-catalog' ) );
299
	}
300
301
302
	/**
303
	 * Removes old entries from the storage.
304
	 *
305
	 * @param iterable $siteids List of IDs for sites whose entries should be deleted
306
	 * @return \Aimeos\MShop\Catalog\Manager\Iface Manager object for chaining method calls
307
	 */
308
	public function clear( iterable $siteids ) : \Aimeos\MShop\Common\Manager\Iface
309
	{
310
		$context = $this->context();
311
		$config = $context->config();
312
		$search = $this->object()->filter();
313
314
		foreach( $config->get( 'mshop/catalog/manager/submanagers', ['lists'] ) as $domain ) {
315
			$this->object()->getSubManager( $domain )->clear( $siteids );
316
		}
317
318
		$conn = $context->db( $this->getResourceName() );
319
320
		/** mshop/catalog/manager/cleanup/mysql
321
		 * Deletes the categories for the given site from the database
322
		 *
323
		 * @see mshop/catalog/manager/cleanup/ansi
324
		 */
325
326
		/** mshop/catalog/manager/cleanup/ansi
327
		 * Deletes the categories for the given site from the database
328
		 *
329
		 * Removes the records matched by the given site ID from the catalog
330
		 * database.
331
		 *
332
		 * The ":siteid" placeholder is replaced by the name and value of the
333
		 * site ID column and the given ID or list of IDs.
334
		 *
335
		 * The SQL statement should conform to the ANSI standard to be
336
		 * compatible with most relational database systems. This also
337
		 * includes using double quotes for table and column names.
338
		 *
339
		 * @param string SQL statement for removing the records
340
		 * @since 2014.03
341
		 * @category Developer
342
		 * @see mshop/catalog/manager/delete/ansi
343
		 * @see mshop/catalog/manager/insert/ansi
344
		 * @see mshop/catalog/manager/update/ansi
345
		 * @see mshop/catalog/manager/newid/ansi
346
		 * @see mshop/catalog/manager/search/ansi
347
		 * @see mshop/catalog/manager/count/ansi
348
		 */
349
		$path = 'mshop/catalog/manager/cleanup';
350
		$sql = $this->getSqlConfig( $path );
351
352
		$types = array( 'siteid' => \Aimeos\Base\DB\Statement\Base::PARAM_STR );
353
		$translations = array( 'siteid' => '"siteid"' );
354
355
		$search->setConditions( $search->compare( '==', 'siteid', $siteids ) );
356
		$sql = str_replace( ':siteid', $search->getConditionSource( $types, $translations ), $sql );
357
358
		$stmt = $conn->create( $sql );
359
		$stmt->bind( 1, 0, \Aimeos\Base\DB\Statement\Base::PARAM_INT );
360
		$stmt->bind( 2, 0x7FFFFFFF, \Aimeos\Base\DB\Statement\Base::PARAM_INT );
361
		$stmt->execute()->finish();
362
363
		return $this;
364
	}
365
366
367
	/**
368
	 * Commits the running database transaction on the connection identified by the given name
369
	 *
370
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
371
	 */
372
	public function commit() : \Aimeos\MShop\Common\Manager\Iface
373
	{
374
		parent::commit();
375
376
		$this->context()->cache()->deleteByTags( $this->cacheTags );
377
		$this->cacheTags = [];
378
379
		return $this;
380
	}
381
382
383
	/**
384
	 * Creates a new empty item instance
385
	 *
386
	 * @param array $values Values the item should be initialized with
387
	 * @return \Aimeos\MShop\Catalog\Item\Iface New catalog item object
388
	 */
389
	public function create( array $values = [] ) : \Aimeos\MShop\Common\Item\Iface
390
	{
391
		$values['siteid'] = $values['siteid'] ?? $this->context()->locale()->getSiteId();
392
		return $this->createItemBase( $values );
393
	}
394
395
396
	/**
397
	 * Creates a filter object.
398
	 *
399
	 * @param bool|null $default Add default criteria or NULL for relaxed default criteria
400
	 * @param bool $site TRUE for adding site criteria to limit items by the site of related items
401
	 * @return \Aimeos\Base\Criteria\Iface Returns the filter object
402
	 */
403
	public function filter( ?bool $default = false, bool $site = false ) : \Aimeos\Base\Criteria\Iface
404
	{
405
		return $this->filterBase( 'catalog', $default );
406
	}
407
408
409
	/**
410
	 * Removes multiple items.
411
	 *
412
	 * @param \Aimeos\MShop\Common\Item\Iface|array|string $items List of item objects or IDs of the items
413
	 * @return \Aimeos\MShop\Catalog\Manager\Iface Manager object for chaining method calls
414
	 */
415
	public function delete( $items ) : \Aimeos\MShop\Common\Manager\Iface
416
	{
417
		if( is_map( $items ) ) { $items = $items->toArray(); }
418
		if( !is_array( $items ) ) { $items = [$items]; }
419
		if( empty( $items ) ) { return $this; }
420
421
		$this->begin();
422
		$this->lock();
423
424
		try
425
		{
426
			$siteid = $this->context()->locale()->getSiteId();
427
428
			foreach( $items as $item ) {
429
				$this->createTreeManager( $siteid )->deleteNode( (string) $item );
430
			}
431
432
			$this->cacheTags = array_merge( $this->cacheTags, map( $items )->cast()->prefix( 'catalog-' )->all() );
433
434
			$this->unlock();
435
			$this->commit();
436
		}
437
		catch( \Exception $e )
438
		{
439
			$this->unlock();
440
			$this->rollback();
441
			throw $e;
442
		}
443
444
		return $this->deleteRefItems( $items );
445
	}
446
447
448
	/**
449
	 * Returns the item specified by its code and domain/type if necessary
450
	 *
451
	 * @param string $code Code of the item
452
	 * @param string[] $ref List of domains to fetch list items and referenced items for
453
	 * @param string|null $domain Domain of the item if necessary to identify the item uniquely
454
	 * @param string|null $type Type code of the item if necessary to identify the item uniquely
455
	 * @param bool|null $default Add default criteria or NULL for relaxed default criteria
456
	 * @return \Aimeos\MShop\Catalog\Item\Iface Catalog item object
457
	 */
458
	public function find( string $code, array $ref = [], string $domain = null, string $type = null,
459
		?bool $default = false ) : \Aimeos\MShop\Common\Item\Iface
460
	{
461
		return $this->findBase( array( 'catalog.code' => $code ), $ref, $default );
462
	}
463
464
465
	/**
466
	 * Returns the item specified by its ID.
467
	 *
468
	 * @param string $id Unique ID of the catalog item
469
	 * @param string[] $ref List of domains to fetch list items and referenced items for
470
	 * @param bool|null $default Add default criteria or NULL for relaxed default criteria
471
	 * @return \Aimeos\MShop\Catalog\Item\Iface Catalog item of the given ID
472
	 * @throws \Aimeos\MShop\Exception If item couldn't be found
473
	 */
474
	public function get( string $id, array $ref = [], ?bool $default = false ) : \Aimeos\MShop\Common\Item\Iface
475
	{
476
		return $this->getItemBase( 'catalog.id', $id, $ref, $default );
477
	}
478
479
480
	/**
481
	 * Returns the available manager types
482
	 *
483
	 * @param bool $withsub Return also the resource type of sub-managers if true
484
	 * @return string[] Type of the manager and submanagers, subtypes are separated by slashes
485
	 */
486
	public function getResourceType( bool $withsub = true ) : array
487
	{
488
		$path = 'mshop/catalog/manager/submanagers';
489
		return $this->getResourceTypeBase( 'catalog', $path, array( 'lists' ), $withsub );
490
	}
491
492
493
	/**
494
	 * Returns the attributes that can be used for searching.
495
	 *
496
	 * @param bool $withsub Return also attributes of sub-managers if true
497
	 * @return \Aimeos\Base\Criteria\Attribute\Iface[] List of search attribute items
498
	 */
499
	public function getSearchAttributes( bool $withsub = true ) : array
500
	{
501
		/** mshop/catalog/manager/submanagers
502
		 * List of manager names that can be instantiated by the catalog manager
503
		 *
504
		 * Managers provide a generic interface to the underlying storage.
505
		 * Each manager has or can have sub-managers caring about particular
506
		 * aspects. Each of these sub-managers can be instantiated by its
507
		 * parent manager using the getSubManager() method.
508
		 *
509
		 * The search keys from sub-managers can be normally used in the
510
		 * manager as well. It allows you to search for items of the manager
511
		 * using the search keys of the sub-managers to further limit the
512
		 * retrieved list of items.
513
		 *
514
		 * @param array List of sub-manager names
515
		 * @since 2014.03
516
		 * @category Developer
517
		 */
518
		$path = 'mshop/catalog/manager/submanagers';
519
520
		return $this->getSearchAttributesBase( $this->searchConfig, $path, [], $withsub );
521
	}
522
523
524
	/**
525
	 * Adds a new item object.
526
	 *
527
	 * @param \Aimeos\MShop\Catalog\Item\Iface $item Item which should be inserted
528
	 * @param string|null $parentId ID of the parent item where the item should be inserted into
529
	 * @param string|null $refId ID of the item where the item should be inserted before (null to append)
530
	 * @return \Aimeos\MShop\Catalog\Item\Iface $item Updated item including the generated ID
531
	 */
532
	public function insert( \Aimeos\MShop\Catalog\Item\Iface $item, string $parentId = null,
533
		string $refId = null ) : \Aimeos\MShop\Catalog\Item\Iface
534
	{
535
		$this->begin();
536
		$this->lock();
537
538
		try
539
		{
540
			$node = $item->getNode();
0 ignored issues
show
Bug introduced by
The method getNode() does not exist on Aimeos\MShop\Catalog\Item\Iface. Did you maybe mean getCode()? ( Ignorable by Annotation )

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

540
			/** @scrutinizer ignore-call */ 
541
   $node = $item->getNode();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
541
			$siteid = $this->context()->locale()->getSiteId();
542
543
			$this->createTreeManager( $siteid )->insertNode( $node, $parentId, $refId );
544
			$this->updateUsage( $node->getId(), $item, true );
545
546
			$this->cacheTags[] = 'catalog';
547
548
			$this->unlock();
549
			$this->commit();
550
		}
551
		catch( \Exception $e )
552
		{
553
			$this->unlock();
554
			$this->rollback();
555
			throw $e;
556
		}
557
558
		$item = $this->saveListItems( $item, 'catalog' );
559
		return $this->saveChildren( $item );
560
	}
561
562
563
	/**
564
	 * Moves an existing item to the new parent in the storage.
565
	 *
566
	 * @param string $id ID of the item that should be moved
567
	 * @param string|null $oldParentId ID of the old parent item which currently contains the item that should be removed
568
	 * @param string|null $newParentId ID of the new parent item where the item should be moved to
569
	 * @param string|null $refId ID of the item where the item should be inserted before (null to append)
570
	 * @return \Aimeos\MShop\Catalog\Manager\Iface Manager object for chaining method calls
571
	 */
572
	public function move( string $id, string $oldParentId = null, string $newParentId = null,
573
		string $refId = null ) : \Aimeos\MShop\Catalog\Manager\Iface
574
	{
575
		$this->begin();
576
		$this->lock();
577
578
		try
579
		{
580
			$item = $this->object()->get( $id );
581
			$siteid = $this->context()->locale()->getSiteId();
582
583
			$this->createTreeManager( $siteid )->moveNode( $id, $oldParentId, $newParentId, $refId );
584
			$this->updateUsage( $id, $item );
585
586
			$this->cacheTags[] = 'catalog';
587
588
			$this->unlock();
589
			$this->commit();
590
		}
591
		catch( \Exception $e )
592
		{
593
			$this->unlock();
594
			$this->rollback();
595
			throw $e;
596
		}
597
598
		return $this;
599
	}
600
601
602
	/**
603
	 * Adds or updates an item object or a list of them.
604
	 *
605
	 * @param \Aimeos\Map|\Aimeos\MShop\Common\Item\Iface[]|\Aimeos\MShop\Common\Item\Iface $items Item or list of items whose data should be saved
606
	 * @param bool $fetch True if the new ID should be returned in the item
607
	 * @return \Aimeos\Map|\Aimeos\MShop\Common\Item\Iface Saved item or items
608
	 */
609
	public function save( $items, bool $fetch = true )
610
	{
611
		$items = parent::save( $items, $fetch );
612
613
		$this->cacheTags = array_merge( $this->cacheTags, map( $items )->getId()->prefix( 'catalog-' )->all() );
614
615
		return $items;
616
	}
617
618
619
	/**
620
	 * Updates an item object.
621
	 *
622
	 * @param \Aimeos\MShop\Catalog\Item\Iface $item Item object whose data should be saved
623
	 * @param bool $fetch True if the new ID should be returned in the item
624
	 * @return \Aimeos\MShop\Catalog\Item\Iface $item Updated item including the generated ID
625
	 */
626
	protected function saveItem( \Aimeos\MShop\Catalog\Item\Iface $item, bool $fetch = true ) : \Aimeos\MShop\Catalog\Item\Iface
627
	{
628
		if( !$item->isModified() )
629
		{
630
			$item = $this->saveListItems( $item, 'catalog', $fetch );
631
			return $this->saveChildren( $item );
632
		}
633
634
		$node = $item->getNode();
635
		$siteid = $this->context()->locale()->getSiteId();
636
637
		$this->createTreeManager( $siteid )->saveNode( $node );
638
		$this->updateUsage( $node->getId(), $item );
639
640
		$item = $this->saveListItems( $item, 'catalog', $fetch );
641
		return $this->saveChildren( $item );
642
	}
643
644
645
	/**
646
	 * Searches for all items matching the given critera.
647
	 *
648
	 * @param \Aimeos\Base\Criteria\Iface $search Search criteria object
649
	 * @param string[] $ref List of domains to fetch list items and referenced items for
650
	 * @param int|null &$total Number of items that are available in total
651
	 * @return \Aimeos\Map List of items implementing \Aimeos\MShop\Catalog\Item\Iface with ids as keys
652
	 */
653
	public function search( \Aimeos\Base\Criteria\Iface $search, array $ref = [], int &$total = null ) : \Aimeos\Map
654
	{
655
		$map = [];
656
		$required = ['catalog'];
657
		$context = $this->context();
658
		$conn = $context->db( $this->getResourceName() );
659
660
		/** mshop/catalog/manager/sitemode
661
		 * Mode how items from levels below or above in the site tree are handled
662
		 *
663
		 * By default, only items from the current site are fetched from the
664
		 * storage. If the ai-sites extension is installed, you can create a
665
		 * tree of sites. Then, this setting allows you to define for the
666
		 * whole catalog domain if items from parent sites are inherited,
667
		 * sites from child sites are aggregated or both.
668
		 *
669
		 * Available constants for the site mode are:
670
		 * * 0 = only items from the current site
671
		 * * 1 = inherit items from parent sites
672
		 * * 2 = aggregate items from child sites
673
		 * * 3 = inherit and aggregate items at the same time
674
		 *
675
		 * You also need to set the mode in the locale manager
676
		 * (mshop/locale/manager/sitelevel) to one of the constants.
677
		 * If you set it to the same value, it will work as described but you
678
		 * can also use different modes. For example, if inheritance and
679
		 * aggregation is configured the locale manager but only inheritance
680
		 * in the domain manager because aggregating items makes no sense in
681
		 * this domain, then items wil be only inherited. Thus, you have full
682
		 * control over inheritance and aggregation in each domain.
683
		 *
684
		 * @param int Constant from Aimeos\MShop\Locale\Manager\Base class
685
		 * @category Developer
686
		 * @since 2018.01
687
		 * @see mshop/locale/manager/sitelevel
688
		 */
689
		$level = \Aimeos\MShop\Locale\Manager\Base::SITE_PATH;
690
		$level = $context->config()->get( 'mshop/catalog/manager/sitemode', $level );
691
692
		/** mshop/catalog/manager/search-item/mysql
693
		 * Retrieves the records matched by the given criteria in the database
694
		 *
695
		 * @see mshop/catalog/manager/search-item/ansi
696
		 */
697
698
		/** mshop/catalog/manager/search-item/ansi
699
		 * Retrieves the records matched by the given criteria in the database
700
		 *
701
		 * Fetches the records matched by the given criteria from the catalog
702
		 * database. The records must be from one of the sites that are
703
		 * configured via the context item. If the current site is part of
704
		 * a tree of sites, the SELECT statement can retrieve all records
705
		 * from the current site and the complete sub-tree of sites.
706
		 *
707
		 * As the records can normally be limited by criteria from sub-managers,
708
		 * their tables must be joined in the SQL context. This is done by
709
		 * using the "internaldeps" property from the definition of the ID
710
		 * column of the sub-managers. These internal dependencies specify
711
		 * the JOIN between the tables and the used columns for joining. The
712
		 * ":joins" placeholder is then replaced by the JOIN strings from
713
		 * the sub-managers.
714
		 *
715
		 * To limit the records matched, conditions can be added to the given
716
		 * criteria object. It can contain comparisons like column names that
717
		 * must match specific values which can be combined by AND, OR or NOT
718
		 * operators. The resulting string of SQL conditions replaces the
719
		 * ":cond" placeholder before the statement is sent to the database
720
		 * server.
721
		 *
722
		 * If the records that are retrieved should be ordered by one or more
723
		 * columns, the generated string of column / sort direction pairs
724
		 * replaces the ":order" placeholder. In case no ordering is required,
725
		 * the complete ORDER BY part including the "\/*-orderby*\/...\/*orderby-*\/"
726
		 * markers is removed to speed up retrieving the records. Columns of
727
		 * sub-managers can also be used for ordering the result set but then
728
		 * no index can be used.
729
		 *
730
		 * The number of returned records can be limited and can start at any
731
		 * number between the begining and the end of the result set. For that
732
		 * the ":size" and ":start" placeholders are replaced by the
733
		 * corresponding values from the criteria object. The default values
734
		 * are 0 for the start and 100 for the size value.
735
		 *
736
		 * The SQL statement should conform to the ANSI standard to be
737
		 * compatible with most relational database systems. This also
738
		 * includes using double quotes for table and column names.
739
		 *
740
		 * @param string SQL statement for searching items
741
		 * @since 2014.03
742
		 * @category Developer
743
		 * @see mshop/catalog/manager/delete/ansi
744
		 * @see mshop/catalog/manager/get/ansi
745
		 * @see mshop/catalog/manager/insert/ansi
746
		 * @see mshop/catalog/manager/update/ansi
747
		 * @see mshop/catalog/manager/newid/ansi
748
		 * @see mshop/catalog/manager/search/ansi
749
		 * @see mshop/catalog/manager/count/ansi
750
		 * @see mshop/catalog/manager/move-left/ansi
751
		 * @see mshop/catalog/manager/move-right/ansi
752
		 * @see mshop/catalog/manager/update-parentid/ansi
753
		 */
754
		$cfgPathSearch = 'mshop/catalog/manager/search-item';
755
756
		/** mshop/catalog/manager/count/mysql
757
		 * Counts the number of records matched by the given criteria in the database
758
		 *
759
		 * @see mshop/catalog/manager/count/ansi
760
		 */
761
762
		/** mshop/catalog/manager/count/ansi
763
		 * Counts the number of records matched by the given criteria in the database
764
		 *
765
		 * Counts all records matched by the given criteria from the catalog
766
		 * database. The records must be from one of the sites that are
767
		 * configured via the context item. If the current site is part of
768
		 * a tree of sites, the statement can count all records from the
769
		 * current site and the complete sub-tree of sites.
770
		 *
771
		 * As the records can normally be limited by criteria from sub-managers,
772
		 * their tables must be joined in the SQL context. This is done by
773
		 * using the "internaldeps" property from the definition of the ID
774
		 * column of the sub-managers. These internal dependencies specify
775
		 * the JOIN between the tables and the used columns for joining. The
776
		 * ":joins" placeholder is then replaced by the JOIN strings from
777
		 * the sub-managers.
778
		 *
779
		 * To limit the records matched, conditions can be added to the given
780
		 * criteria object. It can contain comparisons like column names that
781
		 * must match specific values which can be combined by AND, OR or NOT
782
		 * operators. The resulting string of SQL conditions replaces the
783
		 * ":cond" placeholder before the statement is sent to the database
784
		 * server.
785
		 *
786
		 * Both, the strings for ":joins" and for ":cond" are the same as for
787
		 * the "search" SQL statement.
788
		 *
789
		 * Contrary to the "search" statement, it doesn't return any records
790
		 * but instead the number of records that have been found. As counting
791
		 * thousands of records can be a long running task, the maximum number
792
		 * of counted records is limited for performance reasons.
793
		 *
794
		 * The SQL statement should conform to the ANSI standard to be
795
		 * compatible with most relational database systems. This also
796
		 * includes using double quotes for table and column names.
797
		 *
798
		 * @param string SQL statement for counting items
799
		 * @since 2014.03
800
		 * @category Developer
801
		 * @see mshop/catalog/manager/delete/ansi
802
		 * @see mshop/catalog/manager/get/ansi
803
		 * @see mshop/catalog/manager/insert/ansi
804
		 * @see mshop/catalog/manager/update/ansi
805
		 * @see mshop/catalog/manager/newid/ansi
806
		 * @see mshop/catalog/manager/search/ansi
807
		 * @see mshop/catalog/manager/search-item/ansi
808
		 * @see mshop/catalog/manager/move-left/ansi
809
		 * @see mshop/catalog/manager/move-right/ansi
810
		 * @see mshop/catalog/manager/update-parentid/ansi
811
		 */
812
		$cfgPathCount = 'mshop/catalog/manager/count';
813
814
		if( $search->getSortations() === [] ) {
815
			$search->setSortations( [$search->sort( '+', 'catalog.left' )] );
816
		}
817
818
		$results = $this->searchItemsBase( $conn, $search, $cfgPathSearch, $cfgPathCount, $required, $total, $level );
819
820
		while( ( $row = $results->fetch() ) !== null ) {
821
			$map[$row['id']] = new \Aimeos\MW\Tree\Node\Standard( $row );
822
		}
823
824
		return $this->buildItems( $map, $ref, 'catalog' );
825
	}
826
827
828
	/**
829
	 * Returns a list of items starting with the given category that are in the path to the root node
830
	 *
831
	 * @param string $id ID of item to get the path for
832
	 * @param string[] $ref List of domains to fetch list items and referenced items for
833
	 * @return \Aimeos\Map Associative list of catalog items implementing \Aimeos\MShop\Catalog\Item\Iface with IDs as keys
834
	 */
835
	public function getPath( string $id, array $ref = [] ) : \Aimeos\Map
836
	{
837
		$mode = \Aimeos\MShop\Locale\Manager\Base::SITE_PATH;
838
		$mode = $this->context()->config()->get( 'mshop/catalog/manager/sitemode', $mode );
839
840
		if( $mode !== \Aimeos\MShop\Locale\Manager\Base::SITE_ONE ) {
841
			$sitePath = array_reverse( (array) $this->context()->locale()->getSitePath() );
842
		} else {
843
			$sitePath = [$this->context()->locale()->getSiteId()];
844
		}
845
846
		foreach( $sitePath as $siteId )
847
		{
848
			try {
849
				$path = $this->createTreeManager( $siteId )->getPath( $id );
850
			} catch( \Exception $e ) {
851
				continue;
852
			}
853
854
			if( !empty( $path ) )
855
			{
856
				$itemMap = [];
857
858
				foreach( $path as $node ) {
859
					$itemMap[$node->getId()] = $node;
860
				}
861
862
				return $this->buildItems( $itemMap, $ref, 'catalog' );
863
			}
864
		}
865
866
		$msg = $this->context()->translate( 'mshop', 'Catalog path for ID "%1$s" not found' );
867
		throw new \Aimeos\MShop\Catalog\Exception( sprintf( $msg, $id ), 404 );
868
	}
869
870
871
	/**
872
	 * Returns a node and its descendants depending on the given resource.
873
	 *
874
	 * @param string|null $id Retrieve nodes starting from the given ID
875
	 * @param string[] List of domains (e.g. text, media, etc.) whose referenced items should be attached to the objects
876
	 * @param int $level One of the level constants from \Aimeos\MW\Tree\Manager\Base
877
	 * @param \Aimeos\Base\Criteria\Iface|null $criteria Optional criteria object with conditions
878
	 * @return \Aimeos\MShop\Catalog\Item\Iface Catalog item, maybe with subnodes
879
	 */
880
	public function getTree( string $id = null, array $ref = [], int $level = \Aimeos\MW\Tree\Manager\Base::LEVEL_TREE,
881
		\Aimeos\Base\Criteria\Iface $criteria = null ) : \Aimeos\MShop\Catalog\Item\Iface
882
	{
883
		$mode = \Aimeos\MShop\Locale\Manager\Base::SITE_PATH;
884
		$mode = $this->context()->config()->get( 'mshop/catalog/manager/sitemode', $mode );
885
886
		if( $mode === \Aimeos\MShop\Locale\Manager\Base::SITE_PATH ) {
887
			$sitePath = array_reverse( (array) $this->context()->locale()->getSitePath() );
888
		} else {
889
			$sitePath = [$this->context()->locale()->getSiteId()];
890
		}
891
892
		foreach( $sitePath as $siteId )
893
		{
894
			try {
895
				$node = $this->createTreeManager( $siteId )->getNode( $id, $level, $criteria );
896
			} catch( \Aimeos\MW\Tree\Exception $e ) {
897
				continue;
898
			}
899
900
			$listItems = $listItemMap = $refIdMap = [];
901
			$nodeMap = $this->getNodeMap( $node );
902
903
			if( count( $ref ) > 0 ) {
904
				$listItems = $this->getListItems( array_keys( $nodeMap ), $ref, 'catalog' );
905
			}
906
907
			foreach( $listItems as $listItem )
908
			{
909
				$domain = $listItem->getDomain();
910
				$parentid = $listItem->getParentId();
911
912
				$listItemMap[$parentid][$domain][$listItem->getId()] = $listItem;
913
				$refIdMap[$domain][$listItem->getRefId()][] = $parentid;
914
			}
915
916
			$refItemMap = $this->getRefItems( $refIdMap, $ref );
917
			$nodeid = $node->getId();
918
919
			$listItems = [];
920
			if( array_key_exists( $nodeid, $listItemMap ) ) {
921
				$listItems = $listItemMap[$nodeid];
922
			}
923
924
			$refItems = [];
925
			if( array_key_exists( $nodeid, $refItemMap ) ) {
926
				$refItems = $refItemMap[$nodeid];
927
			}
928
929
			if( $item = $this->applyFilter( $this->createItemBase( [], $listItems, $refItems, [], $node ) ) )
930
			{
931
				$this->createTree( $node, $item, $listItemMap, $refItemMap );
0 ignored issues
show
Bug introduced by
$node of type void is incompatible with the type Aimeos\MW\Tree\Node\Iface expected by parameter $node of Aimeos\MShop\Catalog\Manager\Base::createTree(). ( Ignorable by Annotation )

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

931
				$this->createTree( /** @scrutinizer ignore-type */ $node, $item, $listItemMap, $refItemMap );
Loading history...
932
				return $item;
933
			}
934
		}
935
936
		$msg = $this->context()->translate( 'mshop', 'No catalog node for ID "%1$s"' );
937
		throw new \Aimeos\MShop\Catalog\Exception( sprintf( $msg, $id ), 404 );
938
	}
939
940
941
	/**
942
	 * Creates a new extension manager in the domain.
943
	 *
944
	 * @param string $manager Name of the sub manager type
945
	 * @param string|null $name Name of the implementation, will be from configuration (or Default)
946
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager extending the domain functionality
947
	 */
948
	public function getSubManager( string $manager, string $name = null ) : \Aimeos\MShop\Common\Manager\Iface
949
	{
950
		return $this->getSubManagerBase( 'catalog', $manager, $name );
951
	}
952
953
954
	/**
955
	 * Saves the children of the given node
956
	 *
957
	 * @param \Aimeos\MShop\Catalog\Item\Iface $item Catalog item object incl. child items
958
	 * @return \Aimeos\MShop\Catalog\Item\Iface Catalog item with saved child items
959
	 */
960
	protected function saveChildren( \Aimeos\MShop\Catalog\Item\Iface $item ) : \Aimeos\MShop\Catalog\Item\Iface
961
	{
962
		$rmIds = [];
963
		foreach( $item->getChildrenDeleted() as $child ) {
964
			$rmIds[] = $child->getId();
965
		}
966
967
		$this->delete( $rmIds );
968
969
		foreach( $item->getChildren() as $child )
970
		{
971
			if( $child->getId() !== null )
972
			{
973
				$this->save( $child );
974
975
				if( $child->getParentId() !== $item->getId() ) {
976
					$this->move( $child->getId(), $item->getParentId(), $child->getParentId() );
977
				}
978
			}
979
			else
980
			{
981
				$this->insert( $child, $item->getId() );
982
			}
983
		}
984
985
		return $item;
986
	}
987
988
989
	/**
990
	 * Locks the catalog table against modifications from other connections
991
	 *
992
	 * @return \Aimeos\MShop\Catalog\Manager\Iface Manager object for chaining method calls
993
	 */
994
	protected function lock() : \Aimeos\MShop\Catalog\Manager\Iface
995
	{
996
		/** mshop/catalog/manager/lock/mysql
997
		 * SQL statement for locking the catalog table
998
		 *
999
		 * @see mshop/catalog/manager/lock/ansi
1000
		 */
1001
1002
		/** mshop/catalog/manager/lock/ansi
1003
		 * SQL statement for locking the catalog table
1004
		 *
1005
		 * Updating the nested set of categories in the catalog table requires locking
1006
		 * the whole table to avoid data corruption. This statement will be followed by
1007
		 * insert or update statements and closed by an unlock statement.
1008
		 *
1009
		 * @param string Lock SQL statement
1010
		 * @since 2019.04
1011
		 * @category Developer
1012
		 */
1013
		$path = 'mshop/catalog/manager/lock';
1014
1015
		if( ( $sql = $this->getSqlConfig( $path ) ) !== $path )
1016
		{
1017
			$conn = $this->context()->db( $this->getResourceName() );
1018
			$conn->create( $sql )->execute()->finish();
0 ignored issues
show
Bug introduced by
It seems like $sql can also be of type array; however, parameter $sql of Aimeos\Base\DB\Connection\Iface::create() does only seem to accept string, 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

1018
			$conn->create( /** @scrutinizer ignore-type */ $sql )->execute()->finish();
Loading history...
1019
		}
1020
1021
		return $this;
1022
	}
1023
1024
1025
	/**
1026
	 * Unlocks the catalog table for modifications from other connections
1027
	 *
1028
	 * @return \Aimeos\MShop\Catalog\Manager\Iface Manager object for chaining method calls
1029
	 */
1030
	protected function unlock() : \Aimeos\MShop\Catalog\Manager\Iface
1031
	{
1032
		/** mshop/catalog/manager/unlock/mysql
1033
		 * SQL statement for unlocking the catalog table
1034
		 *
1035
		 * @see mshop/catalog/manager/unlock/ansi
1036
		 */
1037
1038
		/** mshop/catalog/manager/unlock/ansi
1039
		 * SQL statement for unlocking the catalog table
1040
		 *
1041
		 * Updating the nested set of categories in the catalog table requires locking
1042
		 * the whole table to avoid data corruption. This statement will be executed
1043
		 * after the table is locked and insert or update statements have been sent to
1044
		 * the database.
1045
		 *
1046
		 * @param string Lock SQL statement
1047
		 * @since 2019.04
1048
		 * @category Developer
1049
		 */
1050
		 $path = 'mshop/catalog/manager/unlock';
1051
1052
		if( ( $sql = $this->getSqlConfig( $path ) ) !== $path )
1053
		{
1054
			$conn = $this->context()->db( $this->getResourceName() );
1055
			$conn->create( $sql )->execute()->finish();
0 ignored issues
show
Bug introduced by
It seems like $sql can also be of type array; however, parameter $sql of Aimeos\Base\DB\Connection\Iface::create() does only seem to accept string, 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

1055
			$conn->create( /** @scrutinizer ignore-type */ $sql )->execute()->finish();
Loading history...
1056
		}
1057
1058
		return $this;
1059
	}
1060
1061
1062
	/**
1063
	 * Updates the usage information of a node.
1064
	 *
1065
	 * @param string $id Id of the record
1066
	 * @param \Aimeos\MShop\Catalog\Item\Iface $item Catalog item
1067
	 * @param bool $case True if the record should be added or false for an update
1068
	 * @return \Aimeos\MShop\Catalog\Manager\Iface Manager object for chaining method calls
1069
	 */
1070
	private function updateUsage( string $id, \Aimeos\MShop\Catalog\Item\Iface $item,
1071
		bool $case = false ) : \Aimeos\MShop\Catalog\Manager\Iface
1072
	{
1073
		$date = date( 'Y-m-d H:i:s' );
1074
		$context = $this->context();
1075
		$conn = $context->db( $this->getResourceName() );
1076
1077
		$siteid = $context->locale()->getSiteId();
1078
		$columns = $this->object()->getSaveAttributes();
1079
1080
		if( $case !== true )
1081
		{
1082
			/** mshop/catalog/manager/update-usage/mysql
1083
			 * Updates the config, editor and mtime value of an updated record
1084
			 *
1085
			 * @see mshop/catalog/manager/update-usage/ansi
1086
			 */
1087
1088
			/** mshop/catalog/manager/update-usage/ansi
1089
			 * Updates the config, editor and mtime value of an updated record
1090
			 *
1091
			 * Each record contains some usage information like when it was
1092
			 * created, last modified and by whom. These information are part
1093
			 * of the catalog items and the generic tree manager doesn't care
1094
			 * about this information. Thus, they are updated after the tree
1095
			 * manager saved the basic record information.
1096
			 *
1097
			 * The SQL statement must be a string suitable for being used as
1098
			 * prepared statement. It must include question marks for binding
1099
			 * the values from the catalog item to the statement before they are
1100
			 * sent to the database server. The order of the columns must
1101
			 * correspond to the order in the method using this statement,
1102
			 * so the correct values are bound to the columns.
1103
			 *
1104
			 * The SQL statement should conform to the ANSI standard to be
1105
			 * compatible with most relational database systems. This also
1106
			 * includes using double quotes for table and column names.
1107
			 *
1108
			 * @param string SQL statement for updating records
1109
			 * @since 2014.03
1110
			 * @category Developer
1111
			 * @see mshop/catalog/manager/delete/ansi
1112
			 * @see mshop/catalog/manager/get/ansi
1113
			 * @see mshop/catalog/manager/insert/ansi
1114
			 * @see mshop/catalog/manager/newid/ansi
1115
			 * @see mshop/catalog/manager/search/ansi
1116
			 * @see mshop/catalog/manager/search-item/ansi
1117
			 * @see mshop/catalog/manager/count/ansi
1118
			 * @see mshop/catalog/manager/move-left/ansi
1119
			 * @see mshop/catalog/manager/move-right/ansi
1120
			 * @see mshop/catalog/manager/update-parentid/ansi
1121
			 * @see mshop/catalog/manager/insert-usage/ansi
1122
			 */
1123
			$path = 'mshop/catalog/manager/update-usage';
1124
		}
1125
		else
1126
		{
1127
			/** mshop/catalog/manager/insert-usage/mysql
1128
			 * Updates the config, editor, ctime and mtime value of an inserted record
1129
			 *
1130
			 * @see mshop/catalog/manager/insert-usage/ansi
1131
			 */
1132
1133
			/** mshop/catalog/manager/insert-usage/ansi
1134
			 * Updates the config, editor, ctime and mtime value of an inserted record
1135
			 *
1136
			 * Each record contains some usage information like when it was
1137
			 * created, last modified and by whom. These information are part
1138
			 * of the catalog items and the generic tree manager doesn't care
1139
			 * about this information. Thus, they are updated after the tree
1140
			 * manager inserted the basic record information.
1141
			 *
1142
			 * The SQL statement must be a string suitable for being used as
1143
			 * prepared statement. It must include question marks for binding
1144
			 * the values from the catalog item to the statement before they are
1145
			 * sent to the database server. The order of the columns must
1146
			 * correspond to the order in the method using this statement,
1147
			 * so the correct values are bound to the columns.
1148
			 *
1149
			 * The SQL statement should conform to the ANSI standard to be
1150
			 * compatible with most relational database systems. This also
1151
			 * includes using double quotes for table and column names.
1152
			 *
1153
			 * @param string SQL statement for updating records
1154
			 * @since 2014.03
1155
			 * @category Developer
1156
			 * @see mshop/catalog/manager/delete/ansi
1157
			 * @see mshop/catalog/manager/get/ansi
1158
			 * @see mshop/catalog/manager/insert/ansi
1159
			 * @see mshop/catalog/manager/newid/ansi
1160
			 * @see mshop/catalog/manager/search/ansi
1161
			 * @see mshop/catalog/manager/search-item/ansi
1162
			 * @see mshop/catalog/manager/count/ansi
1163
			 * @see mshop/catalog/manager/move-left/ansi
1164
			 * @see mshop/catalog/manager/move-right/ansi
1165
			 * @see mshop/catalog/manager/update-parentid/ansi
1166
			 * @see mshop/catalog/manager/update-usage/ansi
1167
			 */
1168
			$path = 'mshop/catalog/manager/insert-usage';
1169
		}
1170
1171
		$sql = $this->addSqlColumns( array_keys( $columns ), $this->getSqlConfig( $path ), false );
0 ignored issues
show
Bug introduced by
It seems like $this->getSqlConfig($path) can also be of type array; however, parameter $sql of Aimeos\MShop\Common\Manager\Base::addSqlColumns() does only seem to accept string, 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

1171
		$sql = $this->addSqlColumns( array_keys( $columns ), /** @scrutinizer ignore-type */ $this->getSqlConfig( $path ), false );
Loading history...
1172
		$stmt = $this->getCachedStatement( $conn, $path, $sql );
1173
		$idx = 1;
1174
1175
		foreach( $columns as $name => $entry ) {
1176
			$stmt->bind( $idx++, $item->get( $name ), \Aimeos\Base\Criteria\SQL::type( $entry->getType() ) );
1177
		}
1178
1179
		$stmt->bind( $idx++, $item->getUrl() );
1180
		$stmt->bind( $idx++, json_encode( $item->getConfig(), JSON_FORCE_OBJECT ) );
1181
		$stmt->bind( $idx++, $date ); // mtime
1182
		$stmt->bind( $idx++, $context->editor() );
1183
		$stmt->bind( $idx++, $item->getTarget() );
1184
1185
		if( $case !== true )
1186
		{
1187
			$stmt->bind( $idx++, $siteid );
1188
			$stmt->bind( $idx++, $id, \Aimeos\Base\DB\Statement\Base::PARAM_INT );
1189
		}
1190
		else
1191
		{
1192
			$stmt->bind( $idx++, $date ); // ctime
1193
			$stmt->bind( $idx++, $siteid );
1194
			$stmt->bind( $idx++, $id, \Aimeos\Base\DB\Statement\Base::PARAM_INT );
1195
		}
1196
1197
		$stmt->execute()->finish();
1198
1199
		return $this;
1200
	}
1201
}
1202