Passed
Push — master ( 7a5ddf...5d4f58 )
by Aimeos
10:45 queued 06:39
created

Base::getSessionLock()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nc 2
nop 1
dl 0
loc 15
rs 9.9332
c 0
b 0
f 0
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;
13
14
15
/**
16
 * Basic methods and constants for order base items (shopping basket).
17
 *
18
 * @package MShop
19
 * @subpackage Order
20
 */
21
abstract class Base
22
	extends \Aimeos\MShop\Common\Manager\Base
23
{
24
	/**
25
	 * Unlock basket.
26
	 * Disable the lock for the serialized basket in the session so
27
	 * modifications of the basket content are allowed again. Note that the
28
	 * locks are advisory locks that can't be enforced if code doesn't care
29
	 * about the lock.
30
	 */
31
	const LOCK_DISABLE = 0;
32
33
	/**
34
	 * Lock basket.
35
	 * Enable the lock for the serialized basket in the session so
36
	 * modifications of the basket content are not allowed any more. Note that
37
	 * the locks are advisory locks that can't be enforced if code doesn't care
38
	 * about the lock.
39
	 */
40
	const LOCK_ENABLE = 1;
41
42
43
	/**
44
	 * Returns a new and empty order base item (shopping basket).
45
	 *
46
	 * @param \Aimeos\MShop\Price\Item\Iface $price Default price of the basket (usually 0.00)
47
	 * @param \Aimeos\MShop\Locale\Item\Iface $locale Locale item containing the site, language and currency
48
	 * @param array $values Associative list of key/value pairs containing, e.g. the order or user ID
49
	 * @param \Aimeos\MShop\Order\Item\Base\Product\Iface[] $products List of ordered product items
50
	 * @param \Aimeos\MShop\Order\Item\Base\Address\Iface[] $addresses List of order address items
51
	 * @param \Aimeos\MShop\Order\Item\Base\Service\Iface[] $services List of order serviceitems
52
	 * @param \Aimeos\MShop\Order\Item\Base\Product\Iface[] $coupons Associative list of coupon codes as keys and items as values
53
	 * @param \Aimeos\MShop\Customer\Item\Iface|null $custItem Customer item object if requested
54
	 * @return \Aimeos\MShop\Order\Item\Base\Iface Order base object
55
	 */
56
	abstract protected function createItemBase( \Aimeos\MShop\Price\Item\Iface $price, \Aimeos\MShop\Locale\Item\Iface $locale,
57
		array $values = [], array $products = [], array $addresses = [], array $services = [], array $coupons = [],
58
		?\Aimeos\MShop\Customer\Item\Iface $custItem = null ) : \Aimeos\MShop\Order\Item\Base\Iface;
59
60
61
	/**
62
	 * Returns the current basket of the customer.
63
	 *
64
	 * @param string $type Basket type if a customer can have more than one basket
65
	 * @return \Aimeos\MShop\Order\Item\Base\Iface Shopping basket
66
	 */
67
	public function getSession( string $type = 'default' ) : \Aimeos\MShop\Order\Item\Base\Iface
68
	{
69
		$context = $this->context();
70
		$token = $context->token();
71
		$locale = $context->locale();
72
		$currency = $locale->getCurrencyId();
73
		$language = $locale->getLanguageId();
74
		$sitecode = $locale->getSiteItem()->getCode();
75
76
		$key = $token . '-' . $sitecode . '-' . $language . '-' . $currency . '-' . $type;
77
78
		try
79
		{
80
			if( ( $order = \Aimeos\MShop::create( $context, 'order/basket' )->get( $key )->getItem() ) === null ) {
81
				return $this->object()->create();
82
			}
83
84
			\Aimeos\MShop::create( $context, 'plugin' )->register( $order, 'order' );
85
		}
86
		catch( \Exception $e )
87
		{
88
			return $this->object()->create();
89
		}
90
91
		return $order;
92
	}
93
94
95
	/**
96
	 * Returns the current lock status of the basket.
97
	 *
98
	 * @param string $type Basket type if a customer can have more than one basket
99
	 * @return int Lock status (@see \Aimeos\MShop\Order\Manager\Base\Base)
100
	 */
101
	public function getSessionLock( string $type = 'default' ) : int
102
	{
103
		$context = $this->context();
104
		$session = $context->session();
105
		$locale = $context->locale();
106
		$currency = $locale->getCurrencyId();
107
		$language = $locale->getLanguageId();
108
		$sitecode = $locale->getSiteItem()->getCode();
109
		$key = 'aimeos/basket/lock-' . $sitecode . '-' . $language . '-' . $currency . '-' . strval( $type );
110
111
		if( ( $value = $session->get( $key ) ) !== null ) {
112
			return (int) $value;
113
		}
114
115
		return \Aimeos\MShop\Order\Manager\Base\Base::LOCK_DISABLE;
116
	}
117
118
119
	/**
120
	 * Saves the current shopping basket of the customer.
121
	 *
122
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $order Shopping basket
123
	 * @param string $type Order type if a customer can have more than one order at once
124
	 * @return \Aimeos\MShop\Order\Manager\Base\Iface Manager object for chaining method calls
125
	 */
126
	public function setSession( \Aimeos\MShop\Order\Item\Base\Iface $order, string $type = 'default' ) : \Aimeos\MShop\Order\Manager\Base\Iface
127
	{
128
		$context = $this->context();
129
		$token = $context->token();
130
		$locale = $context->locale();
131
		$currency = $locale->getCurrencyId();
132
		$language = $locale->getLanguageId();
133
		$sitecode = $locale->getSiteItem()->getCode();
134
135
		$key = $token . '-' . $sitecode . '-' . $language . '-' . $currency . '-' . strval( $type );
136
137
		$session = $context->session();
138
139
		$list = $session->get( 'aimeos/basket/list', [] );
140
		$list[$key] = $key;
141
142
		$session->set( 'aimeos/basket/list', $list );
143
144
		$manager = \Aimeos\MShop::create( $context, 'order/basket' );
145
		$manager->save( $manager->create()->setId( $key )->setCustomerId( $context->user() )->setItem( clone $order ) );
146
147
		return $this;
148
	}
149
150
151
	/**
152
	 * Locks or unlocks the session by setting the lock value.
153
	 * The lock is a cooperative lock and you have to check the lock value before you proceed.
154
	 *
155
	 * @param int $lock Lock value (@see \Aimeos\MShop\Order\Manager\Base\Base)
156
	 * @param string $type Order type if a customer can have more than one order at once
157
	 * @return \Aimeos\MShop\Order\Manager\Base\Iface Manager object for chaining method calls
158
	 */
159
	public function setSessionLock( int $lock, string $type = 'default' ) : \Aimeos\MShop\Order\Manager\Base\Iface
160
	{
161
		$this->checkLock( $lock );
162
163
		$context = $this->context();
164
		$session = $context->session();
165
		$locale = $context->locale();
166
		$currency = $locale->getCurrencyId();
167
		$language = $locale->getLanguageId();
168
		$sitecode = $locale->getSiteItem()->getCode();
169
		$key = 'aimeos/basket/lock-' . $sitecode . '-' . $language . '-' . $currency . '-' . strval( $type );
170
171
		$session->set( $key, strval( $lock ) );
172
173
		return $this;
174
	}
175
176
177
	/**
178
	 * Checks if the lock value is a valid constant.
179
	 *
180
	 * @param int $value Lock constant
181
	 * @return \Aimeos\MShop\Order\Manager\Base\Iface Manager object for chaining method calls
182
	 * @throws \Aimeos\MShop\Order\Exception If given value is invalid
183
	 */
184
	protected function checkLock( int $value ) : \Aimeos\MShop\Order\Manager\Base\Iface
185
	{
186
		switch( $value )
187
		{
188
			case \Aimeos\MShop\Order\Manager\Base\Base::LOCK_DISABLE:
189
			case \Aimeos\MShop\Order\Manager\Base\Base::LOCK_ENABLE:
190
				return $this;
191
		}
192
193
		$msg = $this->context()->translate( 'mshop', 'Lock flag "%1$d" not within allowed range' );
194
		throw new \Aimeos\MShop\Order\Exception( sprintf( $msg, $value ) );
195
	}
196
197
198
	/**
199
	 * Returns the address item map for the given order base IDs
200
	 *
201
	 * @param string[] $baseIds List of order base IDs
202
	 * @param bool $fresh Create new items by copying the existing ones and remove their IDs
203
	 * @return array Multi-dimensional associative list of order base IDs as keys and order address type/item pairs as values
204
	 */
205
	protected function getAddresses( array $baseIds, bool $fresh = false ) : array
206
	{
207
		$items = [];
208
		$manager = $this->object()->getSubManager( 'address' );
209
210
		$criteria = $manager->filter()->slice( 0, 0x7fffffff );
211
		$criteria->setConditions( $criteria->compare( '==', 'order.base.address.baseid', $baseIds ) );
212
213
		foreach( $manager->search( $criteria ) as $item )
214
		{
215
			if( $fresh === true )
216
			{
217
				$item->setBaseId( null );
218
				$item->setId( null );
219
			}
220
221
			$items[$item->getBaseId()][] = $item;
222
		}
223
224
		return $items;
225
	}
226
227
228
	/**
229
	 * Returns the coupon map for the given order base IDs
230
	 *
231
	 * @param string[] $baseIds List of order base IDs
232
	 * @param bool $fresh Create new items by copying the existing ones and remove their IDs
233
	 * @param array $products Associative list of base IDs and order product ID/item pairs as values
234
	 * @return array Multi-dimensional associative list of order base IDs as keys and coupons with product items as values
235
	 */
236
	protected function getCoupons( array $baseIds, bool $fresh = false, array $products = [] ) : array
237
	{
238
		$map = $productMap = [];
239
		$manager = $this->object()->getSubManager( 'coupon' );
240
241
		foreach( $products as $baseId => $list )
242
		{
243
			if( !isset( $productMap[$baseId] ) ) {
244
				$productMap[$baseId] = [];
245
			}
246
247
			foreach( $list as $key => $product )
248
			{
249
				$productMap[$baseId][$product->getId()] = $product;
250
251
				if( $fresh === true )
252
				{
253
					$product->setPosition( null );
254
					$product->setBaseId( null );
255
					$product->setId( null );
256
				}
257
			}
258
		}
259
260
		$criteria = $manager->filter()->slice( 0, 0x7fffffff );
261
		$criteria->setConditions( $criteria->compare( '==', 'order.base.coupon.baseid', $baseIds ) );
262
263
		foreach( $manager->search( $criteria ) as $item )
264
		{
265
			if( !isset( $map[$item->getBaseId()][$item->getCode()] ) ) {
266
				$map[$item->getBaseId()][$item->getCode()] = [];
267
			}
268
269
			if( $item->getProductId() !== null && isset( $productMap[$item->getBaseId()][$item->getProductId()] ) ) {
270
				$map[$item->getBaseId()][$item->getCode()][] = $productMap[$item->getBaseId()][$item->getProductId()];
271
			}
272
		}
273
274
		return $map;
275
	}
276
277
278
	/**
279
	 * Retrieves the ordered products from the storage.
280
	 *
281
	 * @param string[] $baseIds List of order base IDs
282
	 * @param bool $fresh Create new items by copying the existing ones and remove their IDs
283
	 * @return array Multi-dimensional associative list of order base IDs as keys and order product
284
	 *	IDs/items pairs in reversed order as values
285
	 */
286
	protected function getProducts( array $baseIds, bool $fresh = false ) : array
287
	{
288
		$map = $attributes = $subProducts = [];
289
		$manager = $this->object()->getSubManager( 'product' );
290
		$attrManager = $manager->getSubManager( 'attribute' );
291
292
		$criteria = $manager->filter()->slice( 0, 0x7fffffff );
293
		$criteria->setConditions( $criteria->compare( '==', 'order.base.product.baseid', $baseIds ) );
294
		$items = $manager->search( $criteria )->reverse();
295
296
		$search = $attrManager->filter()->slice( 0, 0x7fffffff );
297
		$search->setConditions( $search->compare( '==', 'order.base.product.attribute.parentid', $items->keys()->toArray() ) );
298
299
		foreach( $attrManager->search( $search ) as $id => $attribute )
300
		{
301
			if( $fresh === true )
302
			{
303
				$attributes[$attribute->getParentId()][] = $attribute;
304
				$attribute->setParentId( null );
305
				$attribute->setId( null );
306
			}
307
			else
308
			{
309
				$attributes[$attribute->getParentId()][$id] = $attribute;
310
			}
311
		}
312
313
		foreach( $items as $id => $item )
314
		{
315
			if( isset( $attributes[$id] ) ) {
316
				$item->setAttributeItems( $attributes[$id] );
317
			}
318
319
			if( $item->getOrderProductId() === null )
320
			{
321
				ksort( $subProducts ); // bring the array into the right order because it's reversed
322
				$item->setProducts( $subProducts );
323
				$map[$item->getBaseId()][$item->getPosition()] = $item;
324
325
				$subProducts = [];
326
			}
327
			else
328
			{	// in case it's a sub-product
329
				$subProducts[$item->getPosition()] = $item;
330
			}
331
332
			if( $fresh === true )
333
			{
334
				$item->setPosition( null );
335
				$item->setBaseId( null );
336
				$item->setId( null );
337
			}
338
		}
339
340
		foreach( $map as $key => $list ) {
341
			ksort( $map[$key] );
342
		}
343
344
		return $map;
345
	}
346
347
348
	/**
349
	 * Retrieves the order services from the storage.
350
	 *
351
	 * @param string[] $baseIds List of order base IDs
352
	 * @param bool $fresh Create new items by copying the existing ones and remove their IDs
353
	 * @return array Multi-dimensional associative list of order base IDs as keys and service type/items pairs as values
354
	 */
355
	protected function getServices( array $baseIds, bool $fresh = false ) : array
356
	{
357
		$map = [];
358
		$manager = $this->object()->getSubManager( 'service' );
359
360
		$criteria = $manager->filter()->slice( 0, 0x7fffffff );
361
		$criteria->setConditions( $criteria->compare( '==', 'order.base.service.baseid', $baseIds ) );
362
363
		foreach( $manager->search( $criteria ) as $item )
364
		{
365
			if( $fresh === true )
366
			{
367
				foreach( $item->getAttributeItems() as $attribute )
368
				{
369
						$attribute->setId( null );
370
						$attribute->setParentId( null );
371
				}
372
373
				$item->setBaseId( null );
374
				$item->setId( null );
375
			}
376
377
			$map[$item->getBaseId()][] = $item;
378
		}
379
380
		return $map;
381
	}
382
383
384
	/**
385
	 * Load the basket item for the given ID.
386
	 *
387
	 * @param string $id Unique order base ID
388
	 * @param \Aimeos\MShop\Price\Item\Iface $price Price object with total order value
389
	 * @param \Aimeos\MShop\Locale\Item\Iface $localeItem Locale object of the order
390
	 * @param array $row Array of values with all relevant order information
391
	 * @param array $ref Basket parts that should be loaded too
392
	 * @return \Aimeos\MShop\Order\Item\Base\Iface The loaded order item for the given ID
393
	 */
394
	protected function loadItems( string $id, \Aimeos\MShop\Price\Item\Iface $price,
395
		\Aimeos\MShop\Locale\Item\Iface $localeItem, array $row, array $ref )
396
	{
397
		$products = $coupons = $addresses = $services = [];
398
399
		if( in_array( 'order/base/product', $ref ) || in_array( 'order/base/coupon', $ref ) ) {
400
			$products = $this->loadProducts( $id, false );
401
		}
402
403
		if( in_array( 'order/base/coupon', $ref ) ) {
404
			$coupons = $this->loadCoupons( $id, false, $products );
405
		}
406
407
		if( in_array( 'order/base/address', $ref ) ) {
408
			$addresses = $this->loadAddresses( $id, false );
409
		}
410
411
		if( in_array( 'order/base/service', $ref ) ) {
412
			$services = $this->loadServices( $id, false );
413
		}
414
415
		$basket = $this->createItemBase( $price, $localeItem, $row, $products, $addresses, $services, $coupons );
416
417
		\Aimeos\MShop::create( $this->context(), 'plugin' )->register( $basket, 'order' );
418
419
		return $basket;
420
	}
421
422
423
	/**
424
	 * Create a new basket item as a clone from an existing order ID.
425
	 *
426
	 * @param string $id Unique order base ID
427
	 * @param \Aimeos\MShop\Price\Item\Iface $price Price object with total order value
428
	 * @param \Aimeos\MShop\Locale\Item\Iface $localeItem Locale object of the order
429
	 * @param array $row Array of values with all relevant order information
430
	 * @param array $ref Basket parts that should be loaded
431
	 * @return \Aimeos\MShop\Order\Item\Base\Standard The loaded order item for the given ID
432
	 */
433
	protected function loadFresh( string $id, \Aimeos\MShop\Price\Item\Iface $price,
434
		\Aimeos\MShop\Locale\Item\Iface $localeItem, array $row, array $ref )
435
	{
436
		$products = $coupons = $addresses = $services = [];
437
438
		if( in_array( 'order/base/product', $ref ) ) {
439
			$products = $this->loadProducts( $id, true );
440
		}
441
442
		if( in_array( 'order/base/coupon', $ref ) ) {
443
			// load coupons with product array containing product ids for coupon/product matching
444
			// not very efficient, a better solution might be considered for 2020.01 release
445
			// see https://github.com/aimeos/aimeos-core/pull/175 for discussion
446
			$coupons = $this->loadCoupons( $id, true, $this->loadProducts( $id, false ) );
447
		}
448
449
		if( in_array( 'order/base/address', $ref ) ) {
450
			$addresses = $this->loadAddresses( $id, true );
451
		}
452
453
		if( in_array( 'order/base/service', $ref ) ) {
454
			$services = $this->loadServices( $id, true );
455
		}
456
457
		$basket = $this->createItemBase( $price, $localeItem, $row );
458
		$basket->setId( null );
459
460
		\Aimeos\MShop::create( $this->context(), 'plugin' )->register( $basket, 'order' );
461
462
		foreach( $services as $item ) {
463
			$basket->addService( $item, $item->getType() );
464
		}
465
466
		foreach( $addresses as $item ) {
467
			$basket->addAddress( $item, $item->getType() );
468
		}
469
470
		foreach( $products as $item )
471
		{
472
			if( !( $item->getFlags() & \Aimeos\MShop\Order\Item\Base\Product\Base::FLAG_IMMUTABLE ) ) {
473
				$basket->addProduct( $item );
474
			}
475
		}
476
477
		foreach( $coupons as $code => $items ) {
478
			$basket->addCoupon( $code );
479
		}
480
481
		return $basket;
482
	}
483
484
485
	/**
486
	 * Retrieves the addresses of the order from the storage.
487
	 *
488
	 * @param string $id Order base ID
489
	 * @param bool $fresh Create new items by copying the existing ones and remove their IDs
490
	 * @return \Aimeos\MShop\Order\Item\Base\Address\Iface[] List of order address items
491
	 */
492
	protected function loadAddresses( string $id, bool $fresh ) : array
493
	{
494
		$map = $this->getAddresses( [$id], $fresh );
495
496
		if( ( $items = reset( $map ) ) !== false ) {
497
			return $items;
498
		}
499
500
		return [];
501
	}
502
503
504
	/**
505
	 * Retrieves the coupons of the order from the storage.
506
	 *
507
	 * @param string $id Order base ID
508
	 * @param bool $fresh Create new items by copying the existing ones and remove their IDs
509
	 * @param array $products Multi-dimensional associative list of order base IDs as keys and order product
510
	 *	IDs/items pairs in reversed order as values
511
	 * @return \Aimeos\MShop\Order\Item\Base\Product\Iface[] Associative list of coupon codes as keys and items as values
512
	 */
513
	protected function loadCoupons( string $id, bool $fresh, array $products ) : array
514
	{
515
		$map = $this->getCoupons( [$id], $fresh, [$id => $products] );
516
517
		if( ( $items = reset( $map ) ) !== false ) {
518
			return $items;
519
		}
520
521
		return [];
522
	}
523
524
525
	/**
526
	 * Retrieves the ordered products from the storage.
527
	 *
528
	 * @param string $id Order base ID
529
	 * @param bool $fresh Create new items by copying the existing ones and remove their IDs
530
	 * @return \Aimeos\MShop\Order\Item\Base\Product\Iface[] List of product items
531
	 */
532
	protected function loadProducts( string $id, bool $fresh ) : array
533
	{
534
		$items = current( $this->getProducts( [$id], $fresh ) );
535
		return $items ?: [];
536
	}
537
538
539
	/**
540
	 * Retrieves the services of the order from the storage.
541
	 *
542
	 * @param string $id Order base ID
543
	 * @param bool $fresh Create new items by copying the existing ones and remove their IDs
544
	 * @return \Aimeos\MShop\Order\Item\Base\Service\Iface[] List of order service items
545
	 */
546
	protected function loadServices( string $id, bool $fresh ) : array
547
	{
548
		$map = $this->getServices( [$id], $fresh );
549
550
		if( ( $items = reset( $map ) ) !== false ) {
551
			return $items;
552
		}
553
554
		return [];
555
	}
556
557
558
	/**
559
	 * Saves the addresses of the order to the storage.
560
	 *
561
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $basket Basket containing address items
562
	 * @return \Aimeos\MShop\Order\Manager\Base\Iface Manager object for chaining method calls
563
	 */
564
	protected function saveAddresses( \Aimeos\MShop\Order\Item\Base\Iface $basket ) : \Aimeos\MShop\Order\Manager\Base\Iface
565
	{
566
		$addresses = $basket->getAddresses()->flat( 1 );
567
568
		foreach( $addresses as $address )
569
		{
570
			if( $address->getBaseId() != $basket->getId() ) {
571
				$address->setId( null ); // create new item if copied
572
			}
573
574
			$address->setBaseId( $basket->getId() );
575
		}
576
577
		$this->object()->getSubManager( 'address' )->save( $addresses );
578
579
		return $this;
580
	}
581
582
583
	/**
584
	 * Saves the coupons of the order to the storage.
585
	 *
586
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $basket Basket containing coupon items
587
	 * @return \Aimeos\MShop\Order\Manager\Base\Iface Manager object for chaining method calls
588
	 */
589
	protected function saveCoupons( \Aimeos\MShop\Order\Item\Base\Iface $basket ) : \Aimeos\MShop\Order\Manager\Base\Iface
590
	{
591
		$baseId = $basket->getId();
592
		$manager = $this->object()->getSubManager( 'coupon' );
593
		$filter = $manager->filter()->add( 'order.base.coupon.baseid', '==', $basket->getId() )->slice( 0, 0x7fffffff );
594
		$items = $manager->search( $filter )->groupBy( 'order.base.coupon.code' );
595
596
		foreach( $basket->getCoupons() as $code => $products )
597
		{
598
			if( empty( $products ) )
599
			{
600
				$item = !empty( $items[$code] ) ? current( $items[$code] ) : $manager->create()->setBaseId( $baseId );
0 ignored issues
show
Bug introduced by
It seems like $items[$code] can also be of type null; however, parameter $array of current() does only seem to accept array|object, 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

600
				$item = !empty( $items[$code] ) ? current( /** @scrutinizer ignore-type */ $items[$code] ) : $manager->create()->setBaseId( $baseId );
Loading history...
601
				$manager->save( $item->setCode( $code ) );
602
				continue;
603
			}
604
605
			foreach( $products as $product )
606
			{
607
				foreach( $items[$code] ?? [] as $prodItem )
608
				{
609
					if( $product->getId() === $prodItem->getId() ) {
610
						continue 2;
611
					}
612
				}
613
614
				$manager->save( $manager->create()->setBaseId( $baseId )->setCode( $code )->setProductId( $product->getId() ) );
615
			}
616
		}
617
618
		return $this;
619
	}
620
621
622
	/**
623
	 * Saves the ordered products to the storage.
624
	 *
625
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $basket Basket containing ordered products or bundles
626
	 * @return \Aimeos\MShop\Order\Manager\Base\Iface Manager object for chaining method calls
627
	 */
628
	protected function saveProducts( \Aimeos\MShop\Order\Item\Base\Iface $basket ) : \Aimeos\MShop\Order\Manager\Base\Iface
629
	{
630
		$products = $basket->getProducts();
631
		$pos = $products->merge( $products->getProducts()->flat( 1 ) )->max( 'order.base.product.position' );
632
633
		foreach( $products as $product )
634
		{
635
			if( $product->getBaseId() != $basket->getId() ) {
636
				$product->setId( null ); // create new item if copied
637
			}
638
639
			if( !$product->getPosition() ) {
640
				$product->setPosition( ++$pos );
641
			}
642
643
			$product->setBaseId( $basket->getId() );
644
645
			foreach( $product->getProducts() as $subProduct )
646
			{
647
				if( $subProduct->getBaseId() != $basket->getId() ) {
648
					$subProduct->setId( null ); // create new item if copied
649
				}
650
651
				if( !$subProduct->getPosition() ) {
652
					$subProduct->setPosition( ++$pos );
653
				}
654
655
				$subProduct->setBaseId( $basket->getId() );
656
			}
657
		}
658
659
		$this->object()->getSubManager( 'product' )->save( $products );
660
661
		return $this;
662
	}
663
664
665
	/**
666
	 * Saves the services of the order to the storage.
667
	 *
668
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $basket Basket containing service items
669
	 * @return \Aimeos\MShop\Order\Manager\Base\Iface Manager object for chaining method calls
670
	 */
671
	protected function saveServices( \Aimeos\MShop\Order\Item\Base\Iface $basket ) : \Aimeos\MShop\Order\Manager\Base\Iface
672
	{
673
		$services = $basket->getServices()->flat( 1 );
674
675
		foreach( $services as $service )
676
		{
677
			if( $service->getBaseId() != $basket->getId() ) {
678
				$service->setId( null ); // create new item if copied
679
			}
680
681
			$service->setBaseId( $basket->getId() );
682
		}
683
684
		$this->object()->getSubManager( 'service' )->save( $services );
685
686
		return $this;
687
	}
688
}
689