Select::addProduct()   B
last analyzed

Complexity

Conditions 8
Paths 9

Size

Total Lines 65
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 42
c 2
b 0
f 0
dl 0
loc 65
rs 8.0035
cc 8
nc 9
nop 7

How to fix   Long Method   

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:

1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2017-2025
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|null $siteId Unique ID of the site the product should be bought from or NULL for site the product is from
34
	 * @return \Aimeos\Controller\Frontend\Basket\Iface Basket frontend object for fluent interface
35
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception If the product isn't available
36
	 */
37
	public function addProduct( \Aimeos\MShop\Product\Item\Iface $product, float $quantity = 1,
38
		array $variant = [], array $config = [], array $custom = [], string $stocktype = 'default', ?string $siteId = null
39
	) : \Aimeos\Controller\Frontend\Basket\Iface
40
	{
41
		if( $product->getType() !== 'select' )
42
		{
43
			$this->getController()->addProduct( $product, $quantity, $variant, $config, $custom, $stocktype, $siteId );
44
			return $this;
45
		}
46
47
		$attr = [];
48
		$prices = $product->getRefItems( 'price', 'default', 'default' );
49
		$hidden = $product->getRefItems( 'attribute', null, 'hidden' );
50
51
		$productItem = $this->getArticle( $product, $variant );
52
		$quantity = $this->call( 'checkQuantity', $productItem, $quantity );
53
54
		$orderProductItem = \Aimeos\MShop::create( $this->context(), 'order' )
55
			->createProduct()
0 ignored issues
show
Bug introduced by
The method createProduct() does not exist on Aimeos\MShop\Common\Manager\Iface. Did you maybe mean create()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

55
			->/** @scrutinizer ignore-call */ createProduct()

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
56
			->copyFrom( $product )
57
			->setQuantity( $quantity )
58
			->setStockType( $stocktype )
59
			->setName( $productItem->getName() )
60
			->setScale( $productItem->getScale() )
61
			->setProductId( $productItem->getId() )
62
			->setParentProductId( $product->getId() )
63
			->setProductCode( $productItem->getCode() )
64
			->setSiteId( $siteId ?: $productItem->getSiteId() );
65
66
		$this->call( 'checkAttributes', [$product, $productItem], 'custom', array_keys( $custom ) );
67
		$this->call( 'checkAttributes', [$product, $productItem], 'config', array_keys( $config ) );
68
69
		if( !( $subprices = $productItem->getRefItems( 'price', 'default', 'default' ) )->isEmpty() ) {
70
			$prices = $subprices;
71
		}
72
73
		if( $mediaItem = $productItem->getRefItems( 'media', 'default', 'default' )->first() ) {
74
			$orderProductItem->setMediaUrl( $mediaItem->getPreview() );
75
		}
76
77
		$hidden->union( $productItem->getRefItems( 'attribute', null, 'hidden' ) );
78
79
		$orderManager = \Aimeos\MShop::create( $this->context(), 'order' );
80
		$attributes = $productItem->getRefItems( 'attribute', null, 'variant' );
81
82
		foreach( $this->call( 'getAttributes', $attributes->keys()->toArray(), ['text'] ) as $attrItem ) {
83
			$attr[] = $orderManager->createProductAttribute()->copyFrom( $attrItem )->setType( 'variant' );
0 ignored issues
show
Bug introduced by
The method createProductAttribute() does not exist on Aimeos\MShop\Common\Manager\Iface. Did you maybe mean create()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

83
			$attr[] = $orderManager->/** @scrutinizer ignore-call */ createProductAttribute()->copyFrom( $attrItem )->setType( 'variant' );

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
84
		}
85
86
		$custAttr = $this->call( 'getOrderProductAttributes', 'custom', array_keys( $custom ), $custom );
87
		$confAttr = $this->call( 'getOrderProductAttributes', 'config', array_keys( $config ), [], $config );
88
		$hideAttr = $this->call( 'getOrderProductAttributes', 'hidden', $hidden->keys()->toArray() );
89
90
		$orderProductItem->setAttributeItems( array_merge( $attr, $custAttr, $confAttr, $hideAttr ) );
91
92
		$price = $this->call( 'calcPrice', $orderProductItem, $prices, $quantity );
93
		$orderProductItem
94
			->setPrice( $price )
95
			->setSiteId( $siteId ?: $price->getSiteId() )
96
			->setVendor( $this->getVendor( $siteId ?: $price->getSiteId() ) );
97
98
		$this->getController()->get()->addProduct( $orderProductItem );
99
		$this->getController()->save();
100
101
		return $this;
102
	}
103
104
105
	/**
106
	 * Edits the quantity of a product item in the basket.
107
	 *
108
	 * @param int $position Position number (key) of the order product item
109
	 * @param float $quantity New quantiy of the product item
110
	 * @return \Aimeos\Controller\Frontend\Basket\Iface Basket frontend object for fluent interface
111
	 */
112
	public function updateProduct( int $position, float $quantity ) : \Aimeos\Controller\Frontend\Basket\Iface
113
	{
114
		$orderProduct = $this->get()->getProduct( $position );
115
116
		if( $orderProduct->getType() !== 'select' )
117
		{
118
			$this->getController()->updateProduct( $position, $quantity );
119
			return $this;
120
		}
121
122
		$context = $this->context();
123
124
		if( $orderProduct->getFlags() & \Aimeos\MShop\Order\Item\Product\Base::FLAG_IMMUTABLE )
125
		{
126
			$msg = $context->translate( 'controller/frontend', 'Basket item at position "%1$d" cannot be changed' );
127
			throw new \Aimeos\Controller\Frontend\Basket\Exception( sprintf( $msg, $position ) );
128
		}
129
130
		$manager = \Aimeos\MShop::create( $context, 'product' );
131
		$product = $manager->get( $orderProduct->getProductId(), ['price' => ['default']], true );
132
		$product = \Aimeos\MShop::create( $context, 'rule' )->apply( $product, 'catalog' );
133
		$quantity = $this->call( 'checkQuantity', $product, $quantity );
134
135
		if( ( $prices = $product->getRefItems( 'price', 'default', 'default' ) )->isEmpty() )
136
		{
137
			$product = $manager->get( $orderProduct->getParentProductId(), ['price' => ['default']], true );
138
			$product = \Aimeos\MShop::create( $context, 'rule' )->apply( $product, 'catalog' );
139
			$prices = $product->getRefItems( 'price', 'default', 'default' );
140
		}
141
142
		$price = $this->call( 'calcPrice', $orderProduct, $prices, $quantity );
143
		$orderProduct = $orderProduct->setQuantity( $quantity )->setPrice( $price );
144
145
		$this->getController()->get()->addProduct( $orderProduct, $position );
146
		$this->getController()->save();
147
148
		return $this;
149
	}
150
151
152
	/**
153
	 * Returns the variant attributes and updates the price list if necessary.
154
	 *
155
	 * @param \Aimeos\MShop\Product\Item\Iface $productItem Product item which is replaced if necessary
156
	 * @param array $variantAttributeIds List of product variant attribute IDs
157
	 * @return \Aimeos\MShop\Product\Item\Iface Product variant article
158
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception If no product variant is found
159
	 */
160
	protected function getArticle( \Aimeos\MShop\Product\Item\Iface $productItem, array $variant ) : \Aimeos\MShop\Product\Item\Iface
161
	{
162
		$items = [];
163
		$context = $this->context();
164
165
		/** controller/frontend/basket/require-variant
166
		 * A variant of a selection product must be chosen
167
		 *
168
		 * Selection products normally consist of several article variants and
169
		 * by default exactly one article variant of a selection product can be
170
		 * put into the basket.
171
		 *
172
		 * By setting this option to false, the selection product including the
173
		 * chosen attributes (if any attribute values were selected) can be put
174
		 * into the basket as well. This makes it possible to get all articles
175
		 * or a subset of articles (e.g. all of a color) at once.
176
		 *
177
		 * This option replace the "client/html/basket/require-variant" setting.
178
		 *
179
		 * @param boolean True if a variant must be chosen, false if also the selection product with attributes can be added
180
		 * @since 2018.01
181
		 * @category Developer
182
		 * @category User
183
		 */
184
		$requireVariant = $context->config()->get( 'controller/frontend/basket/require-variant', true );
185
186
		foreach( $productItem->getRefItems( 'product', null, 'default' ) as $item )
187
		{
188
			foreach( $variant as $id )
189
			{
190
				if( $item->getListItem( 'attribute', 'variant', $id ) === null ) {
191
					continue 2;
192
				}
193
			}
194
195
			$items[] = $item;
196
		}
197
198
		if( count( $items ) > 1 )
199
		{
200
			$msg = $context->translate( 'controller/frontend', 'No unique article found for selected attributes and product ID "%1$s"' );
201
			throw new \Aimeos\Controller\Frontend\Basket\Exception( sprintf( $msg, $productItem->getId() ) );
202
		}
203
204
		if( empty( $items ) && $requireVariant != false ) // count == 0
205
		{
206
			$msg = $context->translate( 'controller/frontend', 'No article found for selected attributes and product ID "%1$s"' );
207
			throw new \Aimeos\Controller\Frontend\Basket\Exception( sprintf( $msg, $productItem->getId() ) );
208
		}
209
210
		return current( $items ) ?: $productItem;
211
	}
212
}
213