Passed
Push — master ( 3311dc...3c99d6 )
by Aimeos
05:26
created

lib/mshoplib/src/MShop/Common/Manager/Base.php (1 issue)

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-2018
7
 * @package MShop
8
 * @subpackage Common
9
 */
10
11
12
namespace Aimeos\MShop\Common\Manager;
13
14
15
/**
16
 * Provides common methods required by most of the manager classes.
17
 *
18
 * @package MShop
19
 * @subpackage Common
20
 */
21
abstract class Base extends \Aimeos\MW\Common\Manager\Base
22
{
23
	use \Aimeos\MShop\Common\Manager\Sub\Traits;
24
25
26
	private $context;
27
	private $object;
28
	private $resourceName;
29
	private $stmts = [];
30
31
32
	/**
33
	 * Initialization of class.
34
	 *
35
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context object
36
	 */
37
	public function __construct( \Aimeos\MShop\Context\Item\Iface $context )
38
	{
39
		$this->context = $context;
40
	}
41
42
43
	/**
44
	 * Catch unknown methods
45
	 *
46
	 * @param string $name Name of the method
47
	 * @param array $param List of method parameter
48
	 * @throws \Aimeos\MShop\Manager\Exception If method call failed
49
	 */
50
	public function __call( $name, array $param )
51
	{
52
		throw new \Aimeos\MShop\Exception( sprintf( 'Unable to call method "%1$s"', $name ) );
53
	}
54
55
56
	/**
57
	 * Removes old entries from the storage.
58
	 *
59
	 * @param string[] $siteids List of IDs for sites whose entries should be deleted
60
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
61
	 */
62
	public function cleanup( array $siteids )
63
	{
64
		return $this;
65
	}
66
67
68
	/**
69
	 * Creates a search critera object
70
	 *
71
	 * @param boolean $default Add default criteria (optional)
72
	 * @return \Aimeos\MW\Criteria\Iface New search criteria object
73
	 */
74
	public function createSearch( $default = false )
75
	{
76
		$db = $this->getResourceName();
77
		$config = $this->context->getConfig();
78
		$dbm = $this->context->getDatabaseManager();
79
80
		if( ( $adapter = $config->get( 'resource/' . $db . '/adapter' ) ) === null ) {
81
			$adapter = $config->get( 'resource/db/adapter' );
82
		}
83
84
		$conn = $dbm->acquire( $db );
85
86
		switch( $adapter )
87
		{
88
			case 'pgsql':
89
				$search = new \Aimeos\MW\Criteria\PgSQL( $conn ); break;
90
			default:
91
				$search = new \Aimeos\MW\Criteria\SQL( $conn ); break;
92
		}
93
94
		$dbm->release( $conn, $db );
95
96
		return $search;
97
	}
98
99
100
	/**
101
	 * Deletes an item from storage.
102
	 *
103
	 * @param string $itemId Unique ID of the item in the storage
104
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
105
	 */
106
	public function deleteItem( $itemId )
107
	{
108
		return $this->getObject()->deleteItems( [$itemId] );
109
	}
110
111
112
	/**
113
	 * Starts a database transaction on the connection identified by the given name
114
	 *
115
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
116
	 */
117
	public function begin()
118
	{
119
		return $this->beginTransation( $this->getResourceName() );
120
	}
121
122
123
	/**
124
	 * Commits the running database transaction on the connection identified by the given name
125
	 *
126
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
127
	 */
128
	public function commit()
129
	{
130
		return $this->commitTransaction( $this->getResourceName() );
131
	}
132
133
134
	/**
135
	 * Rolls back the running database transaction on the connection identified by the given name
136
	 *
137
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
138
	 */
139
	public function rollback()
140
	{
141
		return $this->rollbackTransaction( $this->getResourceName() );
142
	}
143
144
145
	/**
146
	 * Adds or updates a list of item objects.
147
	 *
148
	 * @param \Aimeos\MShop\Common\Item\Iface[] $items List of item object whose data should be saved
149
	 * @param boolean $fetch True if the new ID should be returned in the item
150
	 * @return \Aimeos\MShop\Common\Item\Iface[] Saved item objects
151
	 */
152
	public function saveItems( array $items, $fetch = true )
153
	{
154
		foreach( $items as $id => $item ) {
155
			$items[$id] = $this->getObject()->saveItem( $item, $fetch );
156
		}
157
158
		return $items;
159
	}
160
161
162
	/**
163
	 * Injects the reference of the outmost object
164
	 *
165
	 * @param \Aimeos\MShop\Common\Manager\Iface $object Reference to the outmost manager or decorator
166
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
167
	 */
168
	public function setObject( \Aimeos\MShop\Common\Manager\Iface $object )
169
	{
170
		$this->object = $object;
171
		return $this;
172
	}
173
174
175
	/**
176
	 * Counts the number products that are available for the values of the given key.
177
	 *
178
	 * @param \Aimeos\MW\Criteria\Iface $search Search criteria
179
	 * @param string $key Search key for aggregating the key column
180
	 * @param string $cfgPath Configuration key for the SQL statement
181
	 * @param string[] $required List of domain/sub-domain names like "catalog.index" that must be additionally joined
182
	 * @param string|null $value Search key for aggregating the value column
183
	 * @return integer[] List of ID values as key and the number of counted products as value
184
	 * @todo 2018.01 Reorder Parameter list
185
	 */
186
	protected function aggregateBase( \Aimeos\MW\Criteria\Iface $search, $key, $cfgPath, $required = [], $value = null )
187
	{
188
		$list = [];
189
		$context = $this->getContext();
190
191
		$dbname = $this->getResourceName();
192
		$dbm = $context->getDatabaseManager();
193
		$conn = $dbm->acquire( $dbname );
194
195
		try
196
		{
197
			$search = clone $search;
198
			$attrList = $this->getObject()->getSearchAttributes();
199
200
			if( $value === null && ( $value = key( $attrList ) ) === null ) {
201
				throw new \Aimeos\MShop\Exception( sprintf( 'No search keys available' ) );
202
			}
203
204
			if( !isset( $attrList[$key] ) ) {
205
				throw new \Aimeos\MShop\Exception( sprintf( 'Unknown search key "%1$s"', $key ) );
206
			}
207
208
			if( $value !== null && !isset( $attrList[$value] ) ) {
209
				throw new \Aimeos\MShop\Exception( sprintf( 'Unknown search key "%1$s"', $value ) );
210
			}
211
212
			/** @todo Required to get the joins, but there should be a better way */
213
			$expr = array(
214
				$search->getConditions(),
215
				$search->compare( '!=', $key, null ),
216
				$search->compare( '!=', $value, null ),
217
			);
218
			$search->setConditions( $search->combine( '&&', $expr ) );
219
220
			$level = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
221
			$total = null;
222
223
			$sql = $this->getSqlConfig( $cfgPath );
224
			$sql = str_replace( ':key', $attrList[$key]->getInternalCode(), $sql );
225
			$sql = str_replace( ':val', $attrList[$value]->getInternalCode(), $sql );
226
227
			$results = $this->searchItemsBase( $conn, $search, $sql, '', $required, $total, $level );
228
229
			while( ( $row = $results->fetch() ) !== false ) {
230
				$list[$row['key']] = $row['count'];
231
			}
232
233
			$dbm->release( $conn, $dbname );
234
		}
235
		catch( \Exception $e )
236
		{
237
			$dbm->release( $conn, $dbname );
238
			throw $e;
239
		}
240
241
		return $list;
242
	}
243
244
245
	/**
246
	 * Returns the newly created ID for the last record which was inserted.
247
	 *
248
	 * @param \Aimeos\MW\DB\Connection\Iface $conn Database connection used to insert the new record
249
	 * @param string $cfgpath Configuration path to the SQL statement for retrieving the new ID of the last inserted record
250
	 * @return string ID of the last record that was inserted by using the given connection
251
	 * @throws \Aimeos\MShop\Common\Exception if there's no ID of the last record available
252
	 */
253
	protected function newId( \Aimeos\MW\DB\Connection\Iface $conn, $cfgpath )
254
	{
255
		$result = $conn->create( $this->getSqlConfig( $cfgpath ) )->execute();
256
257
		if( ( $row = $result->fetch( \Aimeos\MW\DB\Result\Base::FETCH_NUM ) ) === false ) {
258
			throw new \Aimeos\MShop\Exception( sprintf( 'ID of last inserted database record not available' ) );
259
		}
260
		$result->finish();
261
262
		return $row[0];
263
	}
264
265
266
	/**
267
	 * Removes old entries from the storage.
268
	 *
269
	 * @param string[] $siteids List of IDs for sites whose entries should be deleted
270
	 * @param string $cfgpath Configuration key to the cleanup statement
271
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
272
	 */
273
	protected function cleanupBase( array $siteids, $cfgpath )
274
	{
275
		$dbm = $this->context->getDatabaseManager();
276
		$dbname = $this->getResourceName();
277
		$conn = $dbm->acquire( $dbname );
278
279
		try
280
		{
281
			$sql = $this->getSqlConfig( $cfgpath );
282
			$sql = str_replace( ':cond', '1=1', $sql );
283
284
			$stmt = $conn->create( $sql );
285
286
			foreach( $siteids as $siteid )
287
			{
288
				$stmt->bind( 1, $siteid, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
289
				$stmt->execute()->finish();
290
			}
291
292
			$dbm->release( $conn, $dbname );
293
		}
294
		catch( \Exception $e )
295
		{
296
			$dbm->release( $conn, $dbname );
297
			throw $e;
298
		}
299
300
		return $this;
301
	}
302
303
304
	/**
305
	 * Sets the base criteria "status".
306
	 * (setConditions overwrites the base criteria)
307
	 *
308
	 * @param string $domain Name of the domain/sub-domain like "product" or "product.list"
309
	 * @return \Aimeos\MW\Criteria\Iface Search critery object
310
	 */
311
	protected function createSearchBase( $domain )
312
	{
313
		$object = $this->createSearch();
314
		$object->setConditions( $object->compare( '==', $domain . '.status', 1 ) );
315
316
		return $object;
317
	}
318
319
320
	/**
321
	 * Returns the context object.
322
	 *
323
	 * @return \Aimeos\MShop\Context\Item\Iface Context object
324
	 */
325
	protected function getContext()
326
	{
327
		return $this->context;
328
	}
329
330
331
	/**
332
	 * Returns the outmost decorator of the decorator stack
333
	 *
334
	 * @return \Aimeos\MShop\Common\Manager\Iface Outmost decorator object
335
	 */
336
	protected function getObject()
337
	{
338
		if( $this->object !== null ) {
339
			return $this->object;
340
		}
341
342
		return $this;
343
	}
344
345
346
	/**
347
	 * Returns the search attribute objects used for searching.
348
	 *
349
	 * @param array $list Associative list of search keys and the lists of search definitions
350
	 * @param string $path Configuration path to the sub-domains for fetching the search definitions
351
	 * @param string[] $default List of sub-domains if no others are configured
352
	 * @param boolean $withsub True to include search definitions of sub-domains, false if not
353
	 * @return \Aimeos\MW\Criteria\Attribute\Iface[] Associative list of search keys and criteria attribute items as values
354
	 * @since 2014.09
355
	 */
356
	protected function getSearchAttributesBase( array $list, $path, array $default, $withsub )
357
	{
358
		$attr = [];
359
360
		foreach( $list as $key => $fields ) {
361
			$attr[$key] = new \Aimeos\MW\Criteria\Attribute\Standard( $fields );
362
		}
363
364
		if( $withsub === true )
365
		{
366
			$domains = $this->context->getConfig()->get( $path, $default );
367
368
			foreach( $domains as $domain ) {
369
				$attr += $this->getObject()->getSubManager( $domain )->getSearchAttributes( true );
370
			}
371
		}
372
373
		return $attr;
374
	}
375
376
377
	/**
378
	 * Returns the search results for the given SQL statement.
379
	 *
380
	 * @param \Aimeos\MW\DB\Connection\Iface $conn Database connection
381
	 * @param string $sql SQL statement
382
	 * @return \Aimeos\MW\DB\Result\Iface Search result object
383
	 */
384
	protected function getSearchResults( \Aimeos\MW\DB\Connection\Iface $conn, $sql )
385
	{
386
		$time = microtime( true );
387
388
		$stmt = $conn->create( $sql );
389
		$result = $stmt->execute();
390
391
		$time = ( microtime( true ) - $time ) * 1000;
392
		$msg = [
393
			'time' => $time,
394
			'class' => get_class( $this ),
395
			'stmt' => (string) $stmt,
396
		];
397
398
		if( $time > 1000.0 )
399
		{
400
			$e = new \Exception();
401
			$msg['trace'] = $e->getTraceAsString();
402
			$level = \Aimeos\MW\Logger\Base::NOTICE;
403
		}
404
		else
405
		{
406
			$level = \Aimeos\MW\Logger\Base::DEBUG;
407
		}
408
409
		$this->context->getLogger()->log( $msg, $level, 'core/sql' );
410
411
		return $result;
412
	}
413
414
415
	/**
416
	 * Returns the site IDs for the given site level constant.
417
	 *
418
	 * @param integer $sitelevel Site level constant from \Aimeos\MShop\Locale\Manager\Base
419
	 * @return string[] List of site IDs
420
	 */
421
	private function getSiteIds( $sitelevel )
422
	{
423
		$locale = $this->context->getLocale();
424
		$siteIds = array( $locale->getSiteId() );
425
426
		if( $sitelevel & \Aimeos\MShop\Locale\Manager\Base::SITE_PATH ) {
427
			$siteIds = array_merge( $siteIds, $locale->getSitePath() );
428
		}
429
430
		if( $sitelevel & \Aimeos\MShop\Locale\Manager\Base::SITE_SUBTREE ) {
431
			$siteIds = array_merge( $siteIds, $locale->getSiteSubTree() );
432
		}
433
434
		$siteIds = array_unique( $siteIds );
435
436
		return $siteIds;
437
	}
438
439
440
	/**
441
	 * Returns the SQL statement for the given config path
442
	 *
443
	 * If available, the database specific SQL statement is returned, otherwise
444
	 * the ANSI SQL statement. The database type is determined via the resource
445
	 * adapter.
446
	 *
447
	 * @param string $path Configuration path to the SQL statement
448
	 * @return string ANSI or database specific SQL statement
449
	 */
450
	protected function getSqlConfig( $path )
451
	{
452
		$config = $this->getContext()->getConfig();
453
		$adapter = $config->get( 'resource/' . $this->getResourceName() . '/adapter' );
454
455
		return $config->get( $path . '/' . $adapter, $config->get( $path . '/ansi', $path ) );
456
	}
457
458
459
	/**
460
	 * Returns the item for the given search key/value pairs.
461
	 *
462
	 * @param array $pairs Search key/value pairs for the item
463
	 * @param string[] $ref List of domains whose items should be fetched too
464
	 * @param boolean $default True to add default criteria
465
	 * @return \Aimeos\MShop\Common\Item\Iface Requested item
466
	 * @throws \Aimeos\MShop\Exception if no item with the given ID found
467
	 */
468
	protected function findItemBase( array $pairs, array $ref, $default )
469
	{
470
		$expr = [];
471
		$criteria = $this->getObject()->createSearch( $default )->setSlice( 0, 1 );
472
473
		foreach( $pairs as $key => $value )
474
		{
475
			if( $value === null ) {
476
				throw new \Aimeos\MShop\Exception( sprintf( 'Required value for "%1$s" is missing', $key ) );
477
			}
478
			$expr[] = $criteria->compare( '==', $key, $value );
479
		}
480
481
		$criteria->setConditions( $criteria->combine( '&&', $expr ) );
482
		$items = $this->getObject()->searchItems( $criteria, $ref );
483
484
		if( ( $item = reset( $items ) ) === false ) {
485
			throw new \Aimeos\MShop\Exception( sprintf( 'No item found for conditions: %1$s', print_r( $pairs, true ) ) );
486
		}
487
488
		return $item;
489
	}
490
491
492
	/**
493
	 * Returns the cached statement for the given key or creates a new prepared statement.
494
	 * If no SQL string is given, the key is used to retrieve the SQL string from the configuration.
495
	 *
496
	 * @param \Aimeos\MW\DB\Connection\Iface $conn Database connection
497
	 * @param string $cfgkey Unique key for the SQL
498
	 * @param string|null $sql SQL string if it shouldn't be retrieved from the configuration
499
	 * @return \Aimeos\MW\DB\Statement\Iface Database statement object
500
	 */
501
	protected function getCachedStatement( \Aimeos\MW\DB\Connection\Iface $conn, $cfgkey, $sql = null )
502
	{
503
		if( !isset( $this->stmts['stmt'][$cfgkey] ) || !isset( $this->stmts['conn'][$cfgkey] )
504
				|| $conn !== $this->stmts['conn'][$cfgkey]
505
		) {
506
			if( $sql === null ) {
507
				$sql = $this->getSqlConfig( $cfgkey );
508
			}
509
510
			$this->stmts['stmt'][$cfgkey] = $conn->create( $sql );
511
			$this->stmts['conn'][$cfgkey] = $conn;
512
		}
513
514
		return $this->stmts['stmt'][$cfgkey];
515
	}
516
517
518
	/**
519
	 * Returns the item for the given search key and ID.
520
	 *
521
	 * @param string $key Search key for the requested ID
522
	 * @param string $id Unique ID to search for
523
	 * @param string[] $ref List of domains whose items should be fetched too
524
	 * @param boolean $default True to add default criteria
525
	 * @return \Aimeos\MShop\Common\Item\Iface Requested item
526
	 * @throws \Aimeos\MShop\Exception if no item with the given ID found
527
	 */
528
	protected function getItemBase( $key, $id, array $ref, $default )
529
	{
530
		$criteria = $this->getObject()->createSearch( $default )->setSlice( 0, 1 );
531
		$expr = [
532
			$criteria->compare( '==', $key, $id ),
533
			$criteria->getConditions()
534
		];
535
		$criteria->setConditions( $criteria->combine( '&&', $expr ) );
536
		$items = $this->getObject()->searchItems( $criteria, $ref );
537
538
		if( ( $item = reset( $items ) ) === false ) {
539
			throw new \Aimeos\MShop\Exception( sprintf( 'Item with ID "%2$s" in "%1$s" not found', $key, $id ) );
540
		}
541
542
		return $item;
543
	}
544
545
546
	/**
547
	 * Returns the SQL strings for joining dependent tables.
548
	 *
549
	 * @param \Aimeos\MW\Criteria\Attribute\Iface[] $attributes List of criteria attribute items
550
	 * @param string $prefix Search key prefix
551
	 * @return array List of JOIN SQL strings
552
	 */
553
	private function getJoins( array $attributes, $prefix )
554
	{
555
		$iface = \Aimeos\MW\Criteria\Attribute\Iface::class;
556
		$sep = $this->getKeySeparator();
557
		$name = $prefix . $sep . 'id';
558
559
		if( isset( $attributes[$name] ) && $attributes[$name] instanceof $iface ) {
560
			return $attributes[$name]->getInternalDeps();
561
		}
562
		else if( isset( $attributes['id'] ) && $attributes['id'] instanceof $iface ) {
563
			return $attributes['id']->getInternalDeps();
564
		}
565
566
		return [];
567
	}
568
569
570
	/**
571
	 * Returns the available manager types
572
	 *
573
	 * @param string $type Main manager type
574
	 * @param string $path Configuration path to the sub-domains
575
	 * @param string[] $default List of sub-domains if no others are configured
576
	 * @param boolean $withsub Return also the resource type of sub-managers if true
577
	 * @return string[] Type of the manager and submanagers, subtypes are separated by slashes
578
	 */
579
	protected function getResourceTypeBase( $type, $path, array $default, $withsub )
580
	{
581
		$list = array( $type );
582
583
		foreach( $this->context->getConfig()->get( $path, $default ) as $domain ) {
584
			$list = array_merge( $list, $this->getObject()->getSubManager( $domain )->getResourceType( $withsub ) );
585
		}
586
587
		return $list;
588
	}
589
590
591
	/**
592
	 * Returns the name of the resource or of the default resource.
593
	 *
594
	 * @return string Name of the resource
595
	 */
596
	protected function getResourceName()
597
	{
598
		if( $this->resourceName === null ) {
599
			$this->resourceName = $this->context->getConfig()->get( 'resource/default', 'db' );
600
		}
601
602
		return $this->resourceName;
603
	}
604
605
606
	/**
607
	 * Sets the name of the database resource that should be used.
608
	 *
609
	 * @param string $name Name of the resource
610
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
611
	 */
612
	protected function setResourceName( $name )
613
	{
614
		$config = $this->context->getConfig();
615
616
		if( $config->get( 'resource/' . $name ) === null ) {
617
			$this->resourceName = $config->get( 'resource/default', 'db' );
618
		} else {
619
			$this->resourceName = $name;
620
		}
621
622
		return $this;
623
	}
624
625
626
	/**
627
	 * Replaces the given marker with an expression
628
	 *
629
	 * @param string $column Name (including alias) of the column
630
	 * @param mixed $value Value used in the expression
631
	 * @param string $op Operator used in the expression
632
	 * @param integer $type Type constant from \Aimeos\MW\DB\Statement\Base class
633
	 * @return string Created expression
634
	 */
635
	protected function toExpression( $column, $value, $op = '==', $type = \Aimeos\MW\DB\Statement\Base::PARAM_STR )
636
	{
637
		$types = ['marker' => $type];
638
		$translations = ['marker' => $column];
639
		$value = ( is_array( $value ) ? array_unique( $value ) : $value );
640
641
		return $this->createSearch()->compare( $op, 'marker', $value )->toSource( $types, $translations );
642
	}
643
644
645
	/**
646
	 * Replaces ":site" marker in a search config item array.
647
	 *
648
	 * @param array &$searchAttr Single search config definition including the "internalcode" key
649
	 * @param string $column Name (including alias) of the column containing the site ID in the storage
650
	 * @param string|string[] $value Site ID or list of site IDs
651
	 * @param string $marker Marker to replace
652
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
653
	 * @deprecated 2020.01 Use toExpression() instead
654
	 */
655
	protected function replaceSiteMarker( &$searchAttr, $column, $value, $marker = ':site' )
656
	{
657
		$types = array( 'siteid' => \Aimeos\MW\DB\Statement\Base::PARAM_INT );
658
		$translations = array( 'siteid' => $column );
659
		$conn = new \Aimeos\MW\DB\Connection\None();
660
661
		$search = new \Aimeos\MW\Criteria\SQL( $conn );
662
663
		$expr = $search->compare( '==', 'siteid', ( is_array( $value ) ? array_unique( $value ) : $value ) );
664
		$string = $expr->toSource( $types, $translations );
665
666
		$searchAttr['internalcode'] = str_replace( $marker, $string, $searchAttr['internalcode'] );
667
668
		return $this;
669
	}
670
671
672
	/**
673
	 * Returns the site coditions for the search request
674
	 *
675
	 * @param \Aimeos\MW\Criteria\Iface $search Search criteria object
676
	 * @param string[] $keys Sorted list of criteria keys
677
	 * @param \Aimeos\MW\Criteria\Attribute\Iface[] $attributes Associative list of search keys and criteria attribute items as values
678
	 * @param string[] $siteIds List of site IDs that should be used for searching
679
	 * @return \Aimeos\MW\Criteria\Expression\Iface[] List of search conditions
680
	 * @since 2015.01
681
	 */
682
	protected function getSearchSiteConditions( \Aimeos\MW\Criteria\Iface $search, array $keys, array $attributes, array $siteIds )
683
	{
684
		/** mshop/common/manager/sitecheck
685
		 * Enables or disables using the site IDs in search queries
686
		 *
687
		 * For market places, products of different shop owners managing their
688
		 * own sites should be shown in the frontend. By default, only the items
689
		 * from the current site are displayed. Setting this option to false
690
		 * disables the restriction to the current site and shows all products
691
		 * from all sites. This does also apply to all other records from
692
		 * different domains than "product".
693
		 *
694
		 * This option is most effective if it's only set for the shop frontend,
695
		 * so the shop owners will only see and manager their own products in
696
		 * the administration interface.
697
		 *
698
		 * @param boolean True to resrict items to the current site, false to show item form all sites
699
		 * @since 2016.10
700
		 * @category Developer
701
		 */
702
		if( $this->context->getConfig()->get( 'mshop/common/manager/sitecheck', true ) == false ) {
703
			return [];
704
		}
705
706
		$cond = [];
707
		$sep = $this->getKeySeparator();
708
709
		foreach( $keys as $key )
710
		{
711
			$name = $key . $sep . 'siteid';
712
713
			if( isset( $attributes[$name] ) ) {
714
				$cond[] = $search->compare( '==', $name, $siteIds );
715
			}
716
		}
717
718
		return $cond;
719
	}
720
721
722
	/**
723
	 * Returns the string replacements for the SQL statements
724
	 *
725
	 * @param \Aimeos\MW\Criteria\Iface $search Search critera object
726
	 * @param \Aimeos\MW\Criteria\Attribute\Iface[] $attributes Associative list of search keys and criteria attribute items as values
727
	 * @param \Aimeos\MW\Criteria\Plugin\Iface[] $plugins Associative list of search keys and criteria plugin items as values
728
	 * @param string[] $joins Associative list of SQL joins
729
	 * @return array Array of keys, find and replace arrays
730
	 */
731
	protected function getSQLReplacements( \Aimeos\MW\Criteria\Iface $search, array $attributes, array $plugins, array $joins )
0 ignored issues
show
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...
732
	{
733
		$types = $this->getSearchTypes( $attributes );
734
		$funcs = $this->getSearchFunctions( $attributes );
735
		$translations = $this->getSearchTranslations( $attributes );
736
737
		$keys = [];
738
		$find = array( ':joins', ':cond', ':start', ':size' );
739
		$replace = array(
740
			implode( "\n", array_unique( $joins ) ),
741
			$search->getConditionSource( $types, $translations, $plugins, $funcs ),
742
			$search->getSliceStart(),
743
			$search->getSliceSize(),
744
		);
745
746
		if( count( $search->getSortations() ) > 0 )
747
		{
748
			$keys[] = 'orderby';
749
			$find[] = ':order';
750
			$replace[] = $search->getSortationSource( $types, $translations, $funcs );
751
752
			$keys[] = 'columns';
753
			$find[] = ':columns';
754
			$replace[] = implode( ', ', $search->translate( $search->getSortations(), $translations ) );
755
		}
756
757
		return [$keys, $find, $replace];
758
	}
759
760
761
	/**
762
	 * Returns the search result of the statement combined with the given criteria.
763
	 *
764
	 * @param \Aimeos\MW\DB\Connection\Iface $conn Database connection
765
	 * @param \Aimeos\MW\Criteria\Iface $search Search criteria object
766
	 * @param string $cfgPathSearch Path to SQL statement in configuration for searching
767
	 * @param string $cfgPathCount Path to SQL statement in configuration for counting
768
	 * @param string[] $required Additional search keys to add conditions for even if no conditions are available
769
	 * @param integer|null $total Contains the number of all records matching the criteria if not null
770
	 * @param integer $sitelevel Constant from \Aimeos\MShop\Locale\Manager\Base for defining which site IDs should be used for searching
771
	 * @param \Aimeos\MW\Criteria\Plugin\Iface[] $plugins Associative list of search keys and criteria plugin items as values
772
	 * @return \Aimeos\MW\DB\Result\Iface SQL result object for accessing the found records
773
	 * @throws \Aimeos\MShop\Exception if no number of all matching records is available
774
	 */
775
	protected function searchItemsBase( \Aimeos\MW\DB\Connection\Iface $conn, \Aimeos\MW\Criteria\Iface $search,
776
		$cfgPathSearch, $cfgPathCount, array $required, &$total = null,
777
		$sitelevel = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL, array $plugins = [] )
778
	{
779
		$joins = [];
780
		$conditions = $search->getConditions();
781
		$siteIds = $this->getSiteIds( $sitelevel );
782
		$attributes = $this->getObject()->getSearchAttributes();
783
		$keys = $this->getCriteriaKeyList( $search, $required );
784
785
		$basekey = array_shift( $required );
786
787
		foreach( $keys as $key )
788
		{
789
			if( $key !== $basekey ) {
790
				$joins = array_merge( $joins, $this->getJoins( $attributes, $key ) );
791
			}
792
		}
793
794
		$cond = $this->getSearchSiteConditions( $search, $keys, $attributes, $siteIds );
795
796
		if( $conditions !== null ) {
797
			$cond[] = $conditions;
798
		}
799
800
		$search = clone $search;
801
		$search->setConditions( $search->combine( '&&', $cond ) );
802
803
		list( $keys, $find, $replace ) = $this->getSQLReplacements( $search, $attributes, $plugins, $joins );
804
805
		if( $total !== null )
806
		{
807
			$sql = new \Aimeos\MW\Template\SQL( $this->getSqlConfig( $cfgPathCount ) );
808
			$sql->replace( $find, $replace )->enable( $keys );
809
810
			$result = $this->getSearchResults( $conn, $sql->str() );
811
			$row = $result->fetch();
812
			$result->finish();
813
814
			if( $row === false ) {
815
				throw new \Aimeos\MShop\Exception( sprintf( 'Total results value not found' ) );
816
			}
817
818
			$total = (int) $row['count'];
819
		}
820
821
822
		$sql = new \Aimeos\MW\Template\SQL( $this->getSqlConfig( $cfgPathSearch ) );
823
		$sql->replace( $find, $replace )->enable( $keys );
824
825
		return $this->getSearchResults( $conn, $sql->str() );
826
	}
827
828
829
	/**
830
	 * Deletes items specified by its IDs.
831
	 *
832
	 * @param string[] $ids List of IDs
833
	 * @param string $cfgpath Configuration path to the SQL statement
834
	 * @param boolean $siteidcheck If siteid should be used in the statement
835
	 * @param string $name Name of the ID column
836
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
837
	 */
838
	protected function deleteItemsBase( array $ids, $cfgpath, $siteidcheck = true, $name = 'id' )
839
	{
840
		if( empty( $ids ) ) { return; }
841
842
		$context = $this->getContext();
843
		$dbname = $this->getResourceName();
844
845
		$search = $this->getObject()->createSearch();
846
		$search->setConditions( $search->compare( '==', $name, $ids ) );
847
848
		$types = array( $name => \Aimeos\MW\DB\Statement\Base::PARAM_STR );
849
		$translations = array( $name => '"' . $name . '"' );
850
851
		$cond = $search->getConditionSource( $types, $translations );
852
		$sql = str_replace( ':cond', $cond, $this->getSqlConfig( $cfgpath ) );
853
854
		$dbm = $context->getDatabaseManager();
855
		$conn = $dbm->acquire( $dbname );
856
857
		try
858
		{
859
			$stmt = $conn->create( $sql );
860
861
			if( $siteidcheck ) {
862
				$stmt->bind( 1, $context->getLocale()->getSiteId(), \Aimeos\MW\DB\Statement\Base::PARAM_INT );
863
			}
864
865
			$stmt->execute()->finish();
866
867
			$dbm->release( $conn, $dbname );
868
		}
869
		catch( \Exception $e )
870
		{
871
			$dbm->release( $conn, $dbname );
872
			throw $e;
873
		}
874
875
		return $this;
876
	}
877
878
879
	/**
880
	 * Starts a database transaction on the connection identified by the given name.
881
	 *
882
	 * @param string $dbname Name of the database settings in the resource configuration
883
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
884
	 */
885
	protected function beginTransation( $dbname = 'db' )
886
	{
887
		$dbm = $this->context->getDatabaseManager();
888
889
		$conn = $dbm->acquire( $dbname );
890
		$conn->begin();
891
		$dbm->release( $conn, $dbname );
892
893
		return $this;
894
	}
895
896
897
	/**
898
	 * Commits the running database transaction on the connection identified by the given name.
899
	 *
900
	 * @param string $dbname Name of the database settings in the resource configuration
901
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
902
	 */
903
	protected function commitTransaction( $dbname = 'db' )
904
	{
905
		$dbm = $this->context->getDatabaseManager();
906
907
		$conn = $dbm->acquire( $dbname );
908
		$conn->commit();
909
		$dbm->release( $conn, $dbname );
910
911
		return $this;
912
	}
913
914
915
	/**
916
	 * Rolls back the running database transaction on the connection identified by the given name.
917
	 *
918
	 * @param string $dbname Name of the database settings in the resource configuration
919
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
920
	 */
921
	protected function rollbackTransaction( $dbname = 'db' )
922
	{
923
		$dbm = $this->context->getDatabaseManager();
924
925
		$conn = $dbm->acquire( $dbname );
926
		$conn->rollback();
927
		$dbm->release( $conn, $dbname );
928
929
		return $this;
930
	}
931
}
932