Passed
Push — master ( 5d4f58...4ee10f )
by Aimeos
04:18
created

Standard::createAttributeItem()   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 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 filter object.
336
	 *
337
	 * @param bool|null $default Add default criteria or NULL for relaxed default criteria
338
	 * @param bool $site TRUE for adding site criteria to limit items by the site of related items
339
	 * @return \Aimeos\Base\Criteria\Iface Returns the filter object
340
	 */
341
	public function filter( ?bool $default = false, bool $site = false ) : \Aimeos\Base\Criteria\Iface
342
	{
343
		$search = parent::filter( $default );
344
		$search->setSortations( [$search->sort( '+', 'order.base.service.id' )] );
345
346
		return $search;
347
	}
348
349
350
	/**
351
	 * Removes multiple items.
352
	 *
353
	 * @param \Aimeos\MShop\Common\Item\Iface[]|string[] $itemIds List of item objects or IDs of the items
354
	 * @return \Aimeos\MShop\Order\Manager\Base\Service\Iface Manager object for chaining method calls
355
	 */
356
	public function delete( $itemIds ) : \Aimeos\MShop\Common\Manager\Iface
357
	{
358
		/** mshop/order/manager/base/service/delete/mysql
359
		 * Deletes the items matched by the given IDs from the database
360
		 *
361
		 * @see mshop/order/manager/base/service/delete/ansi
362
		 */
363
364
		/** mshop/order/manager/base/service/delete/ansi
365
		 * Deletes the items matched by the given IDs from the database
366
		 *
367
		 * Removes the records specified by the given IDs from the order database.
368
		 * The records must be from the site that is configured via the
369
		 * context item.
370
		 *
371
		 * The ":cond" placeholder is replaced by the name of the ID column and
372
		 * the given ID or list of IDs while the site ID is bound to the question
373
		 * mark.
374
		 *
375
		 * The SQL statement should conform to the ANSI standard to be
376
		 * compatible with most relational database systems. This also
377
		 * includes using double quotes for table and column names.
378
		 *
379
		 * @param string SQL statement for deleting items
380
		 * @since 2014.03
381
		 * @category Developer
382
		 * @see mshop/order/manager/base/service/insert/ansi
383
		 * @see mshop/order/manager/base/service/update/ansi
384
		 * @see mshop/order/manager/base/service/newid/ansi
385
		 * @see mshop/order/manager/base/service/search/ansi
386
		 * @see mshop/order/manager/base/service/count/ansi
387
		 */
388
		$path = 'mshop/order/manager/base/service/delete';
389
390
		return $this->deleteItemsBase( $itemIds, $path );
391
	}
392
393
394
	/**
395
	 * Returns the order service item object for the given ID.
396
	 *
397
	 * @param string $id Order service ID
398
	 * @param string[] $ref List of domains to fetch list items and referenced items for
399
	 * @param bool|null $default Add default criteria or NULL for relaxed default criteria
400
	 * @return \Aimeos\MShop\Order\Item\Base\Service\Iface Returns order base service item of the given id
401
	 * @throws \Aimeos\MShop\Exception If item couldn't be found
402
	 */
403
	public function get( string $id, array $ref = [], ?bool $default = false ) : \Aimeos\MShop\Common\Item\Iface
404
	{
405
		return $this->getItemBase( 'order.base.service.id', $id, $ref, $default );
406
	}
407
408
409
	/**
410
	 * Returns the available manager types
411
	 *
412
	 * @param bool $withsub Return also the resource type of sub-managers if true
413
	 * @return string[] Type of the manager and submanagers, subtypes are separated by slashes
414
	 */
415
	public function getResourceType( bool $withsub = true ) : array
416
	{
417
		$path = 'mshop/order/manager/base/service/submanagers';
418
		return $this->getResourceTypeBase( 'order/base/service', $path, array( 'attribute' ), $withsub );
419
	}
420
421
422
	/**
423
	 * Returns the search attributes that can be used for searching.
424
	 *
425
	 * @param bool $withsub Return also attributes of sub-managers if true
426
	 * @return \Aimeos\Base\Criteria\Attribute\Iface[] List of search attribute items
427
	 */
428
	public function getSearchAttributes( bool $withsub = true ) : array
429
	{
430
		/** mshop/order/manager/base/service/submanagers
431
		 * List of manager names that can be instantiated by the order base service manager
432
		 *
433
		 * Managers provide a generic interface to the underlying storage.
434
		 * Each manager has or can have sub-managers caring about particular
435
		 * aspects. Each of these sub-managers can be instantiated by its
436
		 * parent manager using the getSubManager() method.
437
		 *
438
		 * The search keys from sub-managers can be normally used in the
439
		 * manager as well. It allows you to search for items of the manager
440
		 * using the search keys of the sub-managers to further limit the
441
		 * retrieved list of items.
442
		 *
443
		 * @param array List of sub-manager names
444
		 * @since 2014.03
445
		 * @category Developer
446
		 */
447
		$path = 'mshop/order/manager/base/service/submanagers';
448
449
		return $this->getSearchAttributesBase( $this->searchConfig, $path, array( 'attribute' ), $withsub );
450
	}
451
452
453
	/**
454
	 * Returns a new manager for order service extensions.
455
	 *
456
	 * @param string $manager Name of the sub manager type in lower case
457
	 * @param string|null $name Name of the implementation (from configuration or "Standard" if null)
458
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager for different extensions, e.g attribute
459
	 */
460
	public function getSubManager( string $manager, string $name = null ) : \Aimeos\MShop\Common\Manager\Iface
461
	{
462
		/** mshop/order/manager/base/service/name
463
		 * Class name of the used order base service manager implementation
464
		 *
465
		 * Each default order base service manager can be replaced by an alternative imlementation.
466
		 * To use this implementation, you have to set the last part of the class
467
		 * name as configuration value so the manager factory knows which class it
468
		 * has to instantiate.
469
		 *
470
		 * For example, if the name of the default class is
471
		 *
472
		 *  \Aimeos\MShop\Order\Manager\Base\Service\Standard
473
		 *
474
		 * and you want to replace it with your own version named
475
		 *
476
		 *  \Aimeos\MShop\Order\Manager\Base\Service\Myservice
477
		 *
478
		 * then you have to set the this configuration option:
479
		 *
480
		 *  mshop/order/manager/base/service/name = Myservice
481
		 *
482
		 * The value is the last part of your own class name and it's case sensitive,
483
		 * so take care that the configuration value is exactly named like the last
484
		 * part of the class name.
485
		 *
486
		 * The allowed characters of the class name are A-Z, a-z and 0-9. No other
487
		 * characters are possible! You should always start the last part of the class
488
		 * name with an upper case character and continue only with lower case characters
489
		 * or numbers. Avoid chamel case names like "MyService"!
490
		 *
491
		 * @param string Last part of the class name
492
		 * @since 2014.03
493
		 * @category Developer
494
		 */
495
496
		/** mshop/order/manager/base/service/decorators/excludes
497
		 * Excludes decorators added by the "common" option from the order base service manager
498
		 *
499
		 * Decorators extend the functionality of a class by adding new aspects
500
		 * (e.g. log what is currently done), executing the methods of the underlying
501
		 * class only in certain conditions (e.g. only for logged in users) or
502
		 * modify what is returned to the caller.
503
		 *
504
		 * This option allows you to remove a decorator added via
505
		 * "mshop/common/manager/decorators/default" before they are wrapped
506
		 * around the order base service manager.
507
		 *
508
		 *  mshop/order/manager/base/service/decorators/excludes = array( 'decorator1' )
509
		 *
510
		 * This would remove the decorator named "decorator1" from the list of
511
		 * common decorators ("\Aimeos\MShop\Common\Manager\Decorator\*") added via
512
		 * "mshop/common/manager/decorators/default" for the order base service manager.
513
		 *
514
		 * @param array List of decorator names
515
		 * @since 2014.03
516
		 * @category Developer
517
		 * @see mshop/common/manager/decorators/default
518
		 * @see mshop/order/manager/base/service/decorators/global
519
		 * @see mshop/order/manager/base/service/decorators/local
520
		 */
521
522
		/** mshop/order/manager/base/service/decorators/global
523
		 * Adds a list of globally available decorators only to the order base service manager
524
		 *
525
		 * Decorators extend the functionality of a class by adding new aspects
526
		 * (e.g. log what is currently done), executing the methods of the underlying
527
		 * class only in certain conditions (e.g. only for logged in users) or
528
		 * modify what is returned to the caller.
529
		 *
530
		 * This option allows you to wrap global decorators
531
		 * ("\Aimeos\MShop\Common\Manager\Decorator\*") around the order base
532
		 * service manager.
533
		 *
534
		 *  mshop/order/manager/base/service/decorators/global = array( 'decorator1' )
535
		 *
536
		 * This would add the decorator named "decorator1" defined by
537
		 * "\Aimeos\MShop\Common\Manager\Decorator\Decorator1" only to the order
538
		 * base service manager.
539
		 *
540
		 * @param array List of decorator names
541
		 * @since 2014.03
542
		 * @category Developer
543
		 * @see mshop/common/manager/decorators/default
544
		 * @see mshop/order/manager/base/service/decorators/excludes
545
		 * @see mshop/order/manager/base/service/decorators/local
546
		 */
547
548
		/** mshop/order/manager/base/service/decorators/local
549
		 * Adds a list of local decorators only to the order base service manager
550
		 *
551
		 * Decorators extend the functionality of a class by adding new aspects
552
		 * (e.g. log what is currently done), executing the methods of the underlying
553
		 * class only in certain conditions (e.g. only for logged in users) or
554
		 * modify what is returned to the caller.
555
		 *
556
		 * This option allows you to wrap local decorators
557
		 * ("\Aimeos\MShop\Order\Manager\Base\Service\Decorator\*") around the
558
		 * order base service manager.
559
		 *
560
		 *  mshop/order/manager/base/service/decorators/local = array( 'decorator2' )
561
		 *
562
		 * This would add the decorator named "decorator2" defined by
563
		 * "\Aimeos\MShop\Order\Manager\Base\Service\Decorator\Decorator2" only
564
		 * to the order base service manager.
565
		 *
566
		 * @param array List of decorator names
567
		 * @since 2014.03
568
		 * @category Developer
569
		 * @see mshop/common/manager/decorators/default
570
		 * @see mshop/order/manager/base/service/decorators/excludes
571
		 * @see mshop/order/manager/base/service/decorators/global
572
		 */
573
574
		return $this->getSubManagerBase( 'order', 'base/service/' . $manager, $name );
575
	}
576
577
578
	/**
579
	 * Adds or updates an order base service item to the storage.
580
	 *
581
	 * @param \Aimeos\MShop\Order\Item\Base\Service\Iface $item Order base service object
582
	 * @param bool $fetch True if the new ID should be returned in the item
583
	 * @return \Aimeos\MShop\Order\Item\Base\Service\Iface $item Updated item including the generated ID
584
	 */
585
	public function saveItem( \Aimeos\MShop\Order\Item\Base\Service\Iface $item, bool $fetch = true ) : \Aimeos\MShop\Order\Item\Base\Service\Iface
586
	{
587
		if( !$item->isModified() && !$item->getPrice()->isModified() ) {
588
			return $this->saveAttributeItems( $item, $fetch );
589
		}
590
591
		$context = $this->context();
592
		$conn = $context->db( $this->getResourceName() );
593
594
		$id = $item->getId();
595
		$price = $item->getPrice();
596
		$date = date( 'Y-m-d H:i:s' );
597
		$columns = $this->object()->getSaveAttributes();
598
599
		if( $id === null )
600
		{
601
			/** mshop/order/manager/base/service/insert/mysql
602
			 * Inserts a new order record into the database table
603
			 *
604
			 * @see mshop/order/manager/base/service/insert/ansi
605
			 */
606
607
			/** mshop/order/manager/base/service/insert/ansi
608
			 * Inserts a new order record into the database table
609
			 *
610
			 * Items with no ID yet (i.e. the ID is NULL) will be created in
611
			 * the database and the newly created ID retrieved afterwards
612
			 * using the "newid" SQL statement.
613
			 *
614
			 * The SQL statement must be a string suitable for being used as
615
			 * prepared statement. It must include question marks for binding
616
			 * the values from the order item to the statement before they are
617
			 * sent to the database server. The number of question marks must
618
			 * be the same as the number of columns listed in the INSERT
619
			 * statement. The order of the columns must correspond to the
620
			 * order in the save() method, so the correct values are
621
			 * bound to the columns.
622
			 *
623
			 * The SQL statement should conform to the ANSI standard to be
624
			 * compatible with most relational database systems. This also
625
			 * includes using double quotes for table and column names.
626
			 *
627
			 * @param string SQL statement for inserting records
628
			 * @since 2014.03
629
			 * @category Developer
630
			 * @see mshop/order/manager/base/service/update/ansi
631
			 * @see mshop/order/manager/base/service/newid/ansi
632
			 * @see mshop/order/manager/base/service/delete/ansi
633
			 * @see mshop/order/manager/base/service/search/ansi
634
			 * @see mshop/order/manager/base/service/count/ansi
635
			 */
636
			$path = 'mshop/order/manager/base/service/insert';
637
			$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

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