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

Select::addProduct()   B

Complexity

Conditions 6
Paths 17

Size

Total Lines 58
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
eloc 34
c 5
b 0
f 0
dl 0
loc 58
rs 8.7537
cc 6
nc 17
nop 8

How to fix   Long Method    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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