Passed
Push — master ( 8673f8...5c56ce )
by Aimeos
04:42
created

Standard::saveAttributeItems()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 10
c 0
b 0
f 0
dl 0
loc 21
rs 9.9332
cc 4
nc 4
nop 2
1
<?php
2
3
/**
4
 * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2015-2024
6
 * @package MShop
7
 * @subpackage Order
8
 */
9
10
11
namespace Aimeos\MShop\Order\Manager\Service;
12
13
14
/**
15
 * Default order service manager implementation.
16
 *
17
 * @package MShop
18
 * @subpackage Order
19
 */
20
class Standard
21
	extends \Aimeos\MShop\Common\Manager\Base
22
	implements \Aimeos\MShop\Order\Manager\Service\Iface, \Aimeos\MShop\Common\Manager\Factory\Iface
23
{
24
	private array $searchConfig = [
25
		'order.service.parentid' => [
26
			'label' => 'Order ID',
27
			'internalcode' => 'parentid',
28
			'type' => 'int',
29
			'public' => false,
30
		],
31
		'order.service.serviceid' => [
32
			'label' => 'Service original service ID',
33
			'internalcode' => 'servid',
34
			'public' => false,
35
		],
36
		'order.service.name' => [
37
			'label' => 'Service name',
38
			'internalcode' => 'name',
39
		],
40
		'order.service.code' => [
41
			'label' => 'Service code',
42
			'internalcode' => 'code',
43
		],
44
		'order.service.type' => [
45
			'label' => 'Service type',
46
			'internalcode' => 'type',
47
		],
48
		'order.service.mediaurl' => [
49
			'label' => 'Service media url',
50
			'internalcode' => 'mediaurl',
51
			'public' => false,
52
		],
53
		'order.service.position' => [
54
			'label' => 'Service position',
55
			'internalcode' => 'pos',
56
			'type' => 'int',
57
			'public' => false,
58
		],
59
	];
60
61
62
	/**
63
	 * Counts the number items that are available for the values of the given key.
64
	 *
65
	 * @param \Aimeos\Base\Criteria\Iface $search Search criteria
66
	 * @param array|string $key Search key or list of keys to aggregate items for
67
	 * @param string|null $value Search key for aggregating the value column
68
	 * @param string|null $type Type of the aggregation, empty string for count or "sum" or "avg" (average)
69
	 * @return \Aimeos\Map List of the search keys as key and the number of counted items as value
70
	 */
71
	public function aggregate( \Aimeos\Base\Criteria\Iface $search, $key, string $value = null, string $type = null ) : \Aimeos\Map
72
	{
73
		/** mshop/order/manager/service/aggregate/mysql
74
		 * Counts the number of records grouped by the values in the key column and matched by the given criteria
75
		 *
76
		 * @see mshop/order/manager/service/aggregate/ansi
77
		 */
78
79
		/** mshop/order/manager/service/aggregate/ansi
80
		 * Counts the number of records grouped by the values in the key column and matched by the given criteria
81
		 *
82
		 * Groups all records by the values in the key column and counts their
83
		 * occurence. The matched records can be limited by the given criteria
84
		 * from the order database. The records must be from one of the sites
85
		 * that are configured via the context item. If the current site is part
86
		 * of a tree of sites, the statement can count all records from the
87
		 * current site and the complete sub-tree of sites.
88
		 *
89
		 * As the records can normally be limited by criteria from sub-managers,
90
		 * their tables must be joined in the SQL context. This is done by
91
		 * using the "internaldeps" property from the definition of the ID
92
		 * column of the sub-managers. These internal dependencies specify
93
		 * the JOIN between the tables and the used columns for joining. The
94
		 * ":joins" placeholder is then replaced by the JOIN strings from
95
		 * the sub-managers.
96
		 *
97
		 * To limit the records matched, conditions can be added to the given
98
		 * criteria object. It can contain comparisons like column names that
99
		 * must match specific values which can be combined by AND, OR or NOT
100
		 * operators. The resulting string of SQL conditions replaces the
101
		 * ":cond" placeholder before the statement is sent to the database
102
		 * server.
103
		 *
104
		 * This statement doesn't return any records. Instead, it returns pairs
105
		 * of the different values found in the key column together with the
106
		 * number of records that have been found for that key values.
107
		 *
108
		 * The SQL statement should conform to the ANSI standard to be
109
		 * compatible with most relational database systems. This also
110
		 * includes using double quotes for table and column names.
111
		 *
112
		 * @param string SQL statement for aggregating order items
113
		 * @since 2014.09
114
		 * @see mshop/order/manager/service/insert/ansi
115
		 * @see mshop/order/manager/service/update/ansi
116
		 * @see mshop/order/manager/service/newid/ansi
117
		 * @see mshop/order/manager/service/delete/ansi
118
		 * @see mshop/order/manager/service/search/ansi
119
		 * @see mshop/order/manager/service/count/ansi
120
		 */
121
122
		/** mshop/order/manager/service/aggregateavg/mysql
123
		 * Computes the average of all values grouped by the key column and matched by the given criteria
124
		 *
125
		 * @param string SQL statement for aggregating the order service items and computing the average value
126
		 * @since 2017.10
127
		 * @see mshop/order/manager/service/aggregateavg/ansi
128
		 * @see mshop/order/manager/service/aggregate/mysql
129
		 */
130
131
		/** mshop/order/manager/service/aggregateavg/ansi
132
		 * Computes the average of all values grouped by the key column and matched by the given criteria
133
		 *
134
		 * @param string SQL statement for aggregating the order service items and computing the average value
135
		 * @since 2017.10
136
		 * @see mshop/order/manager/service/aggregate/ansi
137
		 */
138
139
		/** mshop/order/manager/service/aggregatesum/mysql
140
		 * Computes the sum of all values grouped by the key column and matched by the given criteria
141
		 *
142
		 * @param string SQL statement for aggregating the order service items and computing the sum
143
		 * @since 2017.10
144
		 * @see mshop/order/manager/service/aggregatesum/ansi
145
		 * @see mshop/order/manager/service/aggregate/mysql
146
		 */
147
148
		/** mshop/order/manager/service/aggregatesum/ansi
149
		 * Computes the sum of all values grouped by the key column and matched by the given criteria
150
		 *
151
		 * @param string SQL statement for aggregating the order service items and computing the sum
152
		 * @since 2017.10
153
		 * @see mshop/order/manager/service/aggregate/ansi
154
		 */
155
156
		$cfgkey = 'mshop/order/manager/service/aggregate';
157
		return $this->aggregateBase( $search, $key, $cfgkey, ['order.service'], $value, $type );
158
	}
159
160
161
	/**
162
	 * Creates a new empty item instance
163
	 *
164
	 * @param array $values Values the item should be initialized with
165
	 * @return \Aimeos\MShop\Order\Item\Service\Iface New order service item object
166
	 */
167
	public function create( array $values = [] ) : \Aimeos\MShop\Common\Item\Iface
168
	{
169
		$context = $this->context();
170
171
		$values['.price'] = $values['.price'] ?? \Aimeos\MShop::create( $context, 'price' )->create();
172
		$values['order.service.siteid'] = $values['order.service.siteid'] ?? $context->locale()->getSiteId();
173
174
		return new \Aimeos\MShop\Order\Item\Service\Standard( 'order.service.', $values );
175
	}
176
177
178
	/**
179
	 * Creates a new order service attribute item instance
180
	 *
181
	 * @param array $values Values the item should be initialized with
182
	 * @return \Aimeos\MShop\Order\Item\Service\Attribute\Iface New order service attribute item object
183
	 */
184
	public function createAttributeItem( array $values = [] ) : \Aimeos\MShop\Common\Item\Iface
185
	{
186
		return $this->object()->getSubManager( 'attribute' )->create( $values );
187
	}
188
189
190
	/**
191
	 * Creates a new order service transaction item instance
192
	 *
193
	 * @param array $values Values the item should be initialized with
194
	 * @return \Aimeos\MShop\Order\Item\Service\Transaction\Iface New order service transaction item object
195
	 */
196
	public function createTransaction( array $values = [] ) : \Aimeos\MShop\Common\Item\Iface
197
	{
198
		return $this->object()->getSubManager( 'transaction' )->create( $values );
199
	}
200
201
202
	/**
203
	 * Creates a filter object.
204
	 *
205
	 * @param bool|null $default Add default criteria or NULL for relaxed default criteria
206
	 * @param bool $site TRUE for adding site criteria to limit items by the site of related items
207
	 * @return \Aimeos\Base\Criteria\Iface Returns the filter object
208
	 */
209
	public function filter( ?bool $default = false, bool $site = false ) : \Aimeos\Base\Criteria\Iface
210
	{
211
		return parent::filter( $default )->order( 'order.service.id' );
212
	}
213
214
215
	/**
216
	 * Returns the additional column/search definitions
217
	 *
218
	 * @return array Associative list of column names as keys and items implementing \Aimeos\Base\Criteria\Attribute\Iface
219
	 */
220
	public function getSaveAttributes() : array
221
	{
222
		return $this->createAttributes( $this->searchConfig );
223
	}
224
225
226
	/**
227
	 * Returns the attributes that can be used for searching.
228
	 *
229
	 * @param bool $withsub Return also attributes of sub-managers if true
230
	 * @return \Aimeos\Base\Criteria\Attribute\Iface[] List of search attribute items
231
	 */
232
	public function getSearchAttributes( bool $withsub = true ) : array
233
	{
234
		return array_replace( parent::getSearchAttributes( $withsub ), $this->createAttributes( [
235
			'order.service.id' => [
236
				'label' => 'Service ID',
237
				'internaldeps' => ['LEFT JOIN "mshop_order_service" AS mordse ON ( mord."id" = mordse."parentid" )'],
238
				'internalcode' => 'id',
239
				'type' => 'int',
240
				'public' => false,
241
			],
242
			'order.service.currencyid' => [
243
				'label' => 'Service currencyid code',
244
				'internalcode' => 'currencyid',
245
			],
246
			'order.service.price' => [
247
				'label' => 'Service price',
248
				'internalcode' => 'price',
249
				'type' => 'decimal',
250
			],
251
			'order.service.costs' => [
252
				'label' => 'Service shipping',
253
				'internalcode' => 'costs',
254
				'type' => 'decimal',
255
			],
256
			'order.service.rebate' => [
257
				'label' => 'Service rebate',
258
				'internalcode' => 'rebate',
259
				'type' => 'decimal',
260
			],
261
			'order.service.taxrates' => [
262
				'label' => 'Service taxrates',
263
				'internalcode' => 'taxrate',
264
				'type' => 'json',
265
			],
266
			'order.service.taxvalue' => [
267
				'label' => 'Service tax value',
268
				'internalcode' => 'tax',
269
				'type' => 'decimal',
270
			],
271
			'order.service.taxflag' => [
272
				'label' => 'Service tax flag (0=net, 1=gross)',
273
				'internalcode' => 'taxflag',
274
				'type' => 'int',
275
			],
276
		] ) );
277
	}
278
279
280
	/**
281
	 * Saves the dependent items of the item
282
	 *
283
	 * @param \Aimeos\MShop\Common\Item\Iface $item Item object
284
	 * @param bool $fetch True if the new ID should be returned in the item
285
	 * @return \Aimeos\MShop\Common\Item\Iface Updated item
286
	 */
287
	public function saveRefs( \Aimeos\MShop\Common\Item\Iface $item, bool $fetch = true ) : \Aimeos\MShop\Common\Item\Iface
288
	{
289
		$this->saveAttributeItems( $item, $fetch );
290
		$this->saveTransactions( $item, $fetch );
291
292
		return $item;
293
	}
294
295
296
	/**
297
	 * Merges the data from the given map and the referenced items
298
	 *
299
	 * @param array $entries Associative list of ID as key and the associative list of property key/value pairs as values
300
	 * @param array $ref List of referenced items to fetch and add to the entries
301
	 * @return array Associative list of ID as key and the updated entries as value
302
	 */
303
	public function searchRefs( array $entries, array $ref ) : array
304
	{
305
		$servItems = [];
306
		$parentIds = array_keys( $entries );
307
		$manager = \Aimeos\MShop::create( $this->context(), 'price' );
308
309
		$attributes = $this->getAttributeItems( $parentIds );
310
		$transactions = $this->getTransactions( $parentIds );
311
312
		if( $this->hasRef( $ref, 'service' ) ) {
313
			$servItems = $this->getServiceItems( map( $entries )->col( 'order.service.serviceid' )->filter()->all(), $ref );
314
		}
315
316
		foreach( $entries as $id => $row )
317
		{
318
			$entries[$id]['.price'] = $manager->create( [
319
				'price.currencyid' => $row['order.service.currencyid'],
320
				'price.taxrates' => $row['order.service.taxrates'],
321
				'price.value' => $row['order.service.price'],
322
				'price.costs' => $row['order.service.costs'],
323
				'price.rebate' => $row['order.service.rebate'],
324
				'price.taxflag' => $row['order.service.taxflag'],
325
				'price.taxvalue' => $row['order.service.taxvalue'],
326
				'price.siteid' => $row['order.service.siteid'],
327
			] );
328
329
			$entries[$id]['.service'] = $servItems[$row['order.service.serviceid']] ?? null;
330
			$entries[$id]['.transactions'] = $transactions[$id] ?? map();
331
			$entries[$id]['.attributes'] = $attributes[$id] ?? map();
332
		}
333
334
		return $entries;
335
	}
336
337
338
	/**
339
	 * Binds additional values to the statement before execution.
340
	 *
341
	 * @param \Aimeos\MShop\Common\Item\Iface $item Item object
342
	 * @param \Aimeos\Base\DB\Statement\Iface $stmt Database statement object
343
	 * @param int $idx Current bind index
344
	 * @return \Aimeos\Base\DB\Statement\Iface Database statement object with bound values
345
	 */
346
	protected function bind( \Aimeos\MShop\Common\Item\Iface $item, \Aimeos\Base\DB\Statement\Iface $stmt, int &$idx ) : \Aimeos\Base\DB\Statement\Iface
347
	{
348
		$price = $item->getPrice();
349
350
		$stmt->bind( $idx++, $price->getCurrencyId() );
351
		$stmt->bind( $idx++, $price->getValue() );
352
		$stmt->bind( $idx++, $price->getCosts() );
353
		$stmt->bind( $idx++, $price->getRebate() );
354
		$stmt->bind( $idx++, $price->getTaxValue() );
355
		$stmt->bind( $idx++, json_encode( $price->getTaxRates(), JSON_FORCE_OBJECT ) );
356
		$stmt->bind( $idx++, $price->getTaxFlag(), \Aimeos\Base\DB\Statement\Base::PARAM_INT );
357
358
		return $stmt;
359
	}
360
361
362
	/**
363
	 * Searches for attribute items connected with order service item.
364
	 *
365
	 * @param string[] $ids List of order service item IDs
366
	 * @return \Aimeos\Map Associative list of order service IDs as keys and order service attribute items
367
	 *  implementing \Aimeos\MShop\Order\Item\Service\Attribute\Iface as values
368
	 */
369
	protected function getAttributeItems( array $ids ) : \Aimeos\Map
370
	{
371
		$manager = $this->object()->getSubManager( 'attribute' );
372
		$filter = $manager->filter()->add( 'order.service.attribute.parentid', '==', $ids )->slice( 0, 0x7fffffff );
373
374
		return $manager->search( $filter )->groupBy( 'order.service.attribute.parentid' );
375
	}
376
377
378
	/**
379
	 * Fetches service items connected with order service item.
380
	 *
381
	 * @param string[] $ids List of order service item IDs
382
	 * @return \Aimeos\Map Associative list of order service IDs as keys and order service attribute items
383
	 *  implementing \Aimeos\MShop\Order\Item\Service\Attribute\Iface as values
384
	 */
385
	protected function getServiceItems( array $ids, array $ref ) : \Aimeos\Map
386
	{
387
		$manager = \Aimeos\MShop::create( $this->context(), 'service' );
388
		$search = $manager->filter()->add( 'service.id', '==', array_unique( $ids ) )->slice( 0, 0x7fffffff );
389
390
		return $manager->search( $search, $ref );
391
	}
392
393
394
	/**
395
	 * Searches for transaction items connected with order service item.
396
	 *
397
	 * @param string[] $ids List of order service item IDs
398
	 * @return \Aimeos\Map Associative list of order service IDs as keys and order service transaction items
399
	 *  implementing \Aimeos\MShop\Order\Item\Service\Transaction\Iface as values
400
	 */
401
	protected function getTransactions( array $ids ) : \Aimeos\Map
402
	{
403
		$manager = $this->object()->getSubManager( 'transaction' );
404
		$filter = $manager->filter()->add( 'order.service.transaction.parentid', '==', $ids )->slice( 0, 0x7fffffff );
405
406
		return $manager->search( $filter )->groupBy( 'order.service.attribute.parentid' );
407
	}
408
409
410
	/**
411
	 * Checks if the item is modified
412
	 *
413
	 * @param \Aimeos\MShop\Common\Item\Iface $item Item object
414
	 * @return bool True if the item is modified, false if not
415
	 */
416
	protected function isModified( \Aimeos\MShop\Common\Item\Iface $item ) : bool
417
	{
418
		return $item->isModified() || $item->getPrice()->isModified();
419
	}
420
421
422
	/**
423
	 * Returns the prefix for the item properties and search keys.
424
	 *
425
	 * @return string Prefix for the item properties and search keys
426
	 */
427
	protected function prefix() : string
428
	{
429
		return 'order.service.';
430
	}
431
432
433
	/**
434
	 * Saves the attribute items included in the order service item
435
	 *
436
	 * @param \Aimeos\MShop\Order\Item\Service\Iface $item Order service item with attribute items
437
	 * @param bool $fetch True if the new ID should be set in the attribute item
438
	 * @return \Aimeos\MShop\Order\Item\Service\Iface Object with saved attribute items and IDs
439
	 */
440
	protected function saveAttributeItems( \Aimeos\MShop\Order\Item\Service\Iface $item, bool $fetch ) : \Aimeos\MShop\Order\Item\Service\Iface
441
	{
442
		$attrItems = $item->getAttributeItems();
443
444
		foreach( $attrItems as $key => $attrItem )
445
		{
446
			if( $attrItem->getType() === 'session' )
447
			{
448
				unset( $attrItems[$key] );
449
				continue;
450
			}
451
452
			if( $attrItem->getParentId() != $item->getId() ) {
453
				$attrItem->setId( null ); // create new property item if copied
454
			}
455
456
			$attrItem->setParentId( $item->getId() );
457
		}
458
459
		$this->object()->getSubManager( 'attribute' )->save( $attrItems, $fetch );
460
		return $item;
461
	}
462
463
464
	/**
465
	 * Saves the transaction items included in the order service item
466
	 *
467
	 * @param \Aimeos\MShop\Order\Item\Service\Iface $item Order service item with transaction items
468
	 * @param bool $fetch True if the new ID should be set in the transaction item
469
	 * @return \Aimeos\MShop\Order\Item\Service\Iface Object with saved transaction items and IDs
470
	 */
471
	protected function saveTransactions( \Aimeos\MShop\Order\Item\Service\Iface $item, bool $fetch ) : \Aimeos\MShop\Order\Item\Service\Iface
472
	{
473
		$list = $item->getTransactions();
474
475
		foreach( $list as $key => $txItem )
476
		{
477
			if( $txItem->getParentId() != $item->getId() ) {
478
				$txItem->setId( null ); // create new property item if copied
479
			}
480
481
			$txItem->setParentId( $item->getId() );
482
		}
483
484
		$this->object()->getSubManager( 'transaction' )->save( $list, $fetch );
485
		return $item;
486
	}
487
}
488