Passed
Push — master ( ba6e3e...c663a5 )
by Aimeos
14:17
created

DBBase::createPropertyItem()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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