Passed
Push — master ( 44c1d8...6b9f69 )
by Aimeos
16:18
created

DBBase::searchItemsIndexBase()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 61
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 18
dl 0
loc 61
rs 9.6666
c 0
b 0
f 0
cc 4
nc 6
nop 5

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, https://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2015-2023
6
 * @package MShop
7
 * @subpackage Index
8
 */
9
10
11
namespace Aimeos\MShop\Index\Manager;
12
13
14
/**
15
 * Base class for all database based index managers
16
 *
17
 * @package MShop
18
 * @subpackage Index
19
 */
20
abstract class DBBase
21
	extends \Aimeos\MShop\Common\Manager\Base
22
	implements \Aimeos\MShop\Product\Manager\Iface
23
{
24
	private \Aimeos\MShop\Common\Manager\Iface $manager;
25
26
27
	/**
28
	 * Initializes the manager object
29
	 *
30
	 * @param \Aimeos\MShop\ContextIface $context Context object
31
	 */
32
	public function __construct( \Aimeos\MShop\ContextIface $context )
33
	{
34
		parent::__construct( $context );
35
36
		/** mshop/index/manager/resource
37
		 * Name of the database connection resource to use
38
		 *
39
		 * You can configure a different database connection for each data domain
40
		 * and if no such connection name exists, the "db" connection will be used.
41
		 * It's also possible to use the same database connection for different
42
		 * data domains by configuring the same connection name using this setting.
43
		 *
44
		 * @param string Database connection name
45
		 * @since 2023.04
46
		 */
47
		$this->setResourceName( $context->config()->get( 'mshop/index/manager/resource', 'db-product' ) );
48
		$this->manager = \Aimeos\MShop::create( $this->context(), 'product' );
49
	}
50
51
52
	/**
53
	 * Removes old entries from the storage.
54
	 *
55
	 * @param iterable $siteids List of IDs for sites whose entries should be deleted
56
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
57
	 */
58
	public function clear( iterable $siteids ) : \Aimeos\MShop\Common\Manager\Iface
59
	{
60
		foreach( $this->getSubManagers() as $submanager ) {
61
			$submanager->clear( $siteids );
62
		}
63
64
		return $this;
65
	}
66
67
68
	/**
69
	 * Creates a new empty item instance
70
	 *
71
	 * @param array $values Values the item should be initialized with
72
	 * @return \Aimeos\MShop\Product\Item\Iface New product item object
73
	 */
74
	public function create( array $values = [] ) : \Aimeos\MShop\Common\Item\Iface
75
	{
76
		return $this->manager->create( $values );
77
	}
78
79
80
	/**
81
	 * Creates a filter object.
82
	 *
83
	 * @param bool|null $default Add default criteria or NULL for relaxed default criteria
84
	 * @param bool $site TRUE for adding site criteria to limit items by the site of related items
85
	 * @return \Aimeos\Base\Criteria\Iface Returns the filter object
86
	 */
87
	public function filter( ?bool $default = false, bool $site = false ) : \Aimeos\Base\Criteria\Iface
88
	{
89
		return $this->manager->filter( $default );
90
	}
91
92
93
	/**
94
	 * Removes multiple items.
95
	 *
96
	 * @param \Aimeos\MShop\Common\Item\Iface|\Aimeos\Map|array|string $items Item object, ID or a list of them
97
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
98
	 */
99
	public function delete( $itemIds ) : \Aimeos\MShop\Common\Manager\Iface
100
	{
101
		foreach( $this->getSubManagers() as $submanager ) {
102
			$submanager->delete( $itemIds );
103
		}
104
105
		return $this;
106
	}
107
108
109
	/**
110
	 * Returns the item specified by its code and domain/type if necessary
111
	 *
112
	 * @param string $code Code of the item
113
	 * @param string[] $ref List of domains to fetch list items and referenced items for
114
	 * @param string|null $domain Domain of the item if necessary to identify the item uniquely
115
	 * @param string|null $type Type code of the item if necessary to identify the item uniquely
116
	 * @param bool|null $default Add default criteria or NULL for relaxed default criteria
117
	 * @return \Aimeos\MShop\Common\Item\Iface Item object
118
	 */
119
	public function find( string $code, array $ref = [], ?string $domain = 'product', string $type = null,
120
		?bool $default = false ) : \Aimeos\MShop\Common\Item\Iface
121
	{
122
		return $this->manager->find( $code, $ref, $domain, $type, $default );
123
	}
124
125
126
	/**
127
	 * Returns the product item for the given ID
128
	 *
129
	 * @param string $id Id of item
130
	 * @param string[] $ref List of domains to fetch list items and referenced items for
131
	 * @param bool|null $default Add default criteria or NULL for relaxed default criteria
132
	 * @return \Aimeos\MShop\Product\Item\Iface Product item object
133
	 */
134
	public function get( string $id, array $ref = [], ?bool $default = false ) : \Aimeos\MShop\Common\Item\Iface
135
	{
136
		return $this->manager->get( $id, $ref, $default );
137
	}
138
139
140
	/**
141
	 * Returns a list of attribute objects describing the available criteria for searching
142
	 *
143
	 * @param bool $withsub True to return attributes of sub-managers too
144
	 * @return array List of items implementing \Aimeos\Base\Criteria\Attribute\Iface
145
	 */
146
	public function getSearchAttributes( bool $withsub = true ) : array
147
	{
148
		return $this->manager->getSearchAttributes( $withsub );
149
	}
150
151
152
	/**
153
	 * Iterates over all matched items and returns the found ones
154
	 *
155
	 * @param \Aimeos\MShop\Common\Cursor\Iface $cursor Cursor object with filter, domains and cursor
156
	 * @param string[] $ref List of domains whose items should be fetched too
157
	 * @return \Aimeos\Map|null List of items implementing \Aimeos\MShop\Common\Item\Iface with ids as keys
158
	 */
159
	public function iterate( \Aimeos\MShop\Common\Cursor\Iface $cursor, array $ref = [] ) : ?\Aimeos\Map
160
	{
161
		if( $cursor->value() === '' ) {
162
			return null;
163
		}
164
165
		$filter = $cursor->filter()->add( 'product.id', '>', (int) $cursor->value() )->order( 'product.id' );
166
		$items = $this->search( $filter, $ref );
167
168
		$cursor->setValue( $items->lastKey() ?: '' );
169
		return $items;
170
	}
171
172
173
	/**
174
	 * Updates the rating of the item
175
	 *
176
	 * @param string $id ID of the item
177
	 * @param string $rating Decimal value of the rating
178
	 * @param int $ratings Total number of ratings for the item
179
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
180
	 */
181
	public function rate( string $id, string $rating, int $ratings ) : \Aimeos\MShop\Common\Manager\Iface
182
	{
183
		$this->manager->rate( $id, $rating, $ratings );
184
		return $this;
185
	}
186
187
188
	/**
189
	 * Rebuilds the customer index
190
	 *
191
	 * @param \Aimeos\MShop\Product\Item\Iface[] $items Associative list of product IDs and items values
192
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
193
	 */
194
	public function rebuild( iterable $items = [] ) : \Aimeos\MShop\Index\Manager\Iface
195
	{
196
		foreach( $this->getSubManagers() as $submanager ) {
197
			$submanager->rebuild( $items );
198
		}
199
200
		return $this;
201
	}
202
203
204
	/**
205
	 * Removes the products from the product index.
206
	 *
207
	 * @param iterable|string $ids Product ID or list of IDs
208
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
209
	 */
210
	 public function remove( $ids ) : \Aimeos\MShop\Index\Manager\Iface
211
	 {
212
		foreach( $this->getSubManagers() as $submanager ) {
213
			$submanager->remove( $ids );
214
		}
215
216
		return $this;
217
	}
218
219
220
	/**
221
	 * Adds or updates an item object or a list of them.
222
	 *
223
	 * @param \Aimeos\Map|\Aimeos\MShop\Common\Item\Iface[]|\Aimeos\MShop\Common\Item\Iface $items Item or list of items whose data should be saved
224
	 * @param bool $fetch True if the new ID should be returned in the item
225
	 * @return \Aimeos\Map|\Aimeos\MShop\Common\Item\Iface Saved item or items
226
	 */
227
	public function save( $items, bool $fetch = true )
228
	{
229
		$this->rebuild( map( $this->manager->save( $items, true ) ) );
230
		return $items;
231
	}
232
233
234
	/**
235
	 * Updates if the product is in stock
236
	 *
237
	 * @param string $id ID of the procuct item
238
	 * @param int $value "0" or "1" if product is in stock or not
239
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
240
	 */
241
	public function stock( string $id, int $value ) : \Aimeos\MShop\Common\Manager\Iface
242
	{
243
		$this->manager->stock( $id, $value );
244
		return $this;
245
	}
246
247
248
	/**
249
	 * Removes all entries not touched after the given timestamp
250
	 *
251
	 * @param string $timestamp Timestamp in ISO format (YYYY-MM-DD HH:mm:ss)
252
	 * @param string $path Configuration path to the SQL statement to execute
253
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
254
	 */
255
	protected function cleanupBase( string $timestamp, string $path ) : \Aimeos\MShop\Index\Manager\Iface
256
	{
257
		$context = $this->context();
258
		$siteid = $context->locale()->getSiteId();
259
260
		$this->begin();
261
		$conn = $context->db( $this->getResourceName() );
262
263
		try
264
		{
265
			$stmt = $this->getCachedStatement( $conn, $path );
266
267
			$stmt->bind( 1, $timestamp ); // ctime
268
			$stmt->bind( 2, $siteid );
269
270
			$stmt->execute()->finish();
271
		}
272
		catch( \Exception $e )
273
		{
274
			$this->rollback();
275
			throw $e;
276
		}
277
278
		$this->commit();
279
280
		foreach( $this->getSubManagers() as $submanager ) {
281
			$submanager->cleanup( $timestamp );
282
		}
283
284
		return $this;
285
	}
286
287
288
	/**
289
	 * Removes several items from the index
290
	 *
291
	 * @param string[] $ids List of product IDs
292
	 * @param string $path Configuration path to the SQL statement to execute
293
	 * @param bool $siteidcheck If siteid should be used in the statement
294
	 * @param string $name Name of the ID column
295
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
296
	 */
297
	protected function deleteItemsBase( $ids, string $path, bool $siteidcheck = true,
298
		string $name = 'prodid' ) : \Aimeos\MShop\Common\Manager\Iface
299
	{
300
		foreach( $this->getSubManagers() as $submanager ) {
301
			$submanager->delete( $ids );
302
		}
303
304
		return parent::deleteItemsBase( $ids, $path, $siteidcheck, $name );
305
	}
306
307
308
	/**
309
	 * Returns the product manager instance
310
	 *
311
	 * @return \Aimeos\MShop\Product\Manager\Iface Product manager object
312
	 */
313
	protected function getManager() : \Aimeos\MShop\Common\Manager\Iface
314
	{
315
		return $this->manager;
316
	}
317
318
319
	/**
320
	 * Returns the string replacements for the SQL statements
321
	 *
322
	 * @param \Aimeos\Base\Criteria\Iface $search Search critera object
323
	 * @param \Aimeos\Base\Criteria\Attribute\Iface[] $attributes Associative list of search keys and criteria attribute items as values
324
	 * @param \Aimeos\Base\Criteria\Attribute\Iface[] $attributes Associative list of search keys and criteria attribute items as values for the base table
325
	 * @param \Aimeos\Base\Criteria\Plugin\Iface[] $plugins Associative list of search keys and criteria plugin items as values
326
	 * @param string[] $joins Associative list of SQL joins
327
	 * @param \Aimeos\Base\Criteria\Attribute\Iface[] $columns Additional columns to retrieve values from
328
	 * @return array Array of keys, find and replace arrays
329
	 */
330
	protected function getSQLReplacements( \Aimeos\Base\Criteria\Iface $search, array $attributes, array $attronly, array $plugins, array $joins ) : array
331
	{
332
		$types = $this->getSearchTypes( $attributes );
333
		$funcs = $this->getSearchFunctions( $attributes );
334
		$translations = $this->getSearchTranslations( $attributes );
335
336
		if( !empty( $search->getSortations() ) )
337
		{
338
			$names = $search->translate( $search->getSortations(), [], $funcs );
339
			$cols = $search->translate( $search->getSortations(), $translations, $funcs );
340
341
			$list = $translations = [];
342
			foreach( $cols as $idx => $col )
343
			{
344
				$list[] = 'MIN(' . $col . ') AS "s' . $idx . '"';
345
				$translations[$names[$idx]] = '"s' . $idx . '"';
346
			}
347
		}
348
349
		$map = parent::getSQLReplacements( $search, $attributes, $attronly, $plugins, $joins );
350
351
		$map[':mincols'] = !empty( $list ) ? ', ' . implode( ', ', $list ) : '';
352
		$map[':order'] = $search->getSortationSource( $types, $translations, $funcs );
353
354
		return $map;
355
	}
356
357
358
	/**
359
	 * Optimizes the catalog customer index if necessary
360
	 *
361
	 * @param string $path Configuration path to the SQL statements to execute
362
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
363
	 */
364
	protected function optimizeBase( string $path ) : \Aimeos\MShop\Index\Manager\Iface
365
	{
366
		$context = $this->context();
367
		$conn = $context->db( $this->getResourceName() );
368
369
		foreach( (array) $this->getSqlConfig( $path ) as $sql ) {
370
			$conn->create( $sql )->execute()->finish();
371
		}
372
373
		foreach( $this->getSubManagers() as $submanager ) {
374
			$submanager->optimize();
375
		}
376
377
		return $this;
378
	}
379
380
381
	/**
382
	 * Searches for items matching the given criteria.
383
	 *
384
	 * @param \Aimeos\Base\Criteria\Iface $search Search criteria
385
	 * @param string[] $ref List of domains to fetch list items and referenced items for
386
	 * @param int &$total Total number of items matched by the given criteria
387
	 * @param string $cfgPathSearch Configuration path to the search SQL statement
388
	 * @param string $cfgPathCount Configuration path to the count SQL statement
389
	 * @return \Aimeos\MShop\Product\Item\Iface[] List of product items
390
	 */
391
	protected function searchItemsIndexBase( \Aimeos\Base\Criteria\Iface $search,
392
		array $ref, int &$total = null, string $cfgPathSearch, string $cfgPathCount ) : \Aimeos\Map
393
	{
394
		$list = $ids = [];
395
		$context = $this->context();
396
		$conn = $context->db( $this->getResourceName() );
397
398
		$required = array( 'product' );
399
400
		/** mshop/index/manager/sitemode
401
		 * Mode how items from levels below or above in the site tree are handled
402
		 *
403
		 * By default, only items from the current site are fetched from the
404
		 * storage. If the ai-sites extension is installed, you can create a
405
		 * tree of sites. Then, this setting allows you to define for the
406
		 * whole index domain if items from parent sites are inherited,
407
		 * sites from child sites are aggregated or both.
408
		 *
409
		 * Available constants for the site mode are:
410
		 * * 0 = only items from the current site
411
		 * * 1 = inherit items from parent sites
412
		 * * 2 = aggregate items from child sites
413
		 * * 3 = inherit and aggregate items at the same time
414
		 *
415
		 * You also need to set the mode in the locale manager
416
		 * (mshop/locale/manager/sitelevel) to one of the constants.
417
		 * If you set it to the same value, it will work as described but you
418
		 * can also use different modes. For example, if inheritance and
419
		 * aggregation is configured the locale manager but only inheritance
420
		 * in the domain manager because aggregating items makes no sense in
421
		 * this domain, then items wil be only inherited. Thus, you have full
422
		 * control over inheritance and aggregation in each domain.
423
		 *
424
		 * @param int Constant from Aimeos\MShop\Locale\Manager\Base class
425
		 * @category Developer
426
		 * @since 2018.01
427
		 * @see mshop/locale/manager/sitelevel
428
		 */
429
		$level = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
430
		$level = $context->config()->get( 'mshop/index/manager/sitemode', $level );
431
432
		$results = $this->searchItemsBase( $conn, $search, $cfgPathSearch, $cfgPathCount, $required, $total, $level );
433
434
		while( ( $row = $results->fetch() ) !== null ) {
435
			$ids[] = $row['id'];
436
		}
437
438
		$manager = \Aimeos\MShop::create( $context, 'product' );
439
		$prodSearch = $manager->filter();
440
		$prodSearch->setConditions( $prodSearch->compare( '==', 'product.id', $ids ) );
441
		$prodSearch->slice( 0, $search->getLimit() );
442
		$items = $manager->search( $prodSearch, $ref );
443
444
		foreach( $ids as $id )
445
		{
446
			if( isset( $items[$id] ) ) {
447
				$list[$id] = $items[$id];
448
			}
449
		}
450
451
		return map( $list );
452
	}
453
}
454