Passed
Push — master ( 93fe31...2e8c1e )
by Aimeos
06:37
created

DBBase   A

Complexity

Total Complexity 39

Size/Duplication

Total Lines 455
Duplicated Lines 0 %

Importance

Changes 6
Bugs 0 Features 0
Metric Value
eloc 123
dl 0
loc 455
rs 9.28
c 6
b 0
f 0
wmc 39

18 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A getSearchAttributes() 0 3 1
A clear() 0 7 2
A rebuild() 0 7 2
A filter() 0 3 1
A create() 0 3 1
A find() 0 4 1
A optimizeBase() 0 27 4
A get() 0 3 1
B getSQLReplacements() 0 46 7
A saveItems() 0 10 2
A deleteItemsBase() 0 8 2
A remove() 0 7 2
A cleanupBase() 0 37 3
A saveItem() 0 6 1
A getManager() 0 3 1
A searchItemsIndexBase() 0 74 5
A delete() 0 7 2
1
<?php
2
3
/**
4
 * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2015-2020
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 $manager;
25
26
27
	/**
28
	 * Initializes the manager object
29
	 *
30
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context object
31
	 */
32
	public function __construct( \Aimeos\MShop\Context\Item\Iface $context )
33
	{
34
		parent::__construct( $context );
35
36
		$this->setResourceName( 'db-product' );
37
		$this->manager = \Aimeos\MShop::create( $this->getContext(), 'product' );
38
	}
39
40
41
	/**
42
	 * Removes old entries from the storage.
43
	 *
44
	 * @param string[] $siteids List of IDs for sites whose entries should be deleted
45
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
46
	 */
47
	public function clear( array $siteids ) : \Aimeos\MShop\Common\Manager\Iface
48
	{
49
		foreach( $this->getSubManagers() as $submanager ) {
50
			$submanager->clear( $siteids );
51
		}
52
53
		return $this;
54
	}
55
56
57
	/**
58
	 * Creates a new empty item instance
59
	 *
60
	 * @param array $values Values the item should be initialized with
61
	 * @return \Aimeos\MShop\Product\Item\Iface New product item object
62
	 */
63
	public function create( array $values = [] ) : \Aimeos\MShop\Common\Item\Iface
64
	{
65
		return $this->manager->create( $values );
66
	}
67
68
69
	/**
70
	 * Creates a filter object.
71
	 *
72
	 * @param bool $default Add default criteria
73
	 * @param bool $site TRUE for adding site criteria to limit items by the site of related items
74
	 * @return \Aimeos\MW\Criteria\Iface Returns the filter object
75
	 */
76
	public function filter( bool $default = false, bool $site = false ) : \Aimeos\MW\Criteria\Iface
77
	{
78
		return $this->manager->filter( $default );
79
	}
80
81
82
	/**
83
	 * Removes multiple items.
84
	 *
85
	 * @param \Aimeos\MShop\Common\Item\Iface|\Aimeos\Map|array|string $items Item object, ID or a list of them
86
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
87
	 */
88
	public function delete( $itemIds ) : \Aimeos\MShop\Common\Manager\Iface
89
	{
90
		foreach( $this->getSubManagers() as $submanager ) {
91
			$submanager->delete( $itemIds );
92
		}
93
94
		return $this;
95
	}
96
97
98
	/**
99
	 * Returns the item specified by its code and domain/type if necessary
100
	 *
101
	 * @param string $code Code of the item
102
	 * @param string[] $ref List of domains to fetch list items and referenced items for
103
	 * @param string|null $domain Domain of the item if necessary to identify the item uniquely
104
	 * @param string|null $type Type code of the item if necessary to identify the item uniquely
105
	 * @param bool $default True to add default criteria
106
	 * @return \Aimeos\MShop\Common\Item\Iface Item object
107
	 */
108
	public function find( string $code, array $ref = [], string $domain = 'product', string $type = null,
109
		bool $default = false ) : \Aimeos\MShop\Common\Item\Iface
110
	{
111
		return $this->manager->find( $code, $ref, $domain, $type, $default );
112
	}
113
114
115
	/**
116
	 * Returns the product item for the given ID
117
	 *
118
	 * @param string $id Id of item
119
	 * @param string[] $ref List of domains to fetch list items and referenced items for
120
	 * @param bool $default Add default criteria
121
	 * @return \Aimeos\MShop\Product\Item\Iface Product item object
122
	 */
123
	public function get( string $id, array $ref = [], bool $default = false ) : \Aimeos\MShop\Common\Item\Iface
124
	{
125
		return $this->manager->get( $id, $ref, $default );
126
	}
127
128
129
	/**
130
	 * Returns a list of attribute objects describing the available criteria for searching
131
	 *
132
	 * @param bool $withsub True to return attributes of sub-managers too
133
	 * @return array List of items implementing \Aimeos\MW\Criteria\Attribute\Iface
134
	 */
135
	public function getSearchAttributes( bool $withsub = true ) : array
136
	{
137
		return $this->manager->getSearchAttributes( $withsub );
138
	}
139
140
141
	/**
142
	 * Rebuilds the customer index
143
	 *
144
	 * @param \Aimeos\MShop\Product\Item\Iface[] $items Associative list of product IDs and items values
145
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
146
	 */
147
	public function rebuild( array $items = [] ) : \Aimeos\MShop\Index\Manager\Iface
148
	{
149
		foreach( $this->getSubManagers() as $submanager ) {
150
			$submanager->rebuild( $items );
151
		}
152
153
		return $this;
154
	}
155
156
157
	/**
158
	 * Removes the products from the product index.
159
	 *
160
	 * @param array|string $ids Product ID or list of IDs
161
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
162
	 */
163
	 public function remove( $ids ) : \Aimeos\MShop\Index\Manager\Iface
164
	 {
165
		foreach( $this->getSubManagers() as $submanager ) {
166
			$submanager->remove( $ids );
167
		}
168
169
		return $this;
170
	 }
171
172
173
	/**
174
	 * Stores a new item into the index
175
	 *
176
	 * @param \Aimeos\MShop\Product\Item\Iface $item Product item
177
	 * @param bool $fetch True if the new ID should be set in the item
178
	 * @return \Aimeos\MShop\Product\Item\Iface Saved item
179
	 */
180
	public function saveItem( \Aimeos\MShop\Product\Item\Iface $item, bool $fetch = true ) : \Aimeos\MShop\Product\Item\Iface
181
	{
182
		$item = $this->manager->saveItem( $item, true );
183
		$this->rebuild( [$item->getId() => $item] );
184
185
		return $item;
186
	}
187
188
189
	/**
190
	 * Adds or updates a list of items
191
	 *
192
	 * @param \Aimeos\MShop\Product\Item\Iface[] $items List of items whose data should be saved
193
	 * @param bool $fetch True if the new ID should be returned in the item
194
	 * @return \Aimeos\MShop\Product\Item\Iface[] Saved items
195
	 */
196
	public function saveItems( array $items, bool $fetch = true ) : array
197
	{
198
		$list = [];
199
200
		foreach( $this->manager->saveItems( $items, true ) as $item ) {
201
			$list[$item->getId()] = $item;
202
		}
203
204
		$this->rebuild( $list );
205
		return $list;
206
	}
207
208
209
	/**
210
	 * Removes all entries not touched after the given timestamp
211
	 *
212
	 * @param string $timestamp Timestamp in ISO format (YYYY-MM-DD HH:mm:ss)
213
	 * @param string $path Configuration path to the SQL statement to execute
214
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
215
	 */
216
	protected function cleanupBase( string $timestamp, string $path ) : \Aimeos\MShop\Index\Manager\Iface
217
	{
218
		$context = $this->getContext();
219
		$siteid = $context->getLocale()->getSiteId();
220
221
222
		$this->begin();
223
224
		$dbm = $context->getDatabaseManager();
225
		$dbname = $this->getResourceName();
226
		$conn = $dbm->acquire( $dbname );
227
228
		try
229
		{
230
			$stmt = $this->getCachedStatement( $conn, $path );
231
232
			$stmt->bind( 1, $timestamp ); // ctime
233
			$stmt->bind( 2, $siteid );
234
235
			$stmt->execute()->finish();
236
237
			$dbm->release( $conn, $dbname );
238
		}
239
		catch( \Exception $e )
240
		{
241
			$dbm->release( $conn, $dbname );
242
			$this->rollback();
243
			throw $e;
244
		}
245
246
		$this->commit();
247
248
		foreach( $this->getSubManagers() as $submanager ) {
249
			$submanager->cleanup( $timestamp );
250
		}
251
252
		return $this;
253
	}
254
255
256
	/**
257
	 * Removes several items from the index
258
	 *
259
	 * @param string[] $ids List of product IDs
260
	 * @param string $path Configuration path to the SQL statement to execute
261
	 * @param bool $siteidcheck If siteid should be used in the statement
262
	 * @param string $name Name of the ID column
263
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
264
	 */
265
	protected function deleteItemsBase( $ids, string $path, bool $siteidcheck = true,
266
		string $name = 'prodid' ) : \Aimeos\MShop\Common\Manager\Iface
267
	{
268
		foreach( $this->getSubManagers() as $submanager ) {
269
			$submanager->delete( $ids );
270
		}
271
272
		return parent::deleteItemsBase( $ids, $path, $siteidcheck, $name );
273
	}
274
275
276
	/**
277
	 * Returns the product manager instance
278
	 *
279
	 * @return \Aimeos\MShop\Product\Manager\Iface Product manager object
280
	 */
281
	protected function getManager() : \Aimeos\MShop\Common\Manager\Iface
282
	{
283
		return $this->manager;
284
	}
285
286
287
	/**
288
	 * Returns the string replacements for the SQL statements
289
	 *
290
	 * @param \Aimeos\MW\Criteria\Iface $search Search critera object
291
	 * @param \Aimeos\MW\Criteria\Attribute\Iface[] $attributes Associative list of search keys and criteria attribute items
292
	 * @param \Aimeos\MW\Criteria\Plugin\Iface[] $plugins Associative list of item keys and criteria plugin objects
293
	 * @param string[] $joins Associative list of SQL joins
294
	 * @param \Aimeos\MW\Criteria\Attribute\Iface[] $columns Additional columns to retrieve values from
295
	 * @return array Array of keys, find and replace arrays
296
	 */
297
	protected function getSQLReplacements( \Aimeos\MW\Criteria\Iface $search, array $attributes, array $plugins,
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
298
		array $joins, array $columns = [] ) : array
299
	{
300
		$list = [];
301
		$types = $this->getSearchTypes( $attributes );
302
		$funcs = $this->getSearchFunctions( $attributes );
303
		$translations = $this->getSearchTranslations( $attributes );
304
305
		$colstring = '';
306
		foreach( $columns as $name => $entry ) {
307
			$colstring .= $entry->getInternalCode() . ', ';
308
		}
309
310
		$find = array( ':columns', ':joins', ':cond', ':start', ':size' );
311
		$replace = array(
312
			$colstring,
313
			implode( "\n", array_unique( $joins ) ),
314
			$search->getConditionSource( $types, $translations, $plugins, $funcs ),
315
			$search->getSliceStart(),
316
			$search->getSliceSize(),
317
		);
318
319
		if( empty( $search->getSortations() ) && ( $attribute = reset( $attributes ) ) !== false )
320
		{
321
			$search = ( clone $search )->setSortations( [$search->sort( '+', $attribute->getCode() )] );
322
		}
323
		elseif( !empty( $search->getSortations() ) )
324
		{
325
			$names = $search->translate( $search->getSortations(), [], $funcs );
326
			$cols = $search->translate( $search->getSortations(), $translations, $funcs );
327
328
			$list = $translations = [];
329
			foreach( $cols as $idx => $col )
330
			{
331
				$list[] = 'MIN(' . $col . ') AS "s' . $idx . '"';
332
				$translations[$names[$idx]] = '"s' . $idx . '"';
333
			}
334
		}
335
336
		$find[] = ':mincols';
337
		$replace[] = !empty( $list ) ? ', ' . implode( ', ', $list ) : '';
338
339
		$find[] = ':order';
340
		$replace[] = $search->getSortationSource( $types, $translations, $funcs );
341
342
		return [$find, $replace];
343
	}
344
345
346
	/**
347
	 * Optimizes the catalog customer index if necessary
348
	 *
349
	 * @param string $path Configuration path to the SQL statements to execute
350
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
351
	 */
352
	protected function optimizeBase( string $path ) : \Aimeos\MShop\Index\Manager\Iface
353
	{
354
		$context = $this->getContext();
355
356
		$dbm = $context->getDatabaseManager();
357
		$dbname = $this->getResourceName();
358
		$conn = $dbm->acquire( $dbname );
359
360
		try
361
		{
362
			foreach( (array) $this->getSqlConfig( $path ) as $sql ) {
363
				$conn->create( $sql )->execute()->finish();
364
			}
365
366
			$dbm->release( $conn, $dbname );
367
		}
368
		catch( \Exception $e )
369
		{
370
			$dbm->release( $conn, $dbname );
371
			throw $e;
372
		}
373
374
		foreach( $this->getSubManagers() as $submanager ) {
375
			$submanager->optimize();
376
		}
377
378
		return $this;
379
	}
380
381
382
	/**
383
	 * Searches for items matching the given criteria.
384
	 *
385
	 * @param \Aimeos\MW\Criteria\Iface $search Search criteria
386
	 * @param string[] $ref List of domains to fetch list items and referenced items for
387
	 * @param int &$total Total number of items matched by the given criteria
388
	 * @param string $cfgPathSearch Configuration path to the search SQL statement
389
	 * @param string $cfgPathCount Configuration path to the count SQL statement
390
	 * @return \Aimeos\MShop\Product\Item\Iface[] List of product items
391
	 */
392
	protected function searchItemsIndexBase( \Aimeos\MW\Criteria\Iface $search,
393
		array $ref, int &$total = null, string $cfgPathSearch, string $cfgPathCount ) : \Aimeos\Map
394
	{
395
		$list = $ids = [];
396
		$context = $this->getContext();
397
398
		$dbm = $context->getDatabaseManager();
399
		$dbname = $this->getResourceName();
400
		$conn = $dbm->acquire( $dbname );
401
402
		try
403
		{
404
			$required = array( 'product' );
405
406
			/** mshop/index/manager/sitemode
407
			 * Mode how items from levels below or above in the site tree are handled
408
			 *
409
			 * By default, only items from the current site are fetched from the
410
			 * storage. If the ai-sites extension is installed, you can create a
411
			 * tree of sites. Then, this setting allows you to define for the
412
			 * whole index domain if items from parent sites are inherited,
413
			 * sites from child sites are aggregated or both.
414
			 *
415
			 * Available constants for the site mode are:
416
			 * * 0 = only items from the current site
417
			 * * 1 = inherit items from parent sites
418
			 * * 2 = aggregate items from child sites
419
			 * * 3 = inherit and aggregate items at the same time
420
			 *
421
			 * You also need to set the mode in the locale manager
422
			 * (mshop/locale/manager/standard/sitelevel) to one of the constants.
423
			 * If you set it to the same value, it will work as described but you
424
			 * can also use different modes. For example, if inheritance and
425
			 * aggregation is configured the locale manager but only inheritance
426
			 * in the domain manager because aggregating items makes no sense in
427
			 * this domain, then items wil be only inherited. Thus, you have full
428
			 * control over inheritance and aggregation in each domain.
429
			 *
430
			 * @param int Constant from Aimeos\MShop\Locale\Manager\Base class
431
			 * @category Developer
432
			 * @since 2018.01
433
			 * @see mshop/locale/manager/standard/sitelevel
434
			 */
435
			$level = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
436
			$level = $context->getConfig()->get( 'mshop/index/manager/sitemode', $level );
437
438
			$results = $this->searchItemsBase( $conn, $search, $cfgPathSearch, $cfgPathCount, $required, $total, $level );
439
440
			while( ( $row = $results->fetch() ) !== null ) {
441
				$ids[] = $row['id'];
442
			}
443
444
			$dbm->release( $conn, $dbname );
445
		}
446
		catch( \Exception $e )
447
		{
448
			$dbm->release( $conn, $dbname );
449
			throw $e;
450
		}
451
452
		$manager = \Aimeos\MShop::create( $context, 'product' );
453
		$prodSearch = $manager->filter();
454
		$prodSearch->setConditions( $prodSearch->compare( '==', 'product.id', $ids ) );
455
		$prodSearch->setSlice( 0, $search->getSliceSize() );
456
		$items = $manager->search( $prodSearch, $ref );
457
458
		foreach( $ids as $id )
459
		{
460
			if( isset( $items[$id] ) ) {
461
				$list[$id] = $items[$id];
462
			}
463
		}
464
465
		return map( $list );
466
	}
467
468
469
	/**
470
	 * Returns the sub-manager instances available for the manager
471
	 *
472
	 * @return array Associative list of the sub-domain as key and the manager object as value
473
	 */
474
	abstract protected function getSubManagers() : array;
475
}
476