Passed
Push — master ( fa68de...8ca22d )
by Aimeos
05:33
created

Standard::createTransaction()   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
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
/**
4
 * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0
5
 * @copyright Metaways Infosystems GmbH, 2011
6
 * @copyright Aimeos (aimeos.org), 2015-2022
7
 * @package MShop
8
 * @subpackage Order
9
 */
10
11
12
namespace Aimeos\MShop\Order\Manager\Base\Service;
13
14
15
/**
16
 * Default Manager Order service
17
 *
18
 * @package MShop
19
 * @subpackage Order
20
 */
21
class Standard
22
	extends \Aimeos\MShop\Common\Manager\Base
23
	implements \Aimeos\MShop\Order\Manager\Base\Service\Iface, \Aimeos\MShop\Common\Manager\Factory\Iface
24
{
25
	private $searchConfig = array(
26
		'order.base.service.id' => array(
27
			'code' => 'order.base.service.id',
28
			'internalcode' => 'mordbase."id"',
29
			'internaldeps' => array( 'LEFT JOIN "mshop_order_base_service" AS mordbase ON ( mordba."id" = mordbase."baseid" )' ),
30
			'label' => 'Service ID',
31
			'type' => 'integer',
32
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_INT,
33
			'public' => false,
34
		),
35
		'order.base.service.siteid' => array(
36
			'code' => 'order.base.service.siteid',
37
			'internalcode' => 'mordbase."siteid"',
38
			'label' => 'Service site ID',
39
			'type' => 'string',
40
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
41
			'public' => false,
42
		),
43
		'order.base.service.baseid' => array(
44
			'code' => 'order.base.service.baseid',
45
			'internalcode' => 'mordbase."baseid"',
46
			'label' => 'Order ID',
47
			'type' => 'integer',
48
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_INT,
49
			'public' => false,
50
		),
51
		'order.base.service.serviceid' => array(
52
			'code' => 'order.base.service.serviceid',
53
			'internalcode' => 'mordbase."servid"',
54
			'label' => 'Service original service ID',
55
			'type' => 'string',
56
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
57
			'public' => false,
58
		),
59
		'order.base.service.name' => array(
60
			'code' => 'order.base.service.name',
61
			'internalcode' => 'mordbase."name"',
62
			'label' => 'Service name',
63
			'type' => 'string',
64
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
65
		),
66
		'order.base.service.code' => array(
67
			'code' => 'order.base.service.code',
68
			'internalcode' => 'mordbase."code"',
69
			'label' => 'Service code',
70
			'type' => 'string',
71
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
72
		),
73
		'order.base.service.type' => array(
74
			'code' => 'order.base.service.type',
75
			'internalcode' => 'mordbase."type"',
76
			'label' => 'Service type',
77
			'type' => 'string',
78
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
79
		),
80
		'order.base.service.currencyid' => array(
81
			'code' => 'order.base.service.currencyid',
82
			'internalcode' => 'mordbase."currencyid"',
83
			'label' => 'Service currencyid code',
84
			'type' => 'string',
85
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
86
		),
87
		'order.base.service.price' => array(
88
			'code' => 'order.base.service.price',
89
			'internalcode' => 'mordbase."price"',
90
			'label' => 'Service price',
91
			'type' => 'decimal',
92
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
93
		),
94
		'order.base.service.costs' => array(
95
			'code' => 'order.base.service.costs',
96
			'internalcode' => 'mordbase."costs"',
97
			'label' => 'Service shipping',
98
			'type' => 'decimal',
99
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
100
		),
101
		'order.base.service.rebate' => array(
102
			'code' => 'order.base.service.rebate',
103
			'internalcode' => 'mordbase."rebate"',
104
			'label' => 'Service rebate',
105
			'type' => 'decimal',
106
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
107
		),
108
		'order.base.service.taxrates' => array(
109
			'code' => 'order.base.service.taxrates',
110
			'internalcode' => 'mordbase."taxrate"',
111
			'label' => 'Service taxrates',
112
			'type' => 'decimal',
113
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
114
		),
115
		'order.base.service.taxvalue' => array(
116
			'code' => 'order.base.service.taxvalue',
117
			'internalcode' => 'mordbase."tax"',
118
			'label' => 'Service tax value',
119
			'type' => 'decimal',
120
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
121
		),
122
		'order.base.service.taxflag' => array(
123
			'code' => 'order.base.service.taxflag',
124
			'internalcode' => 'mordbase."taxflag"',
125
			'label' => 'Service tax flag (0=net, 1=gross)',
126
			'type' => 'integer',
127
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_INT,
128
		),
129
		'order.base.service.mediaurl' => array(
130
			'code' => 'order.base.service.mediaurl',
131
			'internalcode' => 'mordbase."mediaurl"',
132
			'label' => 'Service media url',
133
			'type' => 'string',
134
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
135
			'public' => false,
136
		),
137
		'order.base.service.position' => array(
138
			'code' => 'order.base.service.position',
139
			'internalcode' => 'mordbase."pos"',
140
			'label' => 'Service position',
141
			'type' => 'integer',
142
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_INT,
143
			'public' => false,
144
		),
145
		'order.base.service.ctime' => array(
146
			'code' => 'order.base.service.ctime',
147
			'internalcode' => 'mordbase."ctime"',
148
			'label' => 'Service create date/time',
149
			'type' => 'datetime',
150
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
151
			'public' => false,
152
		),
153
		'order.base.service.mtime' => array(
154
			'code' => 'order.base.service.mtime',
155
			'internalcode' => 'mordbase."mtime"',
156
			'label' => 'Service modify date/time',
157
			'type' => 'datetime',
158
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
159
			'public' => false,
160
		),
161
		'order.base.service.editor' => array(
162
			'code' => 'order.base.service.editor',
163
			'internalcode' => 'mordbase."editor"',
164
			'label' => 'Service editor',
165
			'type' => 'string',
166
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
167
			'public' => false,
168
		),
169
	);
170
171
172
	/**
173
	 * Initializes the object.
174
	 *
175
	 * @param \Aimeos\MShop\ContextIface $context Context object
176
	 */
177
	public function __construct( \Aimeos\MShop\ContextIface $context )
178
	{
179
		parent::__construct( $context );
180
		$this->setResourceName( 'db-order' );
181
	}
182
183
184
	/**
185
	 * Counts the number items that are available for the values of the given key.
186
	 *
187
	 * @param \Aimeos\Base\Criteria\Iface $search Search criteria
188
	 * @param array|string $key Search key or list of keys to aggregate items for
189
	 * @param string|null $value Search key for aggregating the value column
190
	 * @param string|null $type Type of the aggregation, empty string for count or "sum" or "avg" (average)
191
	 * @return \Aimeos\Map List of the search keys as key and the number of counted items as value
192
	 */
193
	public function aggregate( \Aimeos\Base\Criteria\Iface $search, $key, string $value = null, string $type = null ) : \Aimeos\Map
194
	{
195
		/** mshop/order/manager/base/service/aggregate/mysql
196
		 * Counts the number of records grouped by the values in the key column and matched by the given criteria
197
		 *
198
		 * @see mshop/order/manager/base/service/aggregate/ansi
199
		 */
200
201
		/** mshop/order/manager/base/service/aggregate/ansi
202
		 * Counts the number of records grouped by the values in the key column and matched by the given criteria
203
		 *
204
		 * Groups all records by the values in the key column and counts their
205
		 * occurence. The matched records can be limited by the given criteria
206
		 * from the order database. The records must be from one of the sites
207
		 * that are configured via the context item. If the current site is part
208
		 * of a tree of sites, the statement can count all records from the
209
		 * current site and the complete sub-tree of sites.
210
		 *
211
		 * As the records can normally be limited by criteria from sub-managers,
212
		 * their tables must be joined in the SQL context. This is done by
213
		 * using the "internaldeps" property from the definition of the ID
214
		 * column of the sub-managers. These internal dependencies specify
215
		 * the JOIN between the tables and the used columns for joining. The
216
		 * ":joins" placeholder is then replaced by the JOIN strings from
217
		 * the sub-managers.
218
		 *
219
		 * To limit the records matched, conditions can be added to the given
220
		 * criteria object. It can contain comparisons like column names that
221
		 * must match specific values which can be combined by AND, OR or NOT
222
		 * operators. The resulting string of SQL conditions replaces the
223
		 * ":cond" placeholder before the statement is sent to the database
224
		 * server.
225
		 *
226
		 * This statement doesn't return any records. Instead, it returns pairs
227
		 * of the different values found in the key column together with the
228
		 * number of records that have been found for that key values.
229
		 *
230
		 * The SQL statement should conform to the ANSI standard to be
231
		 * compatible with most relational database systems. This also
232
		 * includes using double quotes for table and column names.
233
		 *
234
		 * @param string SQL statement for aggregating order items
235
		 * @since 2014.09
236
		 * @category Developer
237
		 * @see mshop/order/manager/base/service/insert/ansi
238
		 * @see mshop/order/manager/base/service/update/ansi
239
		 * @see mshop/order/manager/base/service/newid/ansi
240
		 * @see mshop/order/manager/base/service/delete/ansi
241
		 * @see mshop/order/manager/base/service/search/ansi
242
		 * @see mshop/order/manager/base/service/count/ansi
243
		 */
244
245
		/** mshop/order/manager/base/service/aggregateavg/mysql
246
		 * Computes the average of all values grouped by the key column and matched by the given criteria
247
		 *
248
		 * @param string SQL statement for aggregating the order service items and computing the average value
249
		 * @since 2017.10
250
		 * @category Developer
251
		 * @see mshop/order/manager/base/service/aggregateavg/ansi
252
		 * @see mshop/order/manager/base/service/aggregate/mysql
253
		 */
254
255
		/** mshop/order/manager/base/service/aggregateavg/ansi
256
		 * Computes the average of all values grouped by the key column and matched by the given criteria
257
		 *
258
		 * @param string SQL statement for aggregating the order service items and computing the average value
259
		 * @since 2017.10
260
		 * @category Developer
261
		 * @see mshop/order/manager/base/service/aggregate/ansi
262
		 */
263
264
		/** mshop/order/manager/base/service/aggregatesum/mysql
265
		 * Computes the sum of all values grouped by the key column and matched by the given criteria
266
		 *
267
		 * @param string SQL statement for aggregating the order service items and computing the sum
268
		 * @since 2017.10
269
		 * @category Developer
270
		 * @see mshop/order/manager/base/service/aggregatesum/ansi
271
		 * @see mshop/order/manager/base/service/aggregate/mysql
272
		 */
273
274
		/** mshop/order/manager/base/service/aggregatesum/ansi
275
		 * Computes the sum of all values grouped by the key column and matched by the given criteria
276
		 *
277
		 * @param string SQL statement for aggregating the order service items and computing the sum
278
		 * @since 2017.10
279
		 * @category Developer
280
		 * @see mshop/order/manager/base/service/aggregate/ansi
281
		 */
282
283
		$cfgkey = 'mshop/order/manager/base/service/aggregate';
284
		return $this->aggregateBase( $search, $key, $cfgkey, ['order.base.service'], $value, $type );
285
	}
286
287
288
	/**
289
	 * Removes old entries from the storage.
290
	 *
291
	 * @param iterable $siteids List of IDs for sites whose entries should be deleted
292
	 * @return \Aimeos\MShop\Order\Manager\Base\Service\Iface Manager object for chaining method calls
293
	 */
294
	public function clear( iterable $siteids ) : \Aimeos\MShop\Common\Manager\Iface
295
	{
296
		$path = 'mshop/order/manager/base/service/submanagers';
297
		foreach( $this->context()->config()->get( $path, array( 'attribute' ) ) as $domain ) {
298
			$this->object()->getSubManager( $domain )->clear( $siteids );
299
		}
300
301
		return $this->clearBase( $siteids, 'mshop/order/manager/base/service/delete' );
302
	}
303
304
305
	/**
306
	 * Creates a new empty item instance
307
	 *
308
	 * @param array $values Values the item should be initialized with
309
	 * @return \Aimeos\MShop\Order\Item\Base\Service\Iface New order service item object
310
	 */
311
	public function create( array $values = [] ) : \Aimeos\MShop\Common\Item\Iface
312
	{
313
		$context = $this->context();
314
		$priceManager = \Aimeos\MShop::create( $context, 'price' );
315
316
		$values['order.base.service.siteid'] = $values['order.base.service.siteid'] ?? $context->locale()->getSiteId();
317
318
		return $this->createItemBase( $priceManager->create(), $values );
319
	}
320
321
322
	/**
323
	 * Creates a new order service attribute item instance
324
	 *
325
	 * @param array $values Values the item should be initialized with
326
	 * @return \Aimeos\MShop\Order\Item\Base\Service\Attribute\Iface New order service attribute item object
327
	 */
328
	public function createAttributeItem( array $values = [] ) : \Aimeos\MShop\Common\Item\Iface
329
	{
330
		return $this->object()->getSubManager( 'attribute' )->create( $values );
331
	}
332
333
334
	/**
335
	 * Creates a new order service transaction item instance
336
	 *
337
	 * @param array $values Values the item should be initialized with
338
	 * @return \Aimeos\MShop\Order\Item\Base\Service\Transaction\Iface New order service transaction item object
339
	 */
340
	public function createTransaction( array $values = [] ) : \Aimeos\MShop\Common\Item\Iface
341
	{
342
		return $this->object()->getSubManager( 'transaction' )->create( $values );
343
	}
344
345
346
	/**
347
	 * Creates a filter object.
348
	 *
349
	 * @param bool|null $default Add default criteria or NULL for relaxed default criteria
350
	 * @param bool $site TRUE for adding site criteria to limit items by the site of related items
351
	 * @return \Aimeos\Base\Criteria\Iface Returns the filter object
352
	 */
353
	public function filter( ?bool $default = false, bool $site = false ) : \Aimeos\Base\Criteria\Iface
354
	{
355
		$search = parent::filter( $default );
356
		$search->setSortations( [$search->sort( '+', 'order.base.service.id' )] );
357
358
		return $search;
359
	}
360
361
362
	/**
363
	 * Removes multiple items.
364
	 *
365
	 * @param \Aimeos\MShop\Common\Item\Iface[]|string[] $itemIds List of item objects or IDs of the items
366
	 * @return \Aimeos\MShop\Order\Manager\Base\Service\Iface Manager object for chaining method calls
367
	 */
368
	public function delete( $itemIds ) : \Aimeos\MShop\Common\Manager\Iface
369
	{
370
		/** mshop/order/manager/base/service/delete/mysql
371
		 * Deletes the items matched by the given IDs from the database
372
		 *
373
		 * @see mshop/order/manager/base/service/delete/ansi
374
		 */
375
376
		/** mshop/order/manager/base/service/delete/ansi
377
		 * Deletes the items matched by the given IDs from the database
378
		 *
379
		 * Removes the records specified by the given IDs from the order database.
380
		 * The records must be from the site that is configured via the
381
		 * context item.
382
		 *
383
		 * The ":cond" placeholder is replaced by the name of the ID column and
384
		 * the given ID or list of IDs while the site ID is bound to the question
385
		 * mark.
386
		 *
387
		 * The SQL statement should conform to the ANSI standard to be
388
		 * compatible with most relational database systems. This also
389
		 * includes using double quotes for table and column names.
390
		 *
391
		 * @param string SQL statement for deleting items
392
		 * @since 2014.03
393
		 * @category Developer
394
		 * @see mshop/order/manager/base/service/insert/ansi
395
		 * @see mshop/order/manager/base/service/update/ansi
396
		 * @see mshop/order/manager/base/service/newid/ansi
397
		 * @see mshop/order/manager/base/service/search/ansi
398
		 * @see mshop/order/manager/base/service/count/ansi
399
		 */
400
		$path = 'mshop/order/manager/base/service/delete';
401
402
		return $this->deleteItemsBase( $itemIds, $path );
403
	}
404
405
406
	/**
407
	 * Returns the order service item object for the given ID.
408
	 *
409
	 * @param string $id Order service ID
410
	 * @param string[] $ref List of domains to fetch list items and referenced items for
411
	 * @param bool|null $default Add default criteria or NULL for relaxed default criteria
412
	 * @return \Aimeos\MShop\Order\Item\Base\Service\Iface Returns order base service item of the given id
413
	 * @throws \Aimeos\MShop\Exception If item couldn't be found
414
	 */
415
	public function get( string $id, array $ref = [], ?bool $default = false ) : \Aimeos\MShop\Common\Item\Iface
416
	{
417
		return $this->getItemBase( 'order.base.service.id', $id, $ref, $default );
418
	}
419
420
421
	/**
422
	 * Returns the available manager types
423
	 *
424
	 * @param bool $withsub Return also the resource type of sub-managers if true
425
	 * @return string[] Type of the manager and submanagers, subtypes are separated by slashes
426
	 */
427
	public function getResourceType( bool $withsub = true ) : array
428
	{
429
		$path = 'mshop/order/manager/base/service/submanagers';
430
		return $this->getResourceTypeBase( 'order/base/service', $path, array( 'attribute' ), $withsub );
431
	}
432
433
434
	/**
435
	 * Returns the search attributes that can be used for searching.
436
	 *
437
	 * @param bool $withsub Return also attributes of sub-managers if true
438
	 * @return \Aimeos\Base\Criteria\Attribute\Iface[] List of search attribute items
439
	 */
440
	public function getSearchAttributes( bool $withsub = true ) : array
441
	{
442
		/** mshop/order/manager/base/service/submanagers
443
		 * List of manager names that can be instantiated by the order base service manager
444
		 *
445
		 * Managers provide a generic interface to the underlying storage.
446
		 * Each manager has or can have sub-managers caring about particular
447
		 * aspects. Each of these sub-managers can be instantiated by its
448
		 * parent manager using the getSubManager() method.
449
		 *
450
		 * The search keys from sub-managers can be normally used in the
451
		 * manager as well. It allows you to search for items of the manager
452
		 * using the search keys of the sub-managers to further limit the
453
		 * retrieved list of items.
454
		 *
455
		 * @param array List of sub-manager names
456
		 * @since 2014.03
457
		 * @category Developer
458
		 */
459
		$path = 'mshop/order/manager/base/service/submanagers';
460
461
		return $this->getSearchAttributesBase( $this->searchConfig, $path, array( 'attribute' ), $withsub );
462
	}
463
464
465
	/**
466
	 * Returns a new manager for order service extensions.
467
	 *
468
	 * @param string $manager Name of the sub manager type in lower case
469
	 * @param string|null $name Name of the implementation (from configuration or "Standard" if null)
470
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager for different extensions, e.g attribute
471
	 */
472
	public function getSubManager( string $manager, string $name = null ) : \Aimeos\MShop\Common\Manager\Iface
473
	{
474
		/** mshop/order/manager/base/service/name
475
		 * Class name of the used order base service manager implementation
476
		 *
477
		 * Each default order base service manager can be replaced by an alternative imlementation.
478
		 * To use this implementation, you have to set the last part of the class
479
		 * name as configuration value so the manager factory knows which class it
480
		 * has to instantiate.
481
		 *
482
		 * For example, if the name of the default class is
483
		 *
484
		 *  \Aimeos\MShop\Order\Manager\Base\Service\Standard
485
		 *
486
		 * and you want to replace it with your own version named
487
		 *
488
		 *  \Aimeos\MShop\Order\Manager\Base\Service\Myservice
489
		 *
490
		 * then you have to set the this configuration option:
491
		 *
492
		 *  mshop/order/manager/base/service/name = Myservice
493
		 *
494
		 * The value is the last part of your own class name and it's case sensitive,
495
		 * so take care that the configuration value is exactly named like the last
496
		 * part of the class name.
497
		 *
498
		 * The allowed characters of the class name are A-Z, a-z and 0-9. No other
499
		 * characters are possible! You should always start the last part of the class
500
		 * name with an upper case character and continue only with lower case characters
501
		 * or numbers. Avoid chamel case names like "MyService"!
502
		 *
503
		 * @param string Last part of the class name
504
		 * @since 2014.03
505
		 * @category Developer
506
		 */
507
508
		/** mshop/order/manager/base/service/decorators/excludes
509
		 * Excludes decorators added by the "common" option from the order base service manager
510
		 *
511
		 * Decorators extend the functionality of a class by adding new aspects
512
		 * (e.g. log what is currently done), executing the methods of the underlying
513
		 * class only in certain conditions (e.g. only for logged in users) or
514
		 * modify what is returned to the caller.
515
		 *
516
		 * This option allows you to remove a decorator added via
517
		 * "mshop/common/manager/decorators/default" before they are wrapped
518
		 * around the order base service manager.
519
		 *
520
		 *  mshop/order/manager/base/service/decorators/excludes = array( 'decorator1' )
521
		 *
522
		 * This would remove the decorator named "decorator1" from the list of
523
		 * common decorators ("\Aimeos\MShop\Common\Manager\Decorator\*") added via
524
		 * "mshop/common/manager/decorators/default" for the order base service manager.
525
		 *
526
		 * @param array List of decorator names
527
		 * @since 2014.03
528
		 * @category Developer
529
		 * @see mshop/common/manager/decorators/default
530
		 * @see mshop/order/manager/base/service/decorators/global
531
		 * @see mshop/order/manager/base/service/decorators/local
532
		 */
533
534
		/** mshop/order/manager/base/service/decorators/global
535
		 * Adds a list of globally available decorators only to the order base service manager
536
		 *
537
		 * Decorators extend the functionality of a class by adding new aspects
538
		 * (e.g. log what is currently done), executing the methods of the underlying
539
		 * class only in certain conditions (e.g. only for logged in users) or
540
		 * modify what is returned to the caller.
541
		 *
542
		 * This option allows you to wrap global decorators
543
		 * ("\Aimeos\MShop\Common\Manager\Decorator\*") around the order base
544
		 * service manager.
545
		 *
546
		 *  mshop/order/manager/base/service/decorators/global = array( 'decorator1' )
547
		 *
548
		 * This would add the decorator named "decorator1" defined by
549
		 * "\Aimeos\MShop\Common\Manager\Decorator\Decorator1" only to the order
550
		 * base service manager.
551
		 *
552
		 * @param array List of decorator names
553
		 * @since 2014.03
554
		 * @category Developer
555
		 * @see mshop/common/manager/decorators/default
556
		 * @see mshop/order/manager/base/service/decorators/excludes
557
		 * @see mshop/order/manager/base/service/decorators/local
558
		 */
559
560
		/** mshop/order/manager/base/service/decorators/local
561
		 * Adds a list of local decorators only to the order base service manager
562
		 *
563
		 * Decorators extend the functionality of a class by adding new aspects
564
		 * (e.g. log what is currently done), executing the methods of the underlying
565
		 * class only in certain conditions (e.g. only for logged in users) or
566
		 * modify what is returned to the caller.
567
		 *
568
		 * This option allows you to wrap local decorators
569
		 * ("\Aimeos\MShop\Order\Manager\Base\Service\Decorator\*") around the
570
		 * order base service manager.
571
		 *
572
		 *  mshop/order/manager/base/service/decorators/local = array( 'decorator2' )
573
		 *
574
		 * This would add the decorator named "decorator2" defined by
575
		 * "\Aimeos\MShop\Order\Manager\Base\Service\Decorator\Decorator2" only
576
		 * to the order base service manager.
577
		 *
578
		 * @param array List of decorator names
579
		 * @since 2014.03
580
		 * @category Developer
581
		 * @see mshop/common/manager/decorators/default
582
		 * @see mshop/order/manager/base/service/decorators/excludes
583
		 * @see mshop/order/manager/base/service/decorators/global
584
		 */
585
586
		return $this->getSubManagerBase( 'order', 'base/service/' . $manager, $name );
587
	}
588
589
590
	/**
591
	 * Adds or updates an order base service item to the storage.
592
	 *
593
	 * @param \Aimeos\MShop\Order\Item\Base\Service\Iface $item Order base service object
594
	 * @param bool $fetch True if the new ID should be returned in the item
595
	 * @return \Aimeos\MShop\Order\Item\Base\Service\Iface $item Updated item including the generated ID
596
	 */
597
	protected function saveItem( \Aimeos\MShop\Order\Item\Base\Service\Iface $item, bool $fetch = true ) : \Aimeos\MShop\Order\Item\Base\Service\Iface
598
	{
599
		if( !$item->isModified() && !$item->getPrice()->isModified() )
600
		{
601
			$this->saveAttributeItems( $item, $fetch );
602
			$this->saveTransactions( $item, $fetch );
603
			return $item;
604
		}
605
606
		$context = $this->context();
607
		$conn = $context->db( $this->getResourceName() );
608
609
		$id = $item->getId();
610
		$price = $item->getPrice();
611
		$date = date( 'Y-m-d H:i:s' );
612
		$columns = $this->object()->getSaveAttributes();
613
614
		if( $id === null )
615
		{
616
			/** mshop/order/manager/base/service/insert/mysql
617
			 * Inserts a new order record into the database table
618
			 *
619
			 * @see mshop/order/manager/base/service/insert/ansi
620
			 */
621
622
			/** mshop/order/manager/base/service/insert/ansi
623
			 * Inserts a new order record into the database table
624
			 *
625
			 * Items with no ID yet (i.e. the ID is NULL) will be created in
626
			 * the database and the newly created ID retrieved afterwards
627
			 * using the "newid" SQL statement.
628
			 *
629
			 * The SQL statement must be a string suitable for being used as
630
			 * prepared statement. It must include question marks for binding
631
			 * the values from the order item to the statement before they are
632
			 * sent to the database server. The number of question marks must
633
			 * be the same as the number of columns listed in the INSERT
634
			 * statement. The order of the columns must correspond to the
635
			 * order in the save() method, so the correct values are
636
			 * bound to the columns.
637
			 *
638
			 * The SQL statement should conform to the ANSI standard to be
639
			 * compatible with most relational database systems. This also
640
			 * includes using double quotes for table and column names.
641
			 *
642
			 * @param string SQL statement for inserting records
643
			 * @since 2014.03
644
			 * @category Developer
645
			 * @see mshop/order/manager/base/service/update/ansi
646
			 * @see mshop/order/manager/base/service/newid/ansi
647
			 * @see mshop/order/manager/base/service/delete/ansi
648
			 * @see mshop/order/manager/base/service/search/ansi
649
			 * @see mshop/order/manager/base/service/count/ansi
650
			 */
651
			$path = 'mshop/order/manager/base/service/insert';
652
			$sql = $this->addSqlColumns( array_keys( $columns ), $this->getSqlConfig( $path ) );
0 ignored issues
show
Bug introduced by
It seems like $this->getSqlConfig($path) can also be of type array; however, parameter $sql of Aimeos\MShop\Common\Manager\Base::addSqlColumns() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

652
			$sql = $this->addSqlColumns( array_keys( $columns ), /** @scrutinizer ignore-type */ $this->getSqlConfig( $path ) );
Loading history...
653
		}
654
		else
655
		{
656
			/** mshop/order/manager/base/service/update/mysql
657
			 * Updates an existing order record in the database
658
			 *
659
			 * @see mshop/order/manager/base/service/update/ansi
660
			 */
661
662
			/** mshop/order/manager/base/service/update/ansi
663
			 * Updates an existing order record in the database
664
			 *
665
			 * Items which already have an ID (i.e. the ID is not NULL) will
666
			 * be updated in the database.
667
			 *
668
			 * The SQL statement must be a string suitable for being used as
669
			 * prepared statement. It must include question marks for binding
670
			 * the values from the order item to the statement before they are
671
			 * sent to the database server. The order of the columns must
672
			 * correspond to the order in the save() method, so the
673
			 * correct values are bound to the columns.
674
			 *
675
			 * The SQL statement should conform to the ANSI standard to be
676
			 * compatible with most relational database systems. This also
677
			 * includes using double quotes for table and column names.
678
			 *
679
			 * @param string SQL statement for updating records
680
			 * @since 2014.03
681
			 * @category Developer
682
			 * @see mshop/order/manager/base/service/insert/ansi
683
			 * @see mshop/order/manager/base/service/newid/ansi
684
			 * @see mshop/order/manager/base/service/delete/ansi
685
			 * @see mshop/order/manager/base/service/search/ansi
686
			 * @see mshop/order/manager/base/service/count/ansi
687
			 */
688
			$path = 'mshop/order/manager/base/service/update';
689
			$sql = $this->addSqlColumns( array_keys( $columns ), $this->getSqlConfig( $path ), false );
690
		}
691
692
		$idx = 1;
693
		$stmt = $this->getCachedStatement( $conn, $path, $sql );
694
695
		foreach( $columns as $name => $entry ) {
696
			$stmt->bind( $idx++, $item->get( $name ), $entry->getInternalType() );
697
		}
698
699
		$stmt->bind( $idx++, $item->getBaseId(), \Aimeos\Base\DB\Statement\Base::PARAM_INT );
700
		$stmt->bind( $idx++, $item->getServiceId() );
701
		$stmt->bind( $idx++, $item->getType() );
702
		$stmt->bind( $idx++, $item->getCode() );
703
		$stmt->bind( $idx++, $item->getName() );
704
		$stmt->bind( $idx++, $item->getMediaUrl() );
705
		$stmt->bind( $idx++, $price->getCurrencyId() );
706
		$stmt->bind( $idx++, $price->getValue() );
707
		$stmt->bind( $idx++, $price->getCosts() );
708
		$stmt->bind( $idx++, $price->getRebate() );
709
		$stmt->bind( $idx++, $price->getTaxValue() );
710
		$stmt->bind( $idx++, json_encode( $price->getTaxRates(), JSON_FORCE_OBJECT ) );
711
		$stmt->bind( $idx++, $price->getTaxFlag(), \Aimeos\Base\DB\Statement\Base::PARAM_INT );
712
		$stmt->bind( $idx++, (int) $item->getPosition(), \Aimeos\Base\DB\Statement\Base::PARAM_INT );
713
		$stmt->bind( $idx++, $date ); // mtime
714
		$stmt->bind( $idx++, $context->editor() );
715
		$stmt->bind( $idx++, $this->siteId( $item->getSiteId(), \Aimeos\MShop\Locale\Manager\Base::SITE_SUBTREE ) );
716
717
		if( $id !== null ) {
718
			$stmt->bind( $idx++, $id, \Aimeos\Base\DB\Statement\Base::PARAM_INT );
719
		} else {
720
			$stmt->bind( $idx++, $date ); // ctime
721
		}
722
723
		$stmt->execute()->finish();
724
725
		if( $id === null && $fetch === true )
726
		{
727
			/** mshop/order/manager/base/service/newid/mysql
728
			 * Retrieves the ID generated by the database when inserting a new record
729
			 *
730
			 * @see mshop/order/manager/base/service/newid/ansi
731
			 */
732
733
			/** mshop/order/manager/base/service/newid/ansi
734
			 * Retrieves the ID generated by the database when inserting a new record
735
			 *
736
			 * As soon as a new record is inserted into the database table,
737
			 * the database server generates a new and unique identifier for
738
			 * that record. This ID can be used for retrieving, updating and
739
			 * deleting that specific record from the table again.
740
			 *
741
			 * For MySQL:
742
			 *  SELECT LAST_INSERT_ID()
743
			 * For PostgreSQL:
744
			 *  SELECT currval('seq_mord_id')
745
			 * For SQL Server:
746
			 *  SELECT SCOPE_IDENTITY()
747
			 * For Oracle:
748
			 *  SELECT "seq_mord_id".CURRVAL FROM DUAL
749
			 *
750
			 * There's no way to retrive the new ID by a SQL statements that
751
			 * fits for most database servers as they implement their own
752
			 * specific way.
753
			 *
754
			 * @param string SQL statement for retrieving the last inserted record ID
755
			 * @since 2014.03
756
			 * @category Developer
757
			 * @see mshop/order/manager/base/service/insert/ansi
758
			 * @see mshop/order/manager/base/service/update/ansi
759
			 * @see mshop/order/manager/base/service/delete/ansi
760
			 * @see mshop/order/manager/base/service/search/ansi
761
			 * @see mshop/order/manager/base/service/count/ansi
762
			 */
763
			$path = 'mshop/order/manager/base/service/newid';
764
			$id = $this->newId( $conn, $path );
765
		}
766
767
		$item->setId( $id );
768
769
		$this->saveAttributeItems( $item, $fetch );
770
		$this->saveTransactions( $item, $fetch );
771
772
		return $item;
773
}
774
775
776
	/**
777
	 * Searches for order service items based on the given criteria.
778
	 *
779
	 * @param \Aimeos\Base\Criteria\Iface $search Search criteria object
780
	 * @param string[] $ref List of domains to fetch list items and referenced items for
781
	 * @param int|null &$total Number of items that are available in total
782
	 * @return \Aimeos\Map List of items implementing \Aimeos\MShop\Order\Item\Base\Service\Iface with ids as keys
783
	 */
784
	public function search( \Aimeos\Base\Criteria\Iface $search, array $ref = [], int &$total = null ) : \Aimeos\Map
785
	{
786
		$context = $this->context();
787
		$priceManager = \Aimeos\MShop::create( $context, 'price' );
788
789
		$conn = $context->db( $this->getResourceName() );
790
		$map = $items = $servItems = [];
791
792
			$required = array( 'order.base.service' );
793
794
			$level = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
795
			$level = $context->config()->get( 'mshop/order/manager/sitemode', $level );
796
797
			/** mshop/order/manager/base/service/search/mysql
798
			 * Retrieves the records matched by the given criteria in the database
799
			 *
800
			 * @see mshop/order/manager/base/service/search/ansi
801
			 */
802
803
			/** mshop/order/manager/base/service/search/ansi
804
			 * Retrieves the records matched by the given criteria in the database
805
			 *
806
			 * Fetches the records matched by the given criteria from the order
807
			 * database. The records must be from one of the sites that are
808
			 * configured via the context item. If the current site is part of
809
			 * a tree of sites, the SELECT statement can retrieve all records
810
			 * from the current site and the complete sub-tree of sites.
811
			 *
812
			 * As the records can normally be limited by criteria from sub-managers,
813
			 * their tables must be joined in the SQL context. This is done by
814
			 * using the "internaldeps" property from the definition of the ID
815
			 * column of the sub-managers. These internal dependencies specify
816
			 * the JOIN between the tables and the used columns for joining. The
817
			 * ":joins" placeholder is then replaced by the JOIN strings from
818
			 * the sub-managers.
819
			 *
820
			 * To limit the records matched, conditions can be added to the given
821
			 * criteria object. It can contain comparisons like column names that
822
			 * must match specific values which can be combined by AND, OR or NOT
823
			 * operators. The resulting string of SQL conditions replaces the
824
			 * ":cond" placeholder before the statement is sent to the database
825
			 * server.
826
			 *
827
			 * If the records that are retrieved should be ordered by one or more
828
			 * columns, the generated string of column / sort direction pairs
829
			 * replaces the ":order" placeholder. In case no ordering is required,
830
			 * the complete ORDER BY part including the "\/*-orderby*\/...\/*orderby-*\/"
831
			 * markers is removed to speed up retrieving the records. Columns of
832
			 * sub-managers can also be used for ordering the result set but then
833
			 * no index can be used.
834
			 *
835
			 * The number of returned records can be limited and can start at any
836
			 * number between the begining and the end of the result set. For that
837
			 * the ":size" and ":start" placeholders are replaced by the
838
			 * corresponding values from the criteria object. The default values
839
			 * are 0 for the start and 100 for the size value.
840
			 *
841
			 * The SQL statement should conform to the ANSI standard to be
842
			 * compatible with most relational database systems. This also
843
			 * includes using double quotes for table and column names.
844
			 *
845
			 * @param string SQL statement for searching items
846
			 * @since 2014.03
847
			 * @category Developer
848
			 * @see mshop/order/manager/base/service/insert/ansi
849
			 * @see mshop/order/manager/base/service/update/ansi
850
			 * @see mshop/order/manager/base/service/newid/ansi
851
			 * @see mshop/order/manager/base/service/delete/ansi
852
			 * @see mshop/order/manager/base/service/count/ansi
853
			 */
854
			$cfgPathSearch = 'mshop/order/manager/base/service/search';
855
856
			/** mshop/order/manager/base/service/count/mysql
857
			 * Counts the number of records matched by the given criteria in the database
858
			 *
859
			 * @see mshop/order/manager/base/service/count/ansi
860
			 */
861
862
			/** mshop/order/manager/base/service/count/ansi
863
			 * Counts the number of records matched by the given criteria in the database
864
			 *
865
			 * Counts all records matched by the given criteria from the order
866
			 * database. The records must be from one of the sites that are
867
			 * configured via the context item. If the current site is part of
868
			 * a tree of sites, the statement can count all records from the
869
			 * current site and the complete sub-tree of sites.
870
			 *
871
			 * As the records can normally be limited by criteria from sub-managers,
872
			 * their tables must be joined in the SQL context. This is done by
873
			 * using the "internaldeps" property from the definition of the ID
874
			 * column of the sub-managers. These internal dependencies specify
875
			 * the JOIN between the tables and the used columns for joining. The
876
			 * ":joins" placeholder is then replaced by the JOIN strings from
877
			 * the sub-managers.
878
			 *
879
			 * To limit the records matched, conditions can be added to the given
880
			 * criteria object. It can contain comparisons like column names that
881
			 * must match specific values which can be combined by AND, OR or NOT
882
			 * operators. The resulting string of SQL conditions replaces the
883
			 * ":cond" placeholder before the statement is sent to the database
884
			 * server.
885
			 *
886
			 * Both, the strings for ":joins" and for ":cond" are the same as for
887
			 * the "search" SQL statement.
888
			 *
889
			 * Contrary to the "search" statement, it doesn't return any records
890
			 * but instead the number of records that have been found. As counting
891
			 * thousands of records can be a long running task, the maximum number
892
			 * of counted records is limited for performance reasons.
893
			 *
894
			 * The SQL statement should conform to the ANSI standard to be
895
			 * compatible with most relational database systems. This also
896
			 * includes using double quotes for table and column names.
897
			 *
898
			 * @param string SQL statement for counting items
899
			 * @since 2014.03
900
			 * @category Developer
901
			 * @see mshop/order/manager/base/service/insert/ansi
902
			 * @see mshop/order/manager/base/service/update/ansi
903
			 * @see mshop/order/manager/base/service/newid/ansi
904
			 * @see mshop/order/manager/base/service/delete/ansi
905
			 * @see mshop/order/manager/base/service/search/ansi
906
			 */
907
			$cfgPathCount = 'mshop/order/manager/base/service/count';
908
909
			$results = $this->searchItemsBase( $conn, $search, $cfgPathSearch, $cfgPathCount,
910
				$required, $total, $level );
911
912
			try
913
			{
914
				while( ( $row = $results->fetch() ) !== null )
915
				{
916
					if( ( $row['order.base.service.taxrates'] = json_decode( $config = $row['order.base.service.taxrates'], true ) ) === null )
917
					{
918
						$msg = sprintf( 'Invalid JSON as result of search for ID "%2$s" in "%1$s": %3$s', 'mshop_order_base_service.taxrates', $row['order.base.service.id'], $config );
919
						$this->context()->logger()->warning( $msg, 'core/order' );
920
					}
921
922
					$price = $priceManager->create( [
923
						'price.currencyid' => $row['order.base.service.currencyid'],
924
						'price.taxrates' => $row['order.base.service.taxrates'],
925
						'price.value' => $row['order.base.service.price'],
926
						'price.costs' => $row['order.base.service.costs'],
927
						'price.rebate' => $row['order.base.service.rebate'],
928
						'price.taxflag' => $row['order.base.service.taxflag'],
929
						'price.taxvalue' => $row['order.base.service.taxvalue'],
930
					] );
931
932
					$map[$row['order.base.service.id']] = ['price' => $price, 'item' => $row];
933
				}
934
			}
935
			catch( \Exception $e )
936
			{
937
				$results->finish();
938
				throw $e;
939
			}
940
941
942
		if( isset( $ref['service'] ) || in_array( 'service', $ref ) )
943
		{
944
			$ids = [];
945
			foreach( $map as $list ) {
946
				$ids[] = $list['item']['order.base.service.serviceid'] ?? null;
947
			}
948
949
			$manager = \Aimeos\MShop::create( $context, 'service' );
950
			$search = $manager->filter()->slice( 0, count( $ids ) )->add( ['service.id' => array_filter( $ids )] );
951
			$servItems = $manager->search( $search, $ref );
952
		}
953
954
		$attributes = $this->getAttributeItems( array_keys( $map ) );
955
		$transactions = $this->getTransactions( array_keys( $map ) );
956
957
		foreach( $map as $id => $list )
958
		{
959
			$servItem = $servItems[$list['item']['order.base.service.serviceid'] ?? null] ?? null;
960
			$item = $this->createItemBase(
961
				$list['price'], $list['item'], $attributes[$id] ?? [], $transactions[$id] ?? [], $servItem
962
			);
963
964
			if( $item = $this->applyFilter( $item ) ) {
965
				$items[$id] = $item;
966
			}
967
		}
968
969
		return map( $items );
970
	}
971
972
973
	/**
974
	 * Creates a new order service item object initialized with given parameters.
975
	 *
976
	 * @param \Aimeos\MShop\Price\Item\Iface $price Price object
977
	 * @param array $values Associative list of values from the database
978
	 * @param \Aimeos\MShop\Order\Item\Base\Service\Attribute\Iface[] $attributes List of order service attribute items
979
	 * @param \Aimeos\MShop\Order\Item\Base\Service\Transaction\Iface[] $transactions List of order service transaction items
980
	 * @param \Aimeos\MShop\Service\Item\Iface|null $servItem Original service item
981
	 * @return \Aimeos\MShop\Order\Item\Base\Service\Iface Order service item
982
	 */
983
	protected function createItemBase( \Aimeos\MShop\Price\Item\Iface $price, array $values = [], array $attributes = [],
984
		array $transactions = [], ?\Aimeos\MShop\Service\Item\Iface $servItem = null ) : \Aimeos\MShop\Order\Item\Base\Service\Iface
985
	{
986
		return new \Aimeos\MShop\Order\Item\Base\Service\Standard( $price, $values, $attributes, $transactions, $servItem );
987
	}
988
989
990
	/**
991
	 * Searches for attribute items connected with order service item.
992
	 *
993
	 * @param string[] $ids List of order service item IDs
994
	 * @return array Associative list of order service IDs as keys and order service attribute items
995
	 *  implementing \Aimeos\MShop\Order\Item\Base\Service\Attribute\Iface as values
996
	 */
997
	protected function getAttributeItems( array $ids ) : array
998
	{
999
		$manager = $this->object()->getSubManager( 'attribute' );
1000
		$search = $manager->filter()->slice( 0, 0x7fffffff );
1001
		$search->setConditions( $search->compare( '==', 'order.base.service.attribute.parentid', $ids ) );
1002
1003
		$result = [];
1004
		foreach( $manager->search( $search ) as $item ) {
1005
			$result[$item->getParentId()][$item->getId()] = $item;
1006
		}
1007
1008
		return $result;
1009
	}
1010
1011
1012
	/**
1013
	 * Searches for transaction items connected with order service item.
1014
	 *
1015
	 * @param string[] $ids List of order service item IDs
1016
	 * @return array Associative list of order service IDs as keys and order service transaction items
1017
	 *  implementing \Aimeos\MShop\Order\Item\Base\Service\Transaction\Iface as values
1018
	 */
1019
	protected function getTransactions( array $ids ) : array
1020
	{
1021
		$manager = $this->object()->getSubManager( 'transaction' );
1022
		$search = $manager->filter()->slice( 0, 0x7fffffff );
1023
		$search->setConditions( $search->compare( '==', 'order.base.service.transaction.parentid', $ids ) );
1024
1025
		$result = [];
1026
		foreach( $manager->search( $search ) as $item ) {
1027
			$result[$item->getParentId()][$item->getId()] = $item;
1028
		}
1029
1030
		return $result;
1031
	}
1032
1033
1034
	/**
1035
	 * Saves the attribute items included in the order service item
1036
	 *
1037
	 * @param \Aimeos\MShop\Order\Item\Base\Service\Iface $item Order service item with attribute items
1038
	 * @param bool $fetch True if the new ID should be set in the attribute item
1039
	 * @return \Aimeos\MShop\Order\Item\Base\Service\Iface Object with saved attribute items and IDs
1040
	 */
1041
	protected function saveAttributeItems( \Aimeos\MShop\Order\Item\Base\Service\Iface $item, bool $fetch ) : \Aimeos\MShop\Order\Item\Base\Service\Iface
1042
	{
1043
		$attrItems = $item->getAttributeItems();
1044
1045
		foreach( $attrItems as $key => $attrItem )
1046
		{
1047
			if( $attrItem->getType() === 'session' )
1048
			{
1049
				unset( $attrItems[$key] );
1050
				continue;
1051
			}
1052
1053
			if( $attrItem->getParentId() != $item->getId() ) {
1054
				$attrItem->setId( null ); // create new property item if copied
1055
			}
1056
1057
			$attrItem->setParentId( $item->getId() );
1058
		}
1059
1060
		$this->object()->getSubManager( 'attribute' )->save( $attrItems, $fetch );
1061
		return $item;
1062
	}
1063
1064
1065
	/**
1066
	 * Saves the transaction items included in the order service item
1067
	 *
1068
	 * @param \Aimeos\MShop\Order\Item\Base\Service\Iface $item Order service item with transaction items
1069
	 * @param bool $fetch True if the new ID should be set in the transaction item
1070
	 * @return \Aimeos\MShop\Order\Item\Base\Service\Iface Object with saved transaction items and IDs
1071
	 */
1072
	protected function saveTransactions( \Aimeos\MShop\Order\Item\Base\Service\Iface $item, bool $fetch ) : \Aimeos\MShop\Order\Item\Base\Service\Iface
1073
	{
1074
		$list = $item->getTransactions();
1075
1076
		foreach( $list as $key => $txItem )
1077
		{
1078
			if( $txItem->getParentId() != $item->getId() ) {
1079
				$txItem->setId( null ); // create new property item if copied
1080
			}
1081
1082
			$txItem->setParentId( $item->getId() );
1083
		}
1084
1085
		$this->object()->getSubManager( 'transaction' )->save( $list, $fetch );
1086
		return $item;
1087
	}
1088
}
1089