Completed
Push — master ( 7d4729...4f5586 )
by Aimeos
07:42
created

Standard   C

Complexity

Total Complexity 46

Size/Duplication

Total Lines 880
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 15

Importance

Changes 0
Metric Value
wmc 46
lcom 2
cbo 15
dl 0
loc 880
rs 6.9473
c 0
b 0
f 0

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A cleanup() 0 68 3
A createItem() 0 6 1
A createSearch() 0 8 2
A deleteItem() 0 16 2
A deleteItems() 0 6 2
A findItem() 0 4 1
A getItem() 0 4 1
A getResourceType() 0 6 1
A getSearchAttributes() 0 23 1
A insertItem() 0 20 2
A moveItem() 0 19 2
B saveItem() 0 25 3
C searchItems() 0 167 7
B getPath() 0 26 5
C getTree() 0 49 7
A getSubManager() 0 4 1
B updateUsage() 0 132 4

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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Metaways Infosystems GmbH, 2011
6
 * @copyright Aimeos (aimeos.org), 2015-2017
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
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces between "Base" and comma; 1 found
Loading history...
22
	implements \Aimeos\MShop\Catalog\Manager\Iface, \Aimeos\MShop\Common\Manager\Factory\Iface
23
{
24
	private $searchConfig = array(
25
		'id' => array(
26
			'code' => 'catalog.id',
27
			'internalcode' => 'mcat."id"',
28
			'label' => 'ID',
29
			'type' => 'integer',
30
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
31
			'public' => false,
32
		),
33
		'catalog.siteid' => array(
34
			'code' => 'catalog.siteid',
35
			'internalcode' => 'mcat."siteid"',
36
			'label' => 'Site ID',
37
			'type' => 'integer',
38
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
39
			'public' => false,
40
		),
41
		'parentid' => array(
42
			'code' => 'catalog.parentid',
43
			'internalcode' => 'mcat."parentid"',
44
			'label' => 'Parent ID',
45
			'type' => 'integer',
46
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
47
			'public' => false,
48
		),
49
		'level' => array(
50
			'code' => 'catalog.level',
51
			'internalcode' => 'mcat."level"',
52
			'label' => 'Tree level',
53
			'type' => 'integer',
54
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
55
			'public' => false,
56
		),
57
		'left' => array(
58
			'code' => 'catalog.left',
59
			'internalcode' => 'mcat."nleft"',
60
			'label' => 'Left value',
61
			'type' => 'integer',
62
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
63
			'public' => false,
64
		),
65
		'right' => array(
66
			'code' => 'catalog.right',
67
			'internalcode' => 'mcat."nright"',
68
			'label' => 'Right value',
69
			'type' => 'integer',
70
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
71
			'public' => false,
72
		),
73
		'label' => array(
74
			'code' => 'catalog.label',
75
			'internalcode' => 'mcat."label"',
76
			'label' => 'Label',
77
			'type' => 'string',
78
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
79
		),
80
		'code' => array(
81
			'code' => 'catalog.code',
82
			'internalcode' => 'mcat."code"',
83
			'label' => 'Code',
84
			'type' => 'string',
85
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
86
		),
87
		'catalog.target' => array(
88
			'code' => 'catalog.target',
89
			'internalcode' => 'mcat."target"',
90
			'label' => 'URL target',
91
			'type' => 'string',
92
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
93
		),
94
		'status' => array(
95
			'code' => 'catalog.status',
96
			'internalcode' => 'mcat."status"',
97
			'label' => 'Status',
98
			'type' => 'integer',
99
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
100
		),
101
		'config' => array(
102
			'code' => 'catalog.config',
103
			'internalcode' => 'mcat."config"',
104
			'label' => 'Config',
105
			'type' => 'string',
106
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
107
			'public' => false,
108
		),
109
		'catalog.ctime' => array(
110
			'label' => 'Create date/time',
111
			'code' => 'catalog.ctime',
112
			'internalcode' => 'mcat."ctime"',
113
			'type' => 'datetime',
114
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
115
			'public' => false,
116
		),
117
		'catalog.mtime' => array(
118
			'label' => 'Modify date/time',
119
			'code' => 'catalog.mtime',
120
			'internalcode' => 'mcat."mtime"',
121
			'type' => 'datetime',
122
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
123
			'public' => false,
124
		),
125
		'catalog.editor' => array(
126
			'code' => 'catalog.editor',
127
			'internalcode' => 'mcat."editor"',
128
			'label' => 'Editor',
129
			'type' => 'string',
130
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
131
			'public' => false,
132
		),
133
		'catalog.contains' => array(
134
			'code' => 'catalog.contains()',
135
			'internalcode' => '( SELECT COUNT(mcatli_cs."parentid")
136
				FROM "mshop_catalog_list" AS mcatli_cs
137
				WHERE mcat."id" = mcatli_cs."parentid" AND :site
138
					AND mcatli_cs."domain" = $1 AND mcatli_cs."refid" IN ( $3 ) AND mcatli_cs."typeid" = $2
139
					AND ( mcatli_cs."start" IS NULL OR mcatli_cs."start" <= \':date\' )
140
					AND ( mcatli_cs."end" IS NULL OR mcatli_cs."end" >= \':date\' ) )',
141
			'label' => 'Number of catalog list items, parameter(<domain>,<list type ID>,<reference IDs>)',
142
			'type' => 'integer',
143
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
144
			'public' => false,
145
		),
146
	);
147
148
149
	/**
150
	 * Initializes the object.
151
	 *
152
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context object
153
	 */
154
	public function __construct( \Aimeos\MShop\Context\Item\Iface $context )
155
	{
156
		parent::__construct( $context, $this->searchConfig );
157
		$this->setResourceName( 'db-catalog' );
158
159
		$date = date( 'Y-m-d H:i:00' );
160
		$sites = $context->getLocale()->getSitePath();
161
162
		$this->replaceSiteMarker( $this->searchConfig['catalog.contains'], 'mcatli_cs."siteid"', $sites, ':site' );
163
		$this->searchConfig['catalog.contains'] = str_replace( ':date', $date, $this->searchConfig['catalog.contains'] );
164
	}
165
166
167
	/**
168
	 * Removes old entries from the storage.
169
	 *
170
	 * @param integer[] $siteids List of IDs for sites whose entries should be deleted
171
	 */
172
	public function cleanup( array $siteids )
173
	{
174
		$context = $this->getContext();
175
		$config = $context->getConfig();
176
		$search = $this->getObject()->createSearch();
177
178
		$path = 'mshop/catalog/manager/submanagers';
179
		foreach( $config->get( $path, array( 'lists' ) ) as $domain ) {
180
			$this->getObject()->getSubManager( $domain )->cleanup( $siteids );
181
		}
182
183
		$dbm = $context->getDatabaseManager();
184
		$dbname = $this->getResourceName();
185
		$conn = $dbm->acquire( $dbname );
186
187
		try
188
		{
189
			/** mshop/catalog/manager/standard/cleanup/mysql
190
			 * Deletes the categories for the given site from the database
191
			 *
192
			 * @see mshop/catalog/manager/standard/cleanup/ansi
193
			 */
194
195
			/** mshop/catalog/manager/standard/cleanup/ansi
196
			 * Deletes the categories for the given site from the database
197
			 *
198
			 * Removes the records matched by the given site ID from the catalog
199
			 * database.
200
			 *
201
			 * The ":siteid" placeholder is replaced by the name and value of the
202
			 * site ID column and the given ID or list of IDs.
203
			 *
204
			 * The SQL statement should conform to the ANSI standard to be
205
			 * compatible with most relational database systems. This also
206
			 * includes using double quotes for table and column names.
207
			 *
208
			 * @param string SQL statement for removing the records
209
			 * @since 2014.03
210
			 * @category Developer
211
			 * @see mshop/catalog/manager/standard/delete/ansi
212
			 * @see mshop/catalog/manager/standard/insert/ansi
213
			 * @see mshop/catalog/manager/standard/update/ansi
214
			 * @see mshop/catalog/manager/standard/newid/ansi
215
			 * @see mshop/catalog/manager/standard/search/ansi
216
			 * @see mshop/catalog/manager/standard/count/ansi
217
			 */
218
			$path = 'mshop/catalog/manager/standard/cleanup';
219
			$sql = $this->getSqlConfig( $path );
220
221
			$types = array( 'siteid' => \Aimeos\MW\DB\Statement\Base::PARAM_STR );
222
			$translations = array( 'siteid' => '"siteid"' );
223
224
			$search->setConditions( $search->compare( '==', 'siteid', $siteids ) );
225
			$sql = str_replace( ':siteid', $search->getConditionString( $types, $translations ), $sql );
226
227
			$stmt = $conn->create( $sql );
228
			$stmt->bind( 1, 0, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
229
			$stmt->bind( 2, 0x7FFFFFFF, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
230
			$stmt->execute()->finish();
231
232
			$dbm->release( $conn, $dbname );
233
		}
234
		catch( \Exception $e )
235
		{
236
			$dbm->release( $conn, $dbname );
237
			throw $e;
238
		}
239
	}
240
241
242
	/**
243
	 * Creates new item object.
244
	 *
245
	 * @return \Aimeos\MShop\Common\Item\Iface New item object
246
	 */
247
	public function createItem()
248
	{
249
		$values = array( 'siteid' => $this->getContext()->getLocale()->getSiteId() );
250
251
		return $this->createItemBase( $values );
252
	}
253
254
255
	/**
256
	 * Creates a search object.
257
	 *
258
	 * @param boolean $default Add default criteria
259
	 * @return \Aimeos\MW\Criteria\Iface Returns the Search object
260
	 */
261
	public function createSearch( $default = false )
262
	{
263
		if( $default === true ) {
264
			return $this->createSearchBase( 'catalog' );
265
		}
266
267
		return parent::createSearch();
268
	}
269
270
271
	/**
272
	 * Deletes the item specified by its ID.
273
	 *
274
	 * @param mixed $id ID of the item object
275
	 */
276
	public function deleteItem( $id )
277
	{
278
		$siteid = $this->getContext()->getLocale()->getSiteId();
279
		$this->begin();
280
281
		try
282
		{
283
			$this->createTreeManager( $siteid )->deleteNode( $id );
284
			$this->commit();
285
		}
286
		catch( \Exception $e )
287
		{
288
			$this->rollback();
289
			throw $e;
290
		}
291
	}
292
293
294
	/**
295
	 * Removes multiple items specified by ids in the array.
296
	 *
297
	 * @param array $ids List of IDs
298
	 */
299
	public function deleteItems( array $ids )
300
	{
301
		foreach( $ids as $id ) {
302
			$this->getObject()->deleteItem( $id );
303
		}
304
	}
305
306
307
	/**
308
	 * Returns the item specified by its code and domain/type if necessary
309
	 *
310
	 * @param string $code Code of the item
311
	 * @param string[] $ref List of domains to fetch list items and referenced items for
312
	 * @param string|null $domain Domain of the item if necessary to identify the item uniquely
313
	 * @param string|null $type Type code of the item if necessary to identify the item uniquely
314
	 * @return \Aimeos\MShop\Common\Item\Iface Item object
315
	 */
316
	public function findItem( $code, array $ref = [], $domain = null, $type = null )
317
	{
318
		return $this->findItemBase( array( 'catalog.code' => $code ), $ref );
319
	}
320
321
322
	/**
323
	 * Returns the item specified by its ID.
324
	 *
325
	 * @param integer $id Unique ID of the catalog item
326
	 * @param string[] $ref List of domains to fetch list items and referenced items for
327
	 * @param boolean $default Add default criteria
328
	 * @return \Aimeos\MShop\Catalog\Item\Iface Returns the catalog item of the given id
329
	 * @throws \Aimeos\MShop\Exception If item couldn't be found
330
	 */
331
	public function getItem( $id, array $ref = [], $default = false )
332
	{
333
		return $this->getItemBase( 'catalog.id', $id, $ref, $default );
334
	}
335
336
337
	/**
338
	 * Returns the available manager types
339
	 *
340
	 * @param boolean $withsub Return also the resource type of sub-managers if true
341
	 * @return array Type of the manager and submanagers, subtypes are separated by slashes
342
	 */
343
	public function getResourceType( $withsub = true )
344
	{
345
		$path = 'mshop/catalog/manager/submanagers';
346
347
		return $this->getResourceTypeBase( 'catalog', $path, array( 'lists' ), $withsub );
348
	}
349
350
351
	/**
352
	 * Returns the attributes that can be used for searching.
353
	 *
354
	 * @param boolean $withsub Return also attributes of sub-managers if true
355
	 * @return array List of attribute items implementing \Aimeos\MW\Criteria\Attribute\Iface
356
	 */
357
	public function getSearchAttributes( $withsub = true )
358
	{
359
		/** mshop/catalog/manager/submanagers
360
		 * List of manager names that can be instantiated by the catalog manager
361
		 *
362
		 * Managers provide a generic interface to the underlying storage.
363
		 * Each manager has or can have sub-managers caring about particular
364
		 * aspects. Each of these sub-managers can be instantiated by its
365
		 * parent manager using the getSubManager() method.
366
		 *
367
		 * The search keys from sub-managers can be normally used in the
368
		 * manager as well. It allows you to search for items of the manager
369
		 * using the search keys of the sub-managers to further limit the
370
		 * retrieved list of items.
371
		 *
372
		 * @param array List of sub-manager names
373
		 * @since 2014.03
374
		 * @category Developer
375
		 */
376
		$path = 'mshop/catalog/manager/submanagers';
377
378
		return $this->getSearchAttributesBase( $this->searchConfig, $path, array( 'lists' ), $withsub );
379
	}
380
381
382
	/**
383
	 * Adds a new item object.
384
	 *
385
	 * @param \Aimeos\MShop\Catalog\Item\Iface $item Item which should be inserted
386
	 * @param string|null $parentId ID of the parent item where the item should be inserted into
387
	 * @param string|null $refId ID of the item where the item should be inserted before (null to append)
388
	 * @return \Aimeos\MShop\Catalog\Item\Iface $item Updated item including the generated ID
389
	 */
390
	public function insertItem( \Aimeos\MShop\Catalog\Item\Iface $item, $parentId = null, $refId = null )
391
	{
392
		$siteid = $this->getContext()->getLocale()->getSiteId();
393
		$node = $item->getNode();
394
		$this->begin();
395
396
		try
397
		{
398
			$this->createTreeManager( $siteid )->insertNode( $node, $parentId, $refId );
399
			$this->updateUsage( $node->getId(), $item, true );
400
			$this->commit();
401
		}
402
		catch( \Exception $e )
403
		{
404
			$this->rollback();
405
			throw $e;
406
		}
407
408
		return $item;
409
	}
410
411
412
	/**
413
	 * Moves an existing item to the new parent in the storage.
414
	 *
415
	 * @param string $id ID of the item that should be moved
416
	 * @param string $oldParentId ID of the old parent item which currently contains the item that should be removed
417
	 * @param string $newParentId ID of the new parent item where the item should be moved to
418
	 * @param string|null $refId ID of the item where the item should be inserted before (null to append)
419
	 */
420
	public function moveItem( $id, $oldParentId, $newParentId, $refId = null )
421
	{
422
		$siteid = $this->getContext()->getLocale()->getSiteId();
423
		$item = $this->getObject()->getItem( $id );
424
425
		$this->begin();
426
427
		try
428
		{
429
			$this->createTreeManager( $siteid )->moveNode( $id, $oldParentId, $newParentId, $refId );
430
			$this->updateUsage( $id, $item );
431
			$this->commit();
432
		}
433
		catch( \Exception $e )
434
		{
435
			$this->rollback();
436
			throw $e;
437
		}
438
	}
439
440
441
	/**
442
	 * Updates an item object.
443
	 *
444
	 * @param \Aimeos\MShop\Common\Item\Iface $item Item object whose data should be saved
445
	 * @param boolean $fetch True if the new ID should be returned in the item
446
	 * @return \Aimeos\MShop\Common\Item\Iface $item Updated item including the generated ID
447
	 */
448
	public function saveItem( \Aimeos\MShop\Common\Item\Iface $item, $fetch = true )
449
	{
450
		$iface = '\\Aimeos\\MShop\\Catalog\\Item\\Iface';
451
		if( !( $item instanceof $iface ) ) {
452
			throw new \Aimeos\MShop\Catalog\Exception( sprintf( 'Object is not of required type "%1$s"', $iface ) );
453
		}
454
455
		$siteid = $this->getContext()->getLocale()->getSiteId();
456
		$node = $item->getNode();
457
		$this->begin();
458
459
		try
460
		{
461
			$this->createTreeManager( $siteid )->saveNode( $node );
462
			$this->updateUsage( $node->getId(), $item );
463
			$this->commit();
464
		}
465
		catch( \Exception $e )
466
		{
467
			$this->rollback();
468
			throw $e;
469
		}
470
471
		return $item;
472
	}
473
474
475
	/**
476
	 * Searches for all items matching the given critera.
477
	 *
478
	 * @param \Aimeos\MW\Criteria\Iface $search Search criteria object
479
	 * @param string[] $ref List of domains to fetch list items and referenced items for
480
	 * @param integer|null &$total Number of items that are available in total
481
	 * @return array List of items implementing \Aimeos\MShop\Common\Item\Iface
482
	 */
483
	public function searchItems( \Aimeos\MW\Criteria\Iface $search, array $ref = [], &$total = null )
484
	{
485
		$nodeMap = $siteMap = [];
486
		$context = $this->getContext();
487
488
		$dbname = $this->getResourceName();
489
		$dbm = $context->getDatabaseManager();
490
		$conn = $dbm->acquire( $dbname );
491
492
		try
493
		{
494
			$required = array( 'catalog' );
495
			$level = \Aimeos\MShop\Locale\Manager\Base::SITE_PATH;
496
497
			/** mshop/catalog/manager/standard/search-item/mysql
498
			 * Retrieves the records matched by the given criteria in the database
499
			 *
500
			 * @see mshop/catalog/manager/standard/search-item/ansi
501
			 */
502
503
			/** mshop/catalog/manager/standard/search-item/ansi
504
			 * Retrieves the records matched by the given criteria in the database
505
			 *
506
			 * Fetches the records matched by the given criteria from the catalog
507
			 * database. The records must be from one of the sites that are
508
			 * configured via the context item. If the current site is part of
509
			 * a tree of sites, the SELECT statement can retrieve all records
510
			 * from the current site and the complete sub-tree of sites.
511
			 *
512
			 * As the records can normally be limited by criteria from sub-managers,
513
			 * their tables must be joined in the SQL context. This is done by
514
			 * using the "internaldeps" property from the definition of the ID
515
			 * column of the sub-managers. These internal dependencies specify
516
			 * the JOIN between the tables and the used columns for joining. The
517
			 * ":joins" placeholder is then replaced by the JOIN strings from
518
			 * the sub-managers.
519
			 *
520
			 * To limit the records matched, conditions can be added to the given
521
			 * criteria object. It can contain comparisons like column names that
522
			 * must match specific values which can be combined by AND, OR or NOT
523
			 * operators. The resulting string of SQL conditions replaces the
524
			 * ":cond" placeholder before the statement is sent to the database
525
			 * server.
526
			 *
527
			 * If the records that are retrieved should be ordered by one or more
528
			 * columns, the generated string of column / sort direction pairs
529
			 * replaces the ":order" placeholder. In case no ordering is required,
530
			 * the complete ORDER BY part including the "\/*-orderby*\/...\/*orderby-*\/"
531
			 * markers is removed to speed up retrieving the records. Columns of
532
			 * sub-managers can also be used for ordering the result set but then
533
			 * no index can be used.
534
			 *
535
			 * The number of returned records can be limited and can start at any
536
			 * number between the begining and the end of the result set. For that
537
			 * the ":size" and ":start" placeholders are replaced by the
538
			 * corresponding values from the criteria object. The default values
539
			 * are 0 for the start and 100 for the size value.
540
			 *
541
			 * The SQL statement should conform to the ANSI standard to be
542
			 * compatible with most relational database systems. This also
543
			 * includes using double quotes for table and column names.
544
			 *
545
			 * @param string SQL statement for searching items
546
			 * @since 2014.03
547
			 * @category Developer
548
			 * @see mshop/catalog/manager/standard/delete/ansi
549
			 * @see mshop/catalog/manager/standard/get/ansi
550
			 * @see mshop/catalog/manager/standard/insert/ansi
551
			 * @see mshop/catalog/manager/standard/update/ansi
552
			 * @see mshop/catalog/manager/standard/newid/ansi
553
			 * @see mshop/catalog/manager/standard/search/ansi
554
			 * @see mshop/catalog/manager/standard/count/ansi
555
			 * @see mshop/catalog/manager/standard/move-left/ansi
556
			 * @see mshop/catalog/manager/standard/move-right/ansi
557
			 * @see mshop/catalog/manager/standard/update-parentid/ansi
558
			 */
559
			$cfgPathSearch = 'mshop/catalog/manager/standard/search-item';
560
561
			/** mshop/catalog/manager/standard/count/mysql
562
			 * Counts the number of records matched by the given criteria in the database
563
			 *
564
			 * @see mshop/catalog/manager/standard/count/ansi
565
			 */
566
567
			/** mshop/catalog/manager/standard/count/ansi
568
			 * Counts the number of records matched by the given criteria in the database
569
			 *
570
			 * Counts all records matched by the given criteria from the catalog
571
			 * database. The records must be from one of the sites that are
572
			 * configured via the context item. If the current site is part of
573
			 * a tree of sites, the statement can count all records from the
574
			 * current site and the complete sub-tree of sites.
575
			 *
576
			 * As the records can normally be limited by criteria from sub-managers,
577
			 * their tables must be joined in the SQL context. This is done by
578
			 * using the "internaldeps" property from the definition of the ID
579
			 * column of the sub-managers. These internal dependencies specify
580
			 * the JOIN between the tables and the used columns for joining. The
581
			 * ":joins" placeholder is then replaced by the JOIN strings from
582
			 * the sub-managers.
583
			 *
584
			 * To limit the records matched, conditions can be added to the given
585
			 * criteria object. It can contain comparisons like column names that
586
			 * must match specific values which can be combined by AND, OR or NOT
587
			 * operators. The resulting string of SQL conditions replaces the
588
			 * ":cond" placeholder before the statement is sent to the database
589
			 * server.
590
			 *
591
			 * Both, the strings for ":joins" and for ":cond" are the same as for
592
			 * the "search" SQL statement.
593
			 *
594
			 * Contrary to the "search" statement, it doesn't return any records
595
			 * but instead the number of records that have been found. As counting
596
			 * thousands of records can be a long running task, the maximum number
597
			 * of counted records is limited for performance reasons.
598
			 *
599
			 * The SQL statement should conform to the ANSI standard to be
600
			 * compatible with most relational database systems. This also
601
			 * includes using double quotes for table and column names.
602
			 *
603
			 * @param string SQL statement for counting items
604
			 * @since 2014.03
605
			 * @category Developer
606
			 * @see mshop/catalog/manager/standard/delete/ansi
607
			 * @see mshop/catalog/manager/standard/get/ansi
608
			 * @see mshop/catalog/manager/standard/insert/ansi
609
			 * @see mshop/catalog/manager/standard/update/ansi
610
			 * @see mshop/catalog/manager/standard/newid/ansi
611
			 * @see mshop/catalog/manager/standard/search/ansi
612
			 * @see mshop/catalog/manager/standard/search-item/ansi
613
			 * @see mshop/catalog/manager/standard/move-left/ansi
614
			 * @see mshop/catalog/manager/standard/move-right/ansi
615
			 * @see mshop/catalog/manager/standard/update-parentid/ansi
616
			 */
617
			$cfgPathCount = 'mshop/catalog/manager/standard/count';
618
619
			if( $search->getSortations() === [] ) {
620
				$search->setSortations( [$search->sort( '+', 'catalog.left')] );
621
			}
622
623
			$results = $this->searchItemsBase( $conn, $search, $cfgPathSearch, $cfgPathCount, $required, $total, $level );
624
625
			while( ( $row = $results->fetch() ) !== false ) {
626
				$siteMap[$row['siteid']][$row['id']] = new \Aimeos\MW\Tree\Node\Standard( $row );
627
			}
628
629
			$sitePath = array_reverse( $this->getContext()->getLocale()->getSitePath() );
630
631
			foreach( $sitePath as $siteId )
632
			{
633
				if( isset( $siteMap[$siteId] ) && !empty( $siteMap[$siteId] ) )
634
				{
635
					$nodeMap = $siteMap[$siteId];
636
					break;
637
				}
638
			}
639
640
			$dbm->release( $conn, $dbname );
641
		}
642
		catch( \Exception $e )
643
		{
644
			$dbm->release( $conn, $dbname );
645
			throw $e;
646
		}
647
648
		return $this->buildItems( $nodeMap, $ref, 'catalog' );
649
	}
650
651
652
	/**
653
	 * Returns a list of items starting with the given category that are in the path to the root node
654
	 *
655
	 * @param integer $id ID of item to get the path for
656
	 * @param string[] $ref List of domains to fetch list items and referenced items for
657
	 * @return array Associative list of items implementing \Aimeos\MShop\Catalog\Item\Iface with IDs as keys
658
	 */
659
	public function getPath( $id, array $ref = [] )
660
	{
661
		$sitePath = array_reverse( $this->getContext()->getLocale()->getSitePath() );
662
663
		foreach( $sitePath as $siteId )
664
		{
665
			try {
666
				$path = $this->createTreeManager( $siteId )->getPath( $id );
667
			} catch( \Exception $e ) {
668
				continue;
669
			}
670
671
			if( !empty( $path ) )
672
			{
673
				$itemMap = [];
674
675
				foreach( $path as $node ) {
676
					$itemMap[$node->getId()] = $node;
677
				}
678
679
				return $this->buildItems( $itemMap, $ref, 'catalog' );
680
			}
681
		}
682
683
		throw new \Aimeos\MShop\Catalog\Exception( sprintf( 'Catalog path for ID "%1$s" not found', $id ) );
684
	}
685
686
687
	/**
688
	 * Returns a node and its descendants depending on the given resource.
689
	 *
690
	 * @param string|null $id Retrieve nodes starting from the given ID
691
	 * @param string[] List of domains (e.g. text, media, etc.) whose referenced items should be attached to the objects
692
	 * @param integer $level One of the level constants from \Aimeos\MW\Tree\Manager\Base
693
	 * @param \Aimeos\MW\Criteria\Iface|null $criteria Optional criteria object with conditions
694
	 * @return \Aimeos\MShop\Catalog\Item\Iface Catalog item, maybe with subnodes
695
	 */
696
	public function getTree( $id = null, array $ref = [], $level = \Aimeos\MW\Tree\Manager\Base::LEVEL_TREE, \Aimeos\MW\Criteria\Iface $criteria = null )
697
	{
698
		$sitePath = array_reverse( $this->getContext()->getLocale()->getSitePath() );
699
700
		foreach( $sitePath as $siteId )
701
		{
702
			try {
703
				$node = $this->createTreeManager( $siteId )->getNode( $id, $level, $criteria );
704
			} catch( \Exception $e ) {
705
				continue;
706
			}
707
708
			$listItems = $listItemMap = $refIdMap = [];
709
			$nodeMap = $this->getNodeMap( $node );
710
711
			if( count( $ref ) > 0 ) {
712
				$listItems = $this->getListItems( array_keys( $nodeMap ), $ref, 'catalog' );
713
			}
714
715
			foreach( $listItems as $listItem )
716
			{
717
				$domain = $listItem->getDomain();
718
				$parentid = $listItem->getParentId();
719
720
				$listItemMap[$parentid][$domain][$listItem->getId()] = $listItem;
721
				$refIdMap[$domain][$listItem->getRefId()][] = $parentid;
722
			}
723
724
			$refItemMap = $this->getRefItems( $refIdMap );
725
			$nodeid = $node->getId();
726
727
			$listItems = [];
728
			if( array_key_exists( $nodeid, $listItemMap ) ) {
729
				$listItems = $listItemMap[$nodeid];
730
			}
731
732
			$refItems = [];
733
			if( array_key_exists( $nodeid, $refItemMap ) ) {
734
				$refItems = $refItemMap[$nodeid];
735
			}
736
737
			$item = $this->createItemBase( [], $listItems, $refItems, [], $node );
738
			$this->createTree( $node, $item, $listItemMap, $refItemMap );
739
740
			return $item;
741
		}
742
743
		throw new \Aimeos\MShop\Catalog\Exception( sprintf( 'for ID "%1$s" not available', $id ) );
744
	}
745
746
747
	/**
748
	 * Creates a new extension manager in the domain.
749
	 *
750
	 * @param string $manager Name of the sub manager type
751
	 * @param string|null $name Name of the implementation, will be from configuration (or Default)
752
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager extending the domain functionality
753
	 */
754
	public function getSubManager( $manager, $name = null )
755
	{
756
		return $this->getSubManagerBase( 'catalog', $manager, $name );
757
	}
758
759
760
	/**
761
	 * Updates the usage information of a node.
762
	 *
763
	 * @param integer $id Id of the record
764
	 * @param \Aimeos\MShop\Catalog\Item\Iface $item Catalog item
765
	 * @param boolean $case True if the record shoud be added or false for an update
766
	 *
767
	 */
768
	private function updateUsage( $id, \Aimeos\MShop\Catalog\Item\Iface $item, $case = false )
769
	{
770
		$date = date( 'Y-m-d H:i:s' );
771
		$context = $this->getContext();
772
773
		$dbm = $context->getDatabaseManager();
774
		$dbname = $this->getResourceName();
775
		$conn = $dbm->acquire( $dbname );
776
777
		try
778
		{
779
			$siteid = $context->getLocale()->getSiteId();
780
781
			if( $case !== true )
782
			{
783
				/** mshop/catalog/manager/standard/update-usage/mysql
784
				 * Updates the config, editor and mtime value of an updated record
785
				 *
786
				 * @see mshop/catalog/manager/standard/update-usage/ansi
787
				 */
788
789
				/** mshop/catalog/manager/standard/update-usage/ansi
790
				 * Updates the config, editor and mtime value of an updated record
791
				 *
792
				 * Each record contains some usage information like when it was
793
				 * created, last modified and by whom. These information are part
794
				 * of the catalog items and the generic tree manager doesn't care
795
				 * about this information. Thus, they are updated after the tree
796
				 * manager saved the basic record information.
797
				 *
798
				 * The SQL statement must be a string suitable for being used as
799
				 * prepared statement. It must include question marks for binding
800
				 * the values from the catalog item to the statement before they are
801
				 * sent to the database server. The order of the columns must
802
				 * correspond to the order in the method using this statement,
803
				 * so the correct values are bound to the columns.
804
				 *
805
				 * The SQL statement should conform to the ANSI standard to be
806
				 * compatible with most relational database systems. This also
807
				 * includes using double quotes for table and column names.
808
				 *
809
				 * @param string SQL statement for updating records
810
				 * @since 2014.03
811
				 * @category Developer
812
				 * @see mshop/catalog/manager/standard/delete/ansi
813
				 * @see mshop/catalog/manager/standard/get/ansi
814
				 * @see mshop/catalog/manager/standard/insert/ansi
815
				 * @see mshop/catalog/manager/standard/newid/ansi
816
				 * @see mshop/catalog/manager/standard/search/ansi
817
				 * @see mshop/catalog/manager/standard/search-item/ansi
818
				 * @see mshop/catalog/manager/standard/count/ansi
819
				 * @see mshop/catalog/manager/standard/move-left/ansi
820
				 * @see mshop/catalog/manager/standard/move-right/ansi
821
				 * @see mshop/catalog/manager/standard/update-parentid/ansi
822
				 * @see mshop/catalog/manager/standard/insert-usage/ansi
823
				 */
824
				$path = 'mshop/catalog/manager/standard/update-usage';
825
			}
826
			else
827
			{
828
				/** mshop/catalog/manager/standard/insert-usage/mysql
829
				 * Updates the config, editor, ctime and mtime value of an inserted record
830
				 *
831
				 * @see mshop/catalog/manager/standard/insert-usage/ansi
832
				 */
833
834
				/** mshop/catalog/manager/standard/insert-usage/ansi
835
				 * Updates the config, editor, ctime and mtime value of an inserted record
836
				 *
837
				 * Each record contains some usage information like when it was
838
				 * created, last modified and by whom. These information are part
839
				 * of the catalog items and the generic tree manager doesn't care
840
				 * about this information. Thus, they are updated after the tree
841
				 * manager inserted the basic record information.
842
				 *
843
				 * The SQL statement must be a string suitable for being used as
844
				 * prepared statement. It must include question marks for binding
845
				 * the values from the catalog item to the statement before they are
846
				 * sent to the database server. The order of the columns must
847
				 * correspond to the order in the method using this statement,
848
				 * so the correct values are bound to the columns.
849
				 *
850
				 * The SQL statement should conform to the ANSI standard to be
851
				 * compatible with most relational database systems. This also
852
				 * includes using double quotes for table and column names.
853
				 *
854
				 * @param string SQL statement for updating records
855
				 * @since 2014.03
856
				 * @category Developer
857
				 * @see mshop/catalog/manager/standard/delete/ansi
858
				 * @see mshop/catalog/manager/standard/get/ansi
859
				 * @see mshop/catalog/manager/standard/insert/ansi
860
				 * @see mshop/catalog/manager/standard/newid/ansi
861
				 * @see mshop/catalog/manager/standard/search/ansi
862
				 * @see mshop/catalog/manager/standard/search-item/ansi
863
				 * @see mshop/catalog/manager/standard/count/ansi
864
				 * @see mshop/catalog/manager/standard/move-left/ansi
865
				 * @see mshop/catalog/manager/standard/move-right/ansi
866
				 * @see mshop/catalog/manager/standard/update-parentid/ansi
867
				 * @see mshop/catalog/manager/standard/update-usage/ansi
868
				 */
869
				$path = 'mshop/catalog/manager/standard/insert-usage';
870
			}
871
872
			$stmt = $conn->create( $this->getSqlConfig( $path ) );
873
			$stmt->bind( 1, json_encode( $item->getConfig() ) );
874
			$stmt->bind( 2, $date ); // mtime
875
			$stmt->bind( 3, $context->getEditor() );
876
			$stmt->bind( 4, $item->getTarget() );
877
878
			if( $case !== true )
879
			{
880
				$stmt->bind( 5, $siteid, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
881
				$stmt->bind( 6, $id, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
882
			}
883
			else
884
			{
885
				$stmt->bind( 5, $date ); // ctime
886
				$stmt->bind( 6, $siteid, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
887
				$stmt->bind( 7, $id, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
888
			}
889
890
			$stmt->execute()->finish();
891
892
			$dbm->release( $conn, $dbname );
893
		}
894
		catch( \Exception $e )
895
		{
896
			$dbm->release( $conn, $dbname );
897
			throw $e;
898
		}
899
	}
900
}
901