Completed
Push — master ( 188d6f...f59b99 )
by Aimeos
11:37
created

Standard::searchItems()   C

Complexity

Conditions 7
Paths 60

Size

Total Lines 167
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 26
nc 60
nop 3
dl 0
loc 167
rs 6.4589
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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