Completed
Push — master ( d60aa0...b67a38 )
by Aimeos
08:15
created

Standard::searchItems()   D

Complexity

Conditions 9
Paths 35

Size

Total Lines 176
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 37
c 0
b 0
f 0
nc 35
nop 3
dl 0
loc 176
rs 4.8196

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 Product
9
 */
10
11
12
namespace Aimeos\MShop\Product\Manager;
13
14
15
/**
16
 * Default product manager.
17
 *
18
 * @package MShop
19
 * @subpackage Product
20
 */
21
class Standard
22
	extends \Aimeos\MShop\Common\Manager\ListRef\Base
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces between "Base" and comma; 1 found
Loading history...
23
	implements \Aimeos\MShop\Product\Manager\Iface
24
{
25
	private $searchConfig = array(
26
		'product.id'=> array(
27
			'code'=>'product.id',
28
			'internalcode'=>'mpro."id"',
29
			'label'=>'Product ID',
30
			'type'=> 'integer',
31
			'internaltype'=> \Aimeos\MW\DB\Statement\Base::PARAM_INT,
32
		),
33
		'product.siteid'=> array(
34
			'code'=>'product.siteid',
35
			'internalcode'=>'mpro."siteid"',
36
			'label'=>'Product site ID',
37
			'type'=> 'integer',
38
			'internaltype'=> \Aimeos\MW\DB\Statement\Base::PARAM_INT,
39
			'public' => false,
40
		),
41
		'product.typeid'=> array(
42
			'code'=>'product.typeid',
43
			'internalcode'=>'mpro."typeid"',
44
			'label'=>'Product type ID',
45
			'type'=> 'integer',
46
			'internaltype'=> \Aimeos\MW\DB\Statement\Base::PARAM_INT,
47
			'public' => false,
48
		),
49
		'product.code'=> array(
50
			'code'=>'product.code',
51
			'internalcode'=>'mpro."code"',
52
			'label'=>'Product code',
53
			'type'=> 'string',
54
			'internaltype'=> \Aimeos\MW\DB\Statement\Base::PARAM_STR,
55
		),
56
		'product.label'=> array(
57
			'code'=>'product.label',
58
			'internalcode'=>'mpro."label"',
59
			'label'=>'Product label',
60
			'type'=> 'string',
61
			'internaltype'=> \Aimeos\MW\DB\Statement\Base::PARAM_STR,
62
		),
63
		'product.config' => array(
64
			'code' => 'product.config',
65
			'internalcode' => 'mpro."config"',
66
			'label' => 'Product config',
67
			'type' => 'string',
68
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
69
		),
70
		'product.datestart'=> array(
71
			'code'=>'product.datestart',
72
			'internalcode'=>'mpro."start"',
73
			'label'=>'Product start date/time',
74
			'type'=> 'datetime',
75
			'internaltype'=> \Aimeos\MW\DB\Statement\Base::PARAM_STR,
76
		),
77
		'product.dateend'=> array(
78
			'code'=>'product.dateend',
79
			'internalcode'=>'mpro."end"',
80
			'label'=>'Product end date/time',
81
			'type'=> 'datetime',
82
			'internaltype'=> \Aimeos\MW\DB\Statement\Base::PARAM_STR,
83
		),
84
		'product.ctime'=> array(
85
			'code'=>'product.ctime',
86
			'internalcode'=>'mpro."ctime"',
87
			'label'=>'Product create date/time',
88
			'type'=> 'datetime',
89
			'internaltype'=> \Aimeos\MW\DB\Statement\Base::PARAM_STR,
90
		),
91
		'product.mtime'=> array(
92
			'code'=>'product.mtime',
93
			'internalcode'=>'mpro."mtime"',
94
			'label'=>'Product modification date/time',
95
			'type'=> 'datetime',
96
			'internaltype'=> \Aimeos\MW\DB\Statement\Base::PARAM_STR,
97
		),
98
		'product.editor'=> array(
99
			'code'=>'product.editor',
100
			'internalcode'=>'mpro."editor"',
101
			'label'=>'Product editor',
102
			'type'=> 'string',
103
			'internaltype'=> \Aimeos\MW\DB\Statement\Base::PARAM_STR,
104
		),
105
		'product.status'=> array(
106
			'code'=>'product.status',
107
			'internalcode'=>'mpro."status"',
108
			'label'=>'Product status',
109
			'type'=> 'integer',
110
			'internaltype'=> \Aimeos\MW\DB\Statement\Base::PARAM_INT,
111
		),
112
		'product.contains' => array(
113
			'code'=>'product.contains()',
114
			'internalcode'=>'',
115
			'label'=>'Number of product list items, parameter(<domain>,<list type ID>,<reference IDs>)',
116
			'type'=> 'integer',
117
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
118
			'public' => false,
119
		),
120
	);
121
122
123
	/**
124
	 * Creates the product manager that will use the given context object.
125
	 *
126
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context object with required objects
127
	 */
128
	public function __construct( \Aimeos\MShop\Context\Item\Iface $context )
129
	{
130
		parent::__construct( $context );
131
		$this->setResourceName( 'db-product' );
132
133
		$date = date( 'Y-m-d H:i:00' );
134
135
		$this->searchConfig['product.contains']['internalcode'] =
136
			'( SELECT COUNT(mproli2."parentid") FROM "mshop_product_list" AS mproli2
137
				WHERE mpro."id" = mproli2."parentid" AND :site
138
					AND mproli2."domain" = $1 AND mproli2."refid" IN ( $3 ) AND mproli2."typeid" = $2
139
					AND ( mproli2."start" IS NULL OR mproli2."start" <= \'' . $date . '\' )
140
					AND ( mproli2."end" IS NULL OR mproli2."end" >= \'' . $date . '\' ) )';
141
142
		$sites = $context->getLocale()->getSitePath();
143
		$this->replaceSiteMarker( $this->searchConfig['product.contains'], 'mproli2."siteid"', $sites, ':site' );
144
	}
145
146
147
	/**
148
	 * Removes old entries from the storage.
149
	 *
150
	 * @param integer[] $siteids List of IDs for sites whose entries should be deleted
151
	 */
152
	public function cleanup( array $siteids )
153
	{
154
		$path = 'mshop/product/manager/submanagers';
155
		$default = array( 'type', 'property', 'lists' );
156
157
		foreach( $this->getContext()->getConfig()->get( $path, $default ) as $domain ) {
158
			$this->getSubManager( $domain )->cleanup( $siteids );
159
		}
160
161
		$this->cleanupBase( $siteids, 'mshop/product/manager/standard/delete' );
162
	}
163
164
165
	/**
166
	 * Create new product item object.
167
	 *
168
	 * @return \Aimeos\MShop\Product\Item\Iface
169
	 */
170
	public function createItem()
171
	{
172
		$values = array( 'product.siteid' => $this->getContext()->getLocale()->getSiteId() );
173
		return $this->createItemBase( $values );
174
	}
175
176
177
	/**
178
	 * Adds a new product to the storage.
179
	 *
180
	 * @param \Aimeos\MShop\Common\Item\Iface $item Product item that should be saved to the storage
181
	 * @param boolean $fetch True if the new ID should be returned in the item
182
	 */
183
	public function saveItem( \Aimeos\MShop\Common\Item\Iface $item, $fetch = true )
184
	{
185
		$iface = '\\Aimeos\\MShop\\Product\\Item\\Iface';
186
		if( !( $item instanceof $iface ) ) {
187
			throw new \Aimeos\MShop\Product\Exception( sprintf( 'Object is not of required type "%1$s"', $iface ) );
188
		}
189
190
		if( !$item->isModified() ) { return; }
191
192
		$context = $this->getContext();
193
194
		$dbm = $context->getDatabaseManager();
195
		$dbname = $this->getResourceName();
196
		$conn = $dbm->acquire( $dbname );
197
198
		try
199
		{
200
			$id = $item->getId();
201
			$date = date( 'Y-m-d H:i:s' );
202
203
			if( $id === null )
204
			{
205
				/** mshop/product/manager/standard/insert/mysql
206
				 * Inserts a new product record into the database table
207
				 *
208
				 * @see mshop/product/manager/standard/insert/ansi
209
				 */
210
211
				/** mshop/product/manager/standard/insert/ansi
212
				 * Inserts a new product record into the database table
213
				 *
214
				 * Items with no ID yet (i.e. the ID is NULL) will be created in
215
				 * the database and the newly created ID retrieved afterwards
216
				 * using the "newid" SQL statement.
217
				 *
218
				 * The SQL statement must be a string suitable for being used as
219
				 * prepared statement. It must include question marks for binding
220
				 * the values from the product item to the statement before they are
221
				 * sent to the database server. The number of question marks must
222
				 * be the same as the number of columns listed in the INSERT
223
				 * statement. The order of the columns must correspond to the
224
				 * order in the saveItems() method, so the correct values are
225
				 * bound to the columns.
226
				 *
227
				 * The SQL statement should conform to the ANSI standard to be
228
				 * compatible with most relational database systems. This also
229
				 * includes using double quotes for table and column names.
230
				 *
231
				 * @param string SQL statement for inserting records
232
				 * @since 2014.03
233
				 * @category Developer
234
				 * @see mshop/product/manager/standard/update/ansi
235
				 * @see mshop/product/manager/standard/newid/ansi
236
				 * @see mshop/product/manager/standard/delete/ansi
237
				 * @see mshop/product/manager/standard/search/ansi
238
				 * @see mshop/product/manager/standard/count/ansi
239
				 */
240
				$path = 'mshop/product/manager/standard/insert';
241
			}
242
			else
243
			{
244
				/** mshop/product/manager/standard/update/mysql
245
				 * Updates an existing product record in the database
246
				 *
247
				 * @see mshop/product/manager/standard/update/ansi
248
				 */
249
250
				/** mshop/product/manager/standard/update/ansi
251
				 * Updates an existing product record in the database
252
				 *
253
				 * Items which already have an ID (i.e. the ID is not NULL) will
254
				 * be updated in the database.
255
				 *
256
				 * The SQL statement must be a string suitable for being used as
257
				 * prepared statement. It must include question marks for binding
258
				 * the values from the product item to the statement before they are
259
				 * sent to the database server. The order of the columns must
260
				 * correspond to the order in the saveItems() method, so the
261
				 * correct values are bound to the columns.
262
				 *
263
				 * The SQL statement should conform to the ANSI standard to be
264
				 * compatible with most relational database systems. This also
265
				 * includes using double quotes for table and column names.
266
				 *
267
				 * @param string SQL statement for updating records
268
				 * @since 2014.03
269
				 * @category Developer
270
				 * @see mshop/product/manager/standard/insert/ansi
271
				 * @see mshop/product/manager/standard/newid/ansi
272
				 * @see mshop/product/manager/standard/delete/ansi
273
				 * @see mshop/product/manager/standard/search/ansi
274
				 * @see mshop/product/manager/standard/count/ansi
275
				 */
276
				$path = 'mshop/product/manager/standard/update';
277
			}
278
279
			$stmt = $this->getCachedStatement( $conn, $path );
280
			$stmt->bind( 1, $context->getLocale()->getSiteId(), \Aimeos\MW\DB\Statement\Base::PARAM_INT );
281
			$stmt->bind( 2, $item->getTypeId(), \Aimeos\MW\DB\Statement\Base::PARAM_INT );
282
			$stmt->bind( 3, $item->getCode() );
283
			$stmt->bind( 4, $item->getLabel() );
284
			$stmt->bind( 5, $item->getStatus(), \Aimeos\MW\DB\Statement\Base::PARAM_INT );
285
			$stmt->bind( 6, $item->getDateStart() );
286
			$stmt->bind( 7, $item->getDateEnd() );
287
			$stmt->bind( 8, json_encode( $item->getConfig() ) );
288
			$stmt->bind( 9, $date ); // mtime
289
			$stmt->bind( 10, $context->getEditor() );
290
291
			if( $id !== null ) {
292
				$stmt->bind( 11, $id, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
293
				$item->setId( $id ); //so item is no longer modified
294
			} else {
295
				$stmt->bind( 11, $date ); // ctime
296
			}
297
298
			$stmt->execute()->finish();
299
300
			if( $id === null && $fetch === true )
301
			{
302
				/** mshop/product/manager/standard/newid/mysql
303
				 * Retrieves the ID generated by the database when inserting a new record
304
				 *
305
				 * @see mshop/product/manager/standard/newid/ansi
306
				 */
307
308
				/** mshop/product/manager/standard/newid/ansi
309
				 * Retrieves the ID generated by the database when inserting a new record
310
				 *
311
				 * As soon as a new record is inserted into the database table,
312
				 * the database server generates a new and unique identifier for
313
				 * that record. This ID can be used for retrieving, updating and
314
				 * deleting that specific record from the table again.
315
				 *
316
				 * For MySQL:
317
				 *  SELECT LAST_INSERT_ID()
318
				 * For PostgreSQL:
319
				 *  SELECT currval('seq_mpro_id')
320
				 * For SQL Server:
321
				 *  SELECT SCOPE_IDENTITY()
322
				 * For Oracle:
323
				 *  SELECT "seq_mpro_id".CURRVAL FROM DUAL
324
				 *
325
				 * There's no way to retrive the new ID by a SQL statements that
326
				 * fits for most database servers as they implement their own
327
				 * specific way.
328
				 *
329
				 * @param string SQL statement for retrieving the last inserted record ID
330
				 * @since 2014.03
331
				 * @category Developer
332
				 * @see mshop/product/manager/standard/insert/ansi
333
				 * @see mshop/product/manager/standard/update/ansi
334
				 * @see mshop/product/manager/standard/delete/ansi
335
				 * @see mshop/product/manager/standard/search/ansi
336
				 * @see mshop/product/manager/standard/count/ansi
337
				 */
338
				$path = 'mshop/product/manager/standard/newid';
339
				$item->setId( $this->newId( $conn, $path ) );
340
			}
341
342
			$dbm->release( $conn, $dbname );
343
		}
344
		catch( \Exception $e )
345
		{
346
			$dbm->release( $conn, $dbname );
347
			throw $e;
348
		}
349
	}
350
351
352
	/**
353
	 * Removes multiple items specified by ids in the array.
354
	 *
355
	 * @param array $ids List of IDs
356
	 */
357
	public function deleteItems( array $ids )
358
	{
359
		/** mshop/product/manager/standard/delete/mysql
360
		 * Deletes the items matched by the given IDs from the database
361
		 *
362
		 * @see mshop/product/manager/standard/delete/ansi
363
		 */
364
365
		/** mshop/product/manager/standard/delete/ansi
366
		 * Deletes the items matched by the given IDs from the database
367
		 *
368
		 * Removes the records specified by the given IDs from the product database.
369
		 * The records must be from the site that is configured via the
370
		 * context item.
371
		 *
372
		 * The ":cond" placeholder is replaced by the name of the ID column and
373
		 * the given ID or list of IDs while the site ID is bound to the question
374
		 * mark.
375
		 *
376
		 * The SQL statement should conform to the ANSI standard to be
377
		 * compatible with most relational database systems. This also
378
		 * includes using double quotes for table and column names.
379
		 *
380
		 * @param string SQL statement for deleting items
381
		 * @since 2014.03
382
		 * @category Developer
383
		 * @see mshop/product/manager/standard/insert/ansi
384
		 * @see mshop/product/manager/standard/update/ansi
385
		 * @see mshop/product/manager/standard/newid/ansi
386
		 * @see mshop/product/manager/standard/search/ansi
387
		 * @see mshop/product/manager/standard/count/ansi
388
		 */
389
		$path = 'mshop/product/manager/standard/delete';
390
		$this->deleteItemsBase( $ids, $path );
391
	}
392
393
394
	/**
395
	 * Returns the item specified by its code and domain/type if necessary
396
	 *
397
	 * @param string $code Code of the item
398
	 * @param string[] $ref List of domains to fetch list items and referenced items for
399
	 * @param string|null $domain Domain of the item if necessary to identify the item uniquely
400
	 * @param string|null $type Type code of the item if necessary to identify the item uniquely
401
	 * @return \Aimeos\MShop\Common\Item\Iface Item object
402
	 */
403
	public function findItem( $code, array $ref = array(), $domain = null, $type = null )
404
	{
405
		return $this->findItemBase( array( 'product.code' => $code ), $ref );
406
	}
407
408
409
	/**
410
	 * Returns the product item for the given product ID.
411
	 *
412
	 * @param integer $id Unique ID of the product item
413
	 * @param string[] $ref List of domains to fetch list items and referenced items for
414
	 * @return \Aimeos\MShop\Product\Item\Iface Returns the product item of the given id
415
	 * @throws \Aimeos\MShop\Exception If item couldn't be found
416
	 */
417
	public function getItem( $id, array $ref = array() )
418
	{
419
		return $this->getItemBase( 'product.id', $id, $ref );
420
	}
421
422
423
	/**
424
	 * Search for products based on the given criteria.
425
	 *
426
	 * @param \Aimeos\MW\Criteria\Iface $search Search criteria object
427
	 * @param string[] $ref List of domains to fetch list items and referenced items for
428
	 * @param integer|null &$total Number of items that are available in total
429
	 * @return array List of products implementing \Aimeos\MShop\Product\Item\Iface
430
	 */
431
	public function searchItems( \Aimeos\MW\Criteria\Iface $search, array $ref = array(), &$total = null )
432
	{
433
		$map = $typeIds = array();
434
		$context = $this->getContext();
435
436
		$dbm = $context->getDatabaseManager();
437
		$dbname = $this->getResourceName();
438
		$conn = $dbm->acquire( $dbname );
439
440
		try
441
		{
442
			$required = array( 'product' );
443
			$level = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
444
445
			/** mshop/product/manager/standard/search/mysql
446
			 * Retrieves the records matched by the given criteria in the database
447
			 *
448
			 * @see mshop/product/manager/standard/search/ansi
449
			 */
450
451
			/** mshop/product/manager/standard/search/ansi
452
			 * Retrieves the records matched by the given criteria in the database
453
			 *
454
			 * Fetches the records matched by the given criteria from the product
455
			 * database. The records must be from one of the sites that are
456
			 * configured via the context item. If the current site is part of
457
			 * a tree of sites, the SELECT statement can retrieve all records
458
			 * from the current site and the complete sub-tree of sites.
459
			 *
460
			 * As the records can normally be limited by criteria from sub-managers,
461
			 * their tables must be joined in the SQL context. This is done by
462
			 * using the "internaldeps" property from the definition of the ID
463
			 * column of the sub-managers. These internal dependencies specify
464
			 * the JOIN between the tables and the used columns for joining. The
465
			 * ":joins" placeholder is then replaced by the JOIN strings from
466
			 * the sub-managers.
467
			 *
468
			 * To limit the records matched, conditions can be added to the given
469
			 * criteria object. It can contain comparisons like column names that
470
			 * must match specific values which can be combined by AND, OR or NOT
471
			 * operators. The resulting string of SQL conditions replaces the
472
			 * ":cond" placeholder before the statement is sent to the database
473
			 * server.
474
			 *
475
			 * If the records that are retrieved should be ordered by one or more
476
			 * columns, the generated string of column / sort direction pairs
477
			 * replaces the ":order" placeholder. In case no ordering is required,
478
			 * the complete ORDER BY part including the "\/*-orderby*\/...\/*orderby-*\/"
479
			 * markers is removed to speed up retrieving the records. Columns of
480
			 * sub-managers can also be used for ordering the result set but then
481
			 * no index can be used.
482
			 *
483
			 * The number of returned records can be limited and can start at any
484
			 * number between the begining and the end of the result set. For that
485
			 * the ":size" and ":start" placeholders are replaced by the
486
			 * corresponding values from the criteria object. The default values
487
			 * are 0 for the start and 100 for the size value.
488
			 *
489
			 * The SQL statement should conform to the ANSI standard to be
490
			 * compatible with most relational database systems. This also
491
			 * includes using double quotes for table and column names.
492
			 *
493
			 * @param string SQL statement for searching items
494
			 * @since 2014.03
495
			 * @category Developer
496
			 * @see mshop/product/manager/standard/insert/ansi
497
			 * @see mshop/product/manager/standard/update/ansi
498
			 * @see mshop/product/manager/standard/newid/ansi
499
			 * @see mshop/product/manager/standard/delete/ansi
500
			 * @see mshop/product/manager/standard/count/ansi
501
			 */
502
			$cfgPathSearch = 'mshop/product/manager/standard/search';
503
504
			/** mshop/product/manager/standard/count/mysql
505
			 * Counts the number of records matched by the given criteria in the database
506
			 *
507
			 * @see mshop/product/manager/standard/count/ansi
508
			 */
509
510
			/** mshop/product/manager/standard/count/ansi
511
			 * Counts the number of records matched by the given criteria in the database
512
			 *
513
			 * Counts all records matched by the given criteria from the product
514
			 * database. The records must be from one of the sites that are
515
			 * configured via the context item. If the current site is part of
516
			 * a tree of sites, the statement can count all records from the
517
			 * current site and the complete sub-tree of sites.
518
			 *
519
			 * As the records can normally be limited by criteria from sub-managers,
520
			 * their tables must be joined in the SQL context. This is done by
521
			 * using the "internaldeps" property from the definition of the ID
522
			 * column of the sub-managers. These internal dependencies specify
523
			 * the JOIN between the tables and the used columns for joining. The
524
			 * ":joins" placeholder is then replaced by the JOIN strings from
525
			 * the sub-managers.
526
			 *
527
			 * To limit the records matched, conditions can be added to the given
528
			 * criteria object. It can contain comparisons like column names that
529
			 * must match specific values which can be combined by AND, OR or NOT
530
			 * operators. The resulting string of SQL conditions replaces the
531
			 * ":cond" placeholder before the statement is sent to the database
532
			 * server.
533
			 *
534
			 * Both, the strings for ":joins" and for ":cond" are the same as for
535
			 * the "search" SQL statement.
536
			 *
537
			 * Contrary to the "search" statement, it doesn't return any records
538
			 * but instead the number of records that have been found. As counting
539
			 * thousands of records can be a long running task, the maximum number
540
			 * of counted records is limited for performance reasons.
541
			 *
542
			 * The SQL statement should conform to the ANSI standard to be
543
			 * compatible with most relational database systems. This also
544
			 * includes using double quotes for table and column names.
545
			 *
546
			 * @param string SQL statement for counting items
547
			 * @since 2014.03
548
			 * @category Developer
549
			 * @see mshop/product/manager/standard/insert/ansi
550
			 * @see mshop/product/manager/standard/update/ansi
551
			 * @see mshop/product/manager/standard/newid/ansi
552
			 * @see mshop/product/manager/standard/delete/ansi
553
			 * @see mshop/product/manager/standard/search/ansi
554
			 */
555
			$cfgPathCount = 'mshop/product/manager/standard/count';
556
557
			$results = $this->searchItemsBase( $conn, $search, $cfgPathSearch, $cfgPathCount, $required, $total, $level );
558
559
			while( ( $row = $results->fetch() ) !== false )
560
			{
561
				$config = $row['product.config'];
562
563
				if( $config && ( $row['product.config'] = json_decode( $config, true ) ) === null )
564
				{
565
					$msg = sprintf( 'Invalid JSON as result of search for ID "%2$s" in "%1$s": %3$s', 'mshop_product.config', $row['product.id'], $config );
566
					$this->getContext()->getLogger()->log( $msg, \Aimeos\MW\Logger\Base::WARN );
567
				}
568
569
				$map[$row['product.id']] = $row;
570
				$typeIds[$row['product.typeid']] = null;
571
			}
572
573
			$dbm->release( $conn, $dbname );
574
		}
575
		catch( \Exception $e )
576
		{
577
			$dbm->release( $conn, $dbname );
578
			throw $e;
579
		}
580
581
		if( !empty( $typeIds ) )
582
		{
583
			$typeManager = $this->getSubManager( 'type' );
584
			$typeSearch = $typeManager->createSearch();
585
			$typeSearch->setConditions( $typeSearch->compare( '==', 'product.type.id', array_keys( $typeIds ) ) );
586
			$typeSearch->setSlice( 0, $search->getSliceSize() );
587
			$typeItems = $typeManager->searchItems( $typeSearch );
588
589
			foreach( $map as $id => $row )
590
			{
591
				if( isset( $typeItems[$row['product.typeid']] ) )
592
				{
593
					$map[$id]['product.type'] = $typeItems[$row['product.typeid']]->getCode();
594
					$map[$id]['product.typename'] = $typeItems[$row['product.typeid']]->getName();
595
				}
596
			}
597
		}
598
599
		$propItems = array();
600
		if( in_array( 'product/property', $ref, true ) ) {
601
			$propItems = $this->getPropertyItems( array_keys( $map ) );
602
		}
603
604
605
		return $this->buildItems( $map, $ref, 'product', $propItems );
606
	}
607
608
609
	/**
610
	 * Returns the available manager types
611
	 *
612
	 * @param boolean $withsub Return also the resource type of sub-managers if true
613
	 * @return array Type of the manager and submanagers, subtypes are separated by slashes
614
	 */
615
	public function getResourceType( $withsub = true )
616
	{
617
		$path = 'mshop/product/manager/submanagers';
618
619
		return $this->getResourceTypeBase( 'product', $path, array( 'type', 'lists', 'property' ), $withsub );
620
	}
621
622
623
	/**
624
	 * Returns the attributes that can be used for searching.
625
	 *
626
	 * @param boolean $withsub Return also attributes of sub-managers if true
627
	 * @return array Returns a list of attribtes implementing \Aimeos\MW\Criteria\Attribute\Iface
628
	 */
629
	public function getSearchAttributes( $withsub = true )
630
	{
631
		/** mshop/product/manager/submanagers
632
		 * List of manager names that can be instantiated by the product manager
633
		 *
634
		 * Managers provide a generic interface to the underlying storage.
635
		 * Each manager has or can have sub-managers caring about particular
636
		 * aspects. Each of these sub-managers can be instantiated by its
637
		 * parent manager using the getSubManager() method.
638
		 *
639
		 * The search keys from sub-managers can be normally used in the
640
		 * manager as well. It allows you to search for items of the manager
641
		 * using the search keys of the sub-managers to further limit the
642
		 * retrieved list of items.
643
		 *
644
		 * @param array List of sub-manager names
645
		 * @since 2014.03
646
		 * @category Developer
647
		 */
648
		$path = 'mshop/product/manager/submanagers';
649
		$default = array( 'type', 'property', 'lists' );
650
651
		return $this->getSearchAttributesBase( $this->searchConfig, $path, $default, $withsub );
652
	}
653
654
655
	/**
656
	 * Returns a new manager for product extensions.
657
	 *
658
	 * @param string $manager Name of the sub manager type in lower case
659
	 * @param string|null $name Name of the implementation, will be from configuration (or Default) if null
660
	 * @return \Aimeos\MShop\Common\Manager\Iface Submanager, e.g. type, property, etc.
661
	 */
662
	public function getSubManager( $manager, $name = null )
663
	{
664
		return $this->getSubManagerBase( 'product', $manager, $name );
665
	}
666
667
668
	/**
669
	 * Creates a search object and optionally sets base criteria.
670
	 *
671
	 * @param boolean $default Add default criteria
672
	 * @return \Aimeos\MW\Criteria\Iface Criteria object
673
	 */
674
	public function createSearch( $default = false )
675
	{
676
		if( $default === true )
677
		{
678
			$curDate = date( 'Y-m-d H:i:00', time() );
679
			$object = $this->createSearchBase( 'product' );
680
681
			$expr = array( $object->getConditions() );
682
683
			$temp = array(
684
				$object->compare( '==', 'product.datestart', null ),
685
				$object->compare( '<=', 'product.datestart', $curDate ),
686
			);
687
			$expr[] = $object->combine( '||', $temp );
688
689
			$temp = array(
690
				$object->compare( '==', 'product.dateend', null ),
691
				$object->compare( '>=', 'product.dateend', $curDate ),
692
			);
693
			$expr[] = $object->combine( '||', $temp );
694
695
			$object->setConditions( $object->combine( '&&', $expr ) );
696
697
			return $object;
698
		}
699
700
		return parent::createSearch();
701
	}
702
703
704
	/**
705
	 * Create new product item object initialized with given parameters.
706
	 *
707
	 * @param array $values Associative list of key/value pairs
708
	 * @param array $listitems List of items implementing \Aimeos\MShop\Common\Item\Lists\Iface
709
	 * @param array $refItems List of items implementing \Aimeos\MShop\Common\Item\Iface
710
	 * @param array $propertyItems List of items implementing \Aimeos\MShop\Product\Item\Property\Iface
711
	 * @return \Aimeos\MShop\Product\Item\Iface New product item
712
	 */
713
	protected function createItemBase( array $values = array(), array $listitems = array(),
714
		array $refItems = array(), array $propertyItems = array() )
715
	{
716
		return new \Aimeos\MShop\Product\Item\Standard( $values, $listitems, $refItems, $propertyItems );
717
	}
718
719
720
	/**
721
	 * Returns the property items for the given product IDs
722
	 *
723
	 * @param array $prodIds List of product IDs
724
	 * @return array Associative list of product IDs / property IDs as keys and items implementing
725
	 * 	\Aimeos\MShop\Product\Item\Property\Iface as values
726
	 */
727
	protected function getPropertyItems( array $prodIds )
728
	{
729
		$list = array();
730
		$propManager = $this->getSubManager( 'property' );
731
732
		$propSearch = $propManager->createSearch();
733
		$propSearch->setConditions( $propSearch->compare( '==', 'product.property.parentid', $prodIds ) );
734
		$propSearch->setSlice( 0, 0x7fffffff );
735
736
		foreach( $propManager->searchItems( $propSearch ) as $id => $propItem ) {
737
			$list[$propItem->getParentId()][$id] = $propItem;
738
		}
739
740
		return $list;
741
	}
742
}
743