Passed
Push — master ( e71c55...7f377b )
by Aimeos
12:09 queued 06:04
created

DBBase::stock()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2015-2021
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 iterable $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( iterable $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
	 * Updates the rating of the item
143
	 *
144
	 * @param string $id ID of the item
145
	 * @param string $rating Decimal value of the rating
146
	 * @param int $ratings Total number of ratings for the item
147
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
148
	 */
149
	public function rate( string $id, string $rating, int $ratings ) : \Aimeos\MShop\Common\Manager\Iface
150
	{
151
		$this->manager->rate( $id, $rating, $ratings );
152
		return $this;
153
	}
154
155
156
	/**
157
	 * Rebuilds the customer index
158
	 *
159
	 * @param \Aimeos\MShop\Product\Item\Iface[] $items Associative list of product IDs and items values
160
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
161
	 */
162
	public function rebuild( iterable $items = [] ) : \Aimeos\MShop\Index\Manager\Iface
163
	{
164
		foreach( $this->getSubManagers() as $submanager ) {
165
			$submanager->rebuild( $items );
166
		}
167
168
		return $this;
169
	}
170
171
172
	/**
173
	 * Removes the products from the product index.
174
	 *
175
	 * @param array|string $ids Product ID or list of IDs
176
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
177
	 */
178
	 public function remove( $ids ) : \Aimeos\MShop\Index\Manager\Iface
179
	 {
180
		foreach( $this->getSubManagers() as $submanager ) {
181
			$submanager->remove( $ids );
182
		}
183
184
		return $this;
185
	}
186
187
188
	/**
189
	 * Adds or updates an item object or a list of them.
190
	 *
191
	 * @param \Aimeos\Map|\Aimeos\MShop\Common\Item\Iface[]|\Aimeos\MShop\Common\Item\Iface $items Item or list of items whose data should be saved
192
	 * @param bool $fetch True if the new ID should be returned in the item
193
	 * @return \Aimeos\Map|\Aimeos\MShop\Common\Item\Iface Saved item or items
194
	 */
195
	public function save( $items, bool $fetch = true )
196
	{
197
		$list = [];
198
199
		foreach( map( $this->manager->save( $items, true ) ) as $item ) {
200
			$list[$item->getId()] = $item;
201
		}
202
203
		$this->rebuild( $list );
204
		return $items;
205
	}
206
207
208
	/**
209
	 * Updates if the product is in stock
210
	 *
211
	 * @param string $id ID of the procuct item
212
	 * @param int $value "0" or "1" if product is in stock or not
213
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
214
	 */
215
	public function stock( string $id, int $value ) : \Aimeos\MShop\Common\Manager\Iface
216
	{
217
		$this->manager->stock( $id, $value );
218
		return $this;
219
	}
220
221
222
	/**
223
	 * Removes all entries not touched after the given timestamp
224
	 *
225
	 * @param string $timestamp Timestamp in ISO format (YYYY-MM-DD HH:mm:ss)
226
	 * @param string $path Configuration path to the SQL statement to execute
227
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
228
	 */
229
	protected function cleanupBase( string $timestamp, string $path ) : \Aimeos\MShop\Index\Manager\Iface
230
	{
231
		$context = $this->getContext();
232
		$siteid = $context->getLocale()->getSiteId();
233
234
235
		$this->begin();
236
237
		$dbm = $context->getDatabaseManager();
238
		$dbname = $this->getResourceName();
239
		$conn = $dbm->acquire( $dbname );
240
241
		try
242
		{
243
			$stmt = $this->getCachedStatement( $conn, $path );
244
245
			$stmt->bind( 1, $timestamp ); // ctime
246
			$stmt->bind( 2, $siteid );
247
248
			$stmt->execute()->finish();
249
250
			$dbm->release( $conn, $dbname );
251
		}
252
		catch( \Exception $e )
253
		{
254
			$dbm->release( $conn, $dbname );
255
			$this->rollback();
256
			throw $e;
257
		}
258
259
		$this->commit();
260
261
		foreach( $this->getSubManagers() as $submanager ) {
262
			$submanager->cleanup( $timestamp );
263
		}
264
265
		return $this;
266
	}
267
268
269
	/**
270
	 * Removes several items from the index
271
	 *
272
	 * @param string[] $ids List of product IDs
273
	 * @param string $path Configuration path to the SQL statement to execute
274
	 * @param bool $siteidcheck If siteid should be used in the statement
275
	 * @param string $name Name of the ID column
276
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
277
	 */
278
	protected function deleteItemsBase( $ids, string $path, bool $siteidcheck = true,
279
		string $name = 'prodid' ) : \Aimeos\MShop\Common\Manager\Iface
280
	{
281
		foreach( $this->getSubManagers() as $submanager ) {
282
			$submanager->delete( $ids );
283
		}
284
285
		return parent::deleteItemsBase( $ids, $path, $siteidcheck, $name );
286
	}
287
288
289
	/**
290
	 * Returns the product manager instance
291
	 *
292
	 * @return \Aimeos\MShop\Product\Manager\Iface Product manager object
293
	 */
294
	protected function getManager() : \Aimeos\MShop\Common\Manager\Iface
295
	{
296
		return $this->manager;
297
	}
298
299
300
	/**
301
	 * Returns the string replacements for the SQL statements
302
	 *
303
	 * @param \Aimeos\MW\Criteria\Iface $search Search critera object
304
	 * @param \Aimeos\MW\Criteria\Attribute\Iface[] $attributes Associative list of search keys and criteria attribute items
305
	 * @param \Aimeos\MW\Criteria\Plugin\Iface[] $plugins Associative list of item keys and criteria plugin objects
306
	 * @param string[] $joins Associative list of SQL joins
307
	 * @param \Aimeos\MW\Criteria\Attribute\Iface[] $columns Additional columns to retrieve values from
308
	 * @return array Array of keys, find and replace arrays
309
	 */
310
	protected function getSQLReplacements( \Aimeos\MW\Criteria\Iface $search, array $attributes, array $plugins,
311
		array $joins, array $columns = [] ) : array
312
	{
313
		$list = [];
314
		$types = $this->getSearchTypes( $attributes );
315
		$funcs = $this->getSearchFunctions( $attributes );
316
		$translations = $this->getSearchTranslations( $attributes );
317
318
		$colstring = '';
319
		foreach( $columns as $name => $entry ) {
320
			$colstring .= $entry->getInternalCode() . ', ';
321
		}
322
323
		$find = array( ':columns', ':joins', ':cond', ':start', ':size' );
324
		$replace = array(
325
			$colstring,
326
			implode( "\n", array_unique( $joins ) ),
327
			$search->getConditionSource( $types, $translations, $plugins, $funcs ),
328
			$search->getOffset(),
329
			$search->getLimit(),
330
		);
331
332
		if( empty( $search->getSortations() ) && ( $attribute = reset( $attributes ) ) !== false )
333
		{
334
			$search = ( clone $search )->setSortations( [$search->sort( '+', $attribute->getCode() )] );
335
		}
336
		elseif( !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
		$find[] = ':mincols';
350
		$replace[] = !empty( $list ) ? ', ' . implode( ', ', $list ) : '';
351
352
		$find[] = ':order';
353
		$replace[] = $search->getSortationSource( $types, $translations, $funcs );
354
355
		return [$find, $replace];
356
	}
357
358
359
	/**
360
	 * Optimizes the catalog customer index if necessary
361
	 *
362
	 * @param string $path Configuration path to the SQL statements to execute
363
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
364
	 */
365
	protected function optimizeBase( string $path ) : \Aimeos\MShop\Index\Manager\Iface
366
	{
367
		$context = $this->getContext();
368
369
		$dbm = $context->getDatabaseManager();
370
		$dbname = $this->getResourceName();
371
		$conn = $dbm->acquire( $dbname );
372
373
		try
374
		{
375
			foreach( (array) $this->getSqlConfig( $path ) as $sql ) {
376
				$conn->create( $sql )->execute()->finish();
377
			}
378
379
			$dbm->release( $conn, $dbname );
380
		}
381
		catch( \Exception $e )
382
		{
383
			$dbm->release( $conn, $dbname );
384
			throw $e;
385
		}
386
387
		foreach( $this->getSubManagers() as $submanager ) {
388
			$submanager->optimize();
389
		}
390
391
		return $this;
392
	}
393
394
395
	/**
396
	 * Searches for items matching the given criteria.
397
	 *
398
	 * @param \Aimeos\MW\Criteria\Iface $search Search criteria
399
	 * @param string[] $ref List of domains to fetch list items and referenced items for
400
	 * @param int &$total Total number of items matched by the given criteria
401
	 * @param string $cfgPathSearch Configuration path to the search SQL statement
402
	 * @param string $cfgPathCount Configuration path to the count SQL statement
403
	 * @return \Aimeos\MShop\Product\Item\Iface[] List of product items
404
	 */
405
	protected function searchItemsIndexBase( \Aimeos\MW\Criteria\Iface $search,
406
		array $ref, int &$total = null, string $cfgPathSearch, string $cfgPathCount ) : \Aimeos\Map
407
	{
408
		$list = $ids = [];
409
		$context = $this->getContext();
410
411
		$dbm = $context->getDatabaseManager();
412
		$dbname = $this->getResourceName();
413
		$conn = $dbm->acquire( $dbname );
414
415
		try
416
		{
417
			$required = array( 'product' );
418
419
			/** mshop/index/manager/sitemode
420
			 * Mode how items from levels below or above in the site tree are handled
421
			 *
422
			 * By default, only items from the current site are fetched from the
423
			 * storage. If the ai-sites extension is installed, you can create a
424
			 * tree of sites. Then, this setting allows you to define for the
425
			 * whole index domain if items from parent sites are inherited,
426
			 * sites from child sites are aggregated or both.
427
			 *
428
			 * Available constants for the site mode are:
429
			 * * 0 = only items from the current site
430
			 * * 1 = inherit items from parent sites
431
			 * * 2 = aggregate items from child sites
432
			 * * 3 = inherit and aggregate items at the same time
433
			 *
434
			 * You also need to set the mode in the locale manager
435
			 * (mshop/locale/manager/sitelevel) to one of the constants.
436
			 * If you set it to the same value, it will work as described but you
437
			 * can also use different modes. For example, if inheritance and
438
			 * aggregation is configured the locale manager but only inheritance
439
			 * in the domain manager because aggregating items makes no sense in
440
			 * this domain, then items wil be only inherited. Thus, you have full
441
			 * control over inheritance and aggregation in each domain.
442
			 *
443
			 * @param int Constant from Aimeos\MShop\Locale\Manager\Base class
444
			 * @category Developer
445
			 * @since 2018.01
446
			 * @see mshop/locale/manager/sitelevel
447
			 */
448
			$level = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
449
			$level = $context->getConfig()->get( 'mshop/index/manager/sitemode', $level );
450
451
			$results = $this->searchItemsBase( $conn, $search, $cfgPathSearch, $cfgPathCount, $required, $total, $level );
452
453
			while( ( $row = $results->fetch() ) !== null ) {
454
				$ids[] = $row['id'];
455
			}
456
457
			$dbm->release( $conn, $dbname );
458
		}
459
		catch( \Exception $e )
460
		{
461
			$dbm->release( $conn, $dbname );
462
			throw $e;
463
		}
464
465
		$manager = \Aimeos\MShop::create( $context, 'product' );
466
		$prodSearch = $manager->filter();
467
		$prodSearch->setConditions( $prodSearch->compare( '==', 'product.id', $ids ) );
468
		$prodSearch->slice( 0, $search->getLimit() );
469
		$items = $manager->search( $prodSearch, $ref );
470
471
		foreach( $ids as $id )
472
		{
473
			if( isset( $items[$id] ) ) {
474
				$list[$id] = $items[$id];
475
			}
476
		}
477
478
		return map( $list );
479
	}
480
481
482
	/**
483
	 * Returns the sub-manager instances available for the manager
484
	 *
485
	 * @return array Associative list of the sub-domain as key and the manager object as value
486
	 */
487
	abstract protected function getSubManagers() : array;
488
}
489