Passed
Push — master ( 938ca6...9526de )
by Aimeos
03:43
created

Select   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 181
Duplicated Lines 0 %

Importance

Changes 6
Bugs 0 Features 0
Metric Value
wmc 18
eloc 70
c 6
b 0
f 0
dl 0
loc 181
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
B addProduct() 0 58 6
A updateProduct() 0 33 4
B getArticle() 0 51 8
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2017-2020
6
 * @package Controller
7
 * @subpackage Frontend
8
 */
9
10
11
namespace Aimeos\Controller\Frontend\Basket\Decorator;
12
13
14
/**
15
 * Selection product handling
16
 *
17
 * @package Controller
18
 * @subpackage Frontend
19
 */
20
class Select
21
	extends \Aimeos\Controller\Frontend\Basket\Decorator\Base
22
	implements \Aimeos\Controller\Frontend\Basket\Iface, \Aimeos\Controller\Frontend\Common\Decorator\Iface
23
{
24
	/**
25
	 * Adds a product to the basket of the customer stored in the session
26
	 *
27
	 * @param \Aimeos\MShop\Product\Item\Iface $product Product to add including texts, media, prices, attributes, etc.
28
	 * @param float $quantity Amount of products that should by added
29
	 * @param array $variant List of variant-building attribute IDs that identify an article in a selection product
30
	 * @param array $config List of configurable attribute IDs the customer has chosen from
31
	 * @param array $custom Associative list of attribute IDs as keys and arbitrary values that will be added to the ordered product
32
	 * @param string $stocktype Unique code of the stock type to deliver the products from
33
	 * @param string $supplier Unique supplier code the product is from
34
	 * @param string|null $siteid Unique site ID the product is from or null for siteid of the product item
35
	 * @return \Aimeos\Controller\Frontend\Basket\Iface Basket frontend object for fluent interface
36
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception If the product isn't available
37
	 */
38
	public function addProduct( \Aimeos\MShop\Product\Item\Iface $product,
39
		float $quantity = 1, array $variant = [], array $config = [], array $custom = [],
40
		string $stocktype = 'default', string $supplier = '', string $siteid = null ) : \Aimeos\Controller\Frontend\Basket\Iface
41
	{
42
		if( $product->getType() !== 'select' )
43
		{
44
			$this->getController()->addProduct( $product, $quantity, $variant, $config, $custom, $stocktype, $supplier, $siteid );
45
			return $this;
46
		}
47
48
		$attr = [];
49
		$quantity = $this->checkQuantity( $product, $quantity );
50
		$prices = $product->getRefItems( 'price', 'default', 'default' );
51
		$hidden = $product->getRefItems( 'attribute', null, 'hidden' );
52
53
		$orderBaseProductItem = \Aimeos\MShop::create( $this->getContext(), 'order/base/product' )->create();
54
		$orderBaseProductItem = $orderBaseProductItem->copyFrom( $product );
55
56
		$productItem = $this->getArticle( $product, $variant );
57
		$orderBaseProductItem->setProductCode( $productItem->getCode() );
58
59
		$this->checkAttributes( [$product, $productItem], 'custom', array_keys( $custom ) );
60
		$this->checkAttributes( [$product, $productItem], 'config', array_keys( $config ) );
61
62
		if( !( $subprices = $productItem->getRefItems( 'price', 'default', 'default' ) )->isEmpty() ) {
63
			$prices = $subprices;
64
		}
65
66
		if( $mediaItem = $productItem->getRefItems( 'media', 'default', 'default' )->first() ) {
67
			$orderBaseProductItem->setMediaUrl( $mediaItem->getPreview() );
68
		}
69
70
		$hidden->union( $productItem->getRefItems( 'attribute', null, 'hidden' ) );
71
72
		$orderProductAttrManager = \Aimeos\MShop::create( $this->getContext(), 'order/base/product/attribute' );
73
		$attributes = $productItem->getRefItems( 'attribute', null, 'variant' );
74
75
		foreach( $this->getAttributes( $attributes->keys()->toArray(), ['text'] ) as $attrItem ) {
76
			$attr[] = $orderProductAttrManager->create()->copyFrom( $attrItem )->setType( 'variant' );
77
		}
78
79
		$custAttr = $this->getOrderProductAttributes( 'custom', array_keys( $custom ), $custom );
80
		$confAttr = $this->getOrderProductAttributes( 'config', array_keys( $config ), [], $config );
81
		$hideAttr = $this->getOrderProductAttributes( 'hidden', $hidden->keys()->toArray() );
82
83
		$orderBaseProductItem = $orderBaseProductItem->setQuantity( $quantity )
84
			->setAttributeItems( array_merge( $attr, $custAttr, $confAttr, $hideAttr ) )
85
			->setPrice( $this->calcPrice( $orderBaseProductItem, $prices, $quantity ) )
86
			->setStockType( $stocktype )->setSupplierCode( $supplier );
87
88
		if( $siteid ) {
89
			$orderBaseProductItem->setSiteId( $siteid );
90
		}
91
92
		$this->getController()->get()->addProduct( $orderBaseProductItem );
93
		$this->getController()->save();
94
95
		return $this;
96
	}
97
98
99
	/**
100
	 * Edits the quantity of a product item in the basket.
101
	 *
102
	 * @param int $position Position number (key) of the order product item
103
	 * @param float $quantity New quantiy of the product item
104
	 * @return \Aimeos\Controller\Frontend\Basket\Iface Basket frontend object for fluent interface
105
	 */
106
	public function updateProduct( int $position, float $quantity ) : \Aimeos\Controller\Frontend\Basket\Iface
107
	{
108
		$orderProduct = $this->get()->getProduct( $position );
109
110
		if( $orderProduct->getType() !== 'select' )
111
		{
112
			$this->getController()->updateProduct( $position, $quantity );
113
			return $this;
114
		}
115
116
		if( $orderProduct->getFlags() & \Aimeos\MShop\Order\Item\Base\Product\Base::FLAG_IMMUTABLE )
117
		{
118
			$msg = $this->getContext()->getI18n()->dt( 'controller/frontend', 'Basket item at position "%1$d" cannot be changed' );
119
			throw new \Aimeos\Controller\Frontend\Basket\Exception( sprintf( $msg, $position ) );
120
		}
121
122
		$manager = \Aimeos\MShop::create( $this->getContext(), 'product' );
123
		$product = $manager->find( $orderProduct->getProductCode(), ['price' => ['default']], true );
124
		$quantity = $this->checkQuantity( $product, $quantity );
125
126
		if( ( $prices = $product->getRefItems( 'price', 'default', 'default' ) )->isEmpty() )
127
		{
128
			$prices = $manager->get( $orderProduct->getProductId(), ['price' => ['default']], true )
129
				->getRefItems( 'price', 'default', 'default' );
130
		}
131
132
		$price = $this->calcPrice( $orderProduct, $prices, $quantity );
133
		$orderProduct = $orderProduct->setQuantity( $quantity )->setPrice( $price );
134
135
		$this->getController()->get()->addProduct( $orderProduct, $position );
136
		$this->getController()->save();
137
138
		return $this;
139
	}
140
141
142
	/**
143
	 * Returns the variant attributes and updates the price list if necessary.
144
	 *
145
	 * @param \Aimeos\MShop\Product\Item\Iface $productItem Product item which is replaced if necessary
146
	 * @param array $variantAttributeIds List of product variant attribute IDs
147
	 * @return \Aimeos\MShop\Product\Item\Iface Product variant article
148
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception If no product variant is found
149
	 */
150
	protected function getArticle( \Aimeos\MShop\Product\Item\Iface $productItem, array $variant ) : \Aimeos\MShop\Product\Item\Iface
151
	{
152
		$items = [];
153
		$context = $this->getContext();
154
155
		/** controller/frontend/basket/require-variant
156
		 * A variant of a selection product must be chosen
157
		 *
158
		 * Selection products normally consist of several article variants and
159
		 * by default exactly one article variant of a selection product can be
160
		 * put into the basket.
161
		 *
162
		 * By setting this option to false, the selection product including the
163
		 * chosen attributes (if any attribute values were selected) can be put
164
		 * into the basket as well. This makes it possible to get all articles
165
		 * or a subset of articles (e.g. all of a color) at once.
166
		 *
167
		 * This option replace the "client/html/basket/require-variant" setting.
168
		 *
169
		 * @param boolean True if a variant must be chosen, false if also the selection product with attributes can be added
170
		 * @since 2018.01
171
		 * @category Developer
172
		 * @category User
173
		 */
174
		$requireVariant = $context->getConfig()->get( 'controller/frontend/basket/require-variant', true );
175
176
		foreach( $productItem->getRefItems( 'product', 'default', 'default' ) as $item )
177
		{
178
			foreach( $variant as $id )
179
			{
180
				if( $item->getListItem( 'attribute', 'variant', $id ) === null ) {
181
					continue 2;
182
				}
183
			}
184
185
			$items[] = $item;
186
		}
187
188
		if( count( $items ) > 1 )
189
		{
190
			$msg = $context->getI18n()->dt( 'controller/frontend', 'No unique article found for selected attributes and product ID "%1$s"' );
191
			throw new \Aimeos\Controller\Frontend\Basket\Exception( sprintf( $msg, $productItem->getId() ) );
192
		}
193
194
		if( empty( $items ) && $requireVariant != false ) // count == 0
195
		{
196
			$msg = $context->getI18n()->dt( 'controller/frontend', 'No article found for selected attributes and product ID "%1$s"' );
197
			throw new \Aimeos\Controller\Frontend\Basket\Exception( sprintf( $msg, $productItem->getId() ) );
198
		}
199
200
		return current( $items ) ?: $productItem;
201
	}
202
}
203