Completed
Push — master ( 1b879b...45b214 )
by Aimeos
02:38
created

Standard::addProduct()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 32
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 32
rs 8.8571
c 0
b 0
f 0
cc 1
eloc 22
nc 1
nop 8

How to fix   Many Parameters   

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 Metaways Infosystems GmbH, 2012
6
 * @copyright Aimeos (aimeos.org), 2015-2016
7
 * @package Controller
8
 * @subpackage Frontend
9
 */
10
11
12
namespace Aimeos\Controller\Frontend\Basket;
13
14
15
/**
16
 * Default implementation of the basket frontend controller.
17
 *
18
 * @package Controller
19
 * @subpackage Frontend
20
 */
21
class Standard
22
	extends Base
2 ignored issues
show
Coding Style introduced by
The extends keyword must be on the same line as the class name
Loading history...
Coding Style introduced by
Expected 0 spaces between "Base" and comma; 1 found
Loading history...
23
	implements Iface, \Aimeos\Controller\Frontend\Common\Iface
1 ignored issue
show
Coding Style introduced by
The implements keyword must be on the same line as the class name
Loading history...
24
{
25
	private $baskets = array();
26
	private $domainManager;
27
	private $type = 'default';
28
29
30
	/**
31
	 * Initializes the frontend controller.
32
	 *
33
	 * @param \Aimeos\MShop\Context\Item\Iface $context Object storing the required instances for manaing databases
34
	 *  connections, logger, session, etc.
35
	 */
36
	public function __construct( \Aimeos\MShop\Context\Item\Iface $context )
37
	{
38
		parent::__construct( $context );
39
40
		$this->domainManager = \Aimeos\MShop\Factory::createManager( $context, 'order/base' );
41
	}
42
43
44
	/**
45
	 * Empties the basket and removing all products, addresses, services, etc.
46
	 *
47
	 * @return \Aimeos\Controller\Frontend\Basket\Iface Basket frontend object
48
	 */
49
	public function clear()
50
	{
51
		$this->baskets[$this->type] = $this->domainManager->createItem();
52
		$this->domainManager->setSession( $this->baskets[$this->type], $this->type );
53
54
		return $this;
55
	}
56
57
58
	/**
59
	 * Returns the basket object.
60
	 *
61
	 * @return \Aimeos\MShop\Order\Item\Base\Iface Basket holding products, addresses and delivery/payment options
62
	 */
63
	public function get()
64
	{
65
		if( !isset( $this->baskets[$this->type] ) )
66
		{
67
			$this->baskets[$this->type] = $this->domainManager->getSession( $this->type );
68
			$this->checkLocale( $this->type );
69
		}
70
71
		return $this->baskets[$this->type];
72
	}
73
74
75
	/**
76
	 * Explicitely persists the basket content
77
	 *
78
	 * @return \Aimeos\Controller\Frontend\Basket\Iface Basket frontend object
79
	 */
80
	public function save()
81
	{
82
		if( isset( $this->baskets[$this->type] ) && $this->baskets[$this->type]->isModified() ) {
83
			$this->domainManager->setSession( $this->baskets[$this->type], $this->type );
84
		}
85
86
		return $this;
87
	}
88
89
90
	/**
91
	 * Sets the new basket type
92
	 *
93
	 * @param string $type Basket type
94
	 * @return \Aimeos\Controller\Frontend\Basket\Iface Basket frontend object
95
	 */
96
	public function setType( $type )
97
	{
98
		$this->type = $type;
99
		return $this;
100
	}
101
102
103
	/**
104
	 * Adds a categorized product to the basket of the user stored in the session.
105
	 *
106
	 * @param string $prodid ID of the base product to add
107
	 * @param integer $quantity Amount of products that should by added
108
	 * @param array $options Possible options are: 'stock'=>true|false and 'variant'=>true|false
109
	 * 	The 'stock'=>false option allows adding products without being in stock.
110
	 * 	The 'variant'=>false option allows adding the selection product to the basket
111
	 * 	instead of the specific sub-product if the variant-building attribute IDs
112
	 * 	doesn't match a specific sub-product or if the attribute IDs are missing.
113
	 * @param array $variantAttributeIds List of variant-building attribute IDs that identify a specific product
114
	 * 	in a selection products
115
	 * @param array $configAttributeIds  List of attribute IDs that doesn't identify a specific product in a
116
	 * 	selection of products but are stored together with the product (e.g. for configurable products)
117
	 * @param array $hiddenAttributeIds List of attribute IDs that should be stored along with the product in the order
118
	 * @param array $customAttributeValues Associative list of attribute IDs and arbitrary values that should be stored
119
	 * 	along with the product in the order
120
	 * @param string $stocktype Unique code of the stock type to deliver the products from
121
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception If the product isn't available
122
	 */
123
	public function addProduct( $prodid, $quantity = 1, array $options = array(), array $variantAttributeIds = array(),
124
		array $configAttributeIds = array(), array $hiddenAttributeIds = array(), array $customAttributeValues = array(),
125
		$stocktype = 'default' )
126
	{
127
		$context = $this->getContext();
128
		$productManager = \Aimeos\MShop\Factory::createManager( $context, 'product' );
129
		$productItem = $productManager->getItem( $prodid, array( 'media', 'supplier', 'price', 'product', 'text' ) );
130
131
		$orderBaseProductItem = \Aimeos\MShop\Factory::createManager( $context, 'order/base/product' )->createItem();
132
		$orderBaseProductItem->copyFrom( $productItem );
133
		$orderBaseProductItem->setQuantity( $quantity );
134
		$orderBaseProductItem->setStockType( $stocktype );
135
136
		$attr = array();
137
		$prices = $productItem->getRefItems( 'price', 'default', 'default' );
138
139
		$priceManager = \Aimeos\MShop\Factory::createManager( $context, 'price' );
140
		$price = $priceManager->getLowestPrice( $prices, $quantity );
141
142
		$attr = array_merge( $attr, $this->createOrderProductAttributes( $price, $prodid, $quantity, $configAttributeIds, 'config' ) );
143
		$attr = array_merge( $attr, $this->createOrderProductAttributes( $price, $prodid, $quantity, $hiddenAttributeIds, 'hidden' ) );
144
		$attr = array_merge( $attr, $this->createOrderProductAttributes( $price, $prodid, $quantity, array_keys( $customAttributeValues ), 'custom', $customAttributeValues ) );
145
146
		// remove product rebate of original price in favor to rebates granted for the order
147
		$price->setRebate( '0.00' );
148
149
		$orderBaseProductItem->setPrice( $price );
150
		$orderBaseProductItem->setAttributes( $attr );
151
152
		$this->get()->addProduct( $orderBaseProductItem );
153
		$this->save();
154
	}
155
156
157
	/**
158
	 * Deletes a product item from the basket.
159
	 *
160
	 * @param integer $position Position number (key) of the order product item
161
	 */
162
	public function deleteProduct( $position )
163
	{
164
		$product = $this->get()->getProduct( $position );
165
166
		if( $product->getFlags() === \Aimeos\MShop\Order\Item\Base\Product\Base::FLAG_IMMUTABLE )
167
		{
168
			$msg = sprintf( 'Basket item at position "%1$d" cannot be deleted manually', $position );
169
			throw new \Aimeos\Controller\Frontend\Basket\Exception( $msg );
170
		}
171
172
		$this->get()->deleteProduct( $position );
173
		$this->save();
174
	}
175
176
177
	/**
178
	 * Edits the quantity of a product item in the basket.
179
	 *
180
	 * @param integer $position Position number (key) of the order product item
181
	 * @param integer $quantity New quantiy of the product item
182
	 * @param array $options Possible options are: 'stock'=>true|false
183
	 * 	The 'stock'=>false option allows adding products without being in stock.
184
	 * @param string[] $configAttributeCodes Codes of the product config attributes that should be REMOVED
185
	 */
186
	public function editProduct( $position, $quantity, array $options = array(),
187
		array $configAttributeCodes = array() )
188
	{
189
		$product = $this->get()->getProduct( $position );
190
191
		if( $product->getFlags() & \Aimeos\MShop\Order\Item\Base\Product\Base::FLAG_IMMUTABLE )
192
		{
193
			$msg = sprintf( 'Basket item at position "%1$d" cannot be changed', $position );
194
			throw new \Aimeos\Controller\Frontend\Basket\Exception( $msg );
195
		}
196
197
		$product->setQuantity( $quantity );
198
199
		$attributes = $product->getAttributes();
200
		foreach( $attributes as $key => $attribute )
201
		{
202
			if( in_array( $attribute->getCode(), $configAttributeCodes ) ) {
203
				unset( $attributes[$key] );
204
			}
205
		}
206
		$product->setAttributes( $attributes );
207
208
		$productItem = $this->getDomainItem( 'product', 'product.code', $product->getProductCode(), array( 'price', 'text' ) );
209
		$prices = $productItem->getRefItems( 'price', 'default' );
210
		$product->setPrice( $this->calcPrice( $product, $prices, $quantity ) );
211
212
		$this->get()->deleteProduct( $position );
213
		$this->get()->addProduct( $product, $position );
214
215
		$this->save();
216
	}
217
218
219
	/**
220
	 * Adds the given coupon code and updates the basket.
221
	 *
222
	 * @param string $code Coupon code entered by the user
223
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception if the coupon code is invalid or not allowed
224
	 */
225
	public function addCoupon( $code )
226
	{
227
		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'coupon' );
228
		$codeManager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'coupon/code' );
229
230
231
		$search = $codeManager->createSearch( true );
232
		$expr = array(
233
			$search->compare( '==', 'coupon.code.code', $code ),
234
			$search->getConditions(),
235
		);
236
		$search->setConditions( $search->combine( '&&', $expr ) );
237
		$search->setSlice( 0, 1 );
238
239
		$result = $codeManager->searchItems( $search );
240
241
		if( ( $codeItem = reset( $result ) ) === false ) {
242
			throw new \Aimeos\Controller\Frontend\Basket\Exception( sprintf( 'Coupon code "%1$s" is invalid or not available any more', $code ) );
243
		}
244
245
246
		$search = $manager->createSearch( true );
247
		$expr = array(
248
			$search->compare( '==', 'coupon.id', $codeItem->getParentId() ),
249
			$search->getConditions(),
250
		);
251
		$search->setConditions( $search->combine( '&&', $expr ) );
252
		$search->setSlice( 0, 1 );
253
254
		$result = $manager->searchItems( $search );
255
256
		if( ( $item = reset( $result ) ) === false ) {
257
			throw new \Aimeos\Controller\Frontend\Basket\Exception( sprintf( 'Coupon for code "%1$s" is not available any more', $code ) );
258
		}
259
260
261
		$provider = $manager->getProvider( $item, $code );
262
263
		if( $provider->isAvailable( $this->get() ) !== true ) {
264
			throw new \Aimeos\Controller\Frontend\Basket\Exception( sprintf( 'Requirements for coupon code "%1$s" aren\'t met', $code ) );
265
		}
266
267
		$provider->addCoupon( $this->get() );
268
		$this->save();
269
	}
270
271
272
	/**
273
	 * Removes the given coupon code and its effects from the basket.
274
	 *
275
	 * @param string $code Coupon code entered by the user
276
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception if the coupon code is invalid
277
	 */
278
	public function deleteCoupon( $code )
279
	{
280
		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'coupon' );
281
282
		$search = $manager->createSearch();
283
		$search->setConditions( $search->compare( '==', 'coupon.code.code', $code ) );
284
		$search->setSlice( 0, 1 );
285
286
		$result = $manager->searchItems( $search );
287
288
		if( ( $item = reset( $result ) ) === false ) {
289
			throw new \Aimeos\Controller\Frontend\Basket\Exception( sprintf( 'Coupon code "%1$s" is invalid', $code ) );
290
		}
291
292
		$manager->getProvider( $item, $code )->deleteCoupon( $this->get() );
293
		$this->save();
294
	}
295
296
297
	/**
298
	 * Sets the address of the customer in the basket.
299
	 *
300
	 * @param string $type Address type constant from \Aimeos\MShop\Order\Item\Base\Address\Base
301
	 * @param \Aimeos\MShop\Common\Item\Address\Iface|array|null $value Address object or array with key/value pairs of address or null to remove address from basket
302
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception If the billing or delivery address is not of any required type of
303
	 * 	if one of the keys is invalid when using an array with key/value pairs
304
	 */
305
	public function setAddress( $type, $value )
306
	{
307
		$address = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'order/base/address' )->createItem();
308
		$address->setType( $type );
309
310
		if( $value instanceof \Aimeos\MShop\Common\Item\Address\Iface )
311
		{
312
			$address->copyFrom( $value );
313
			$this->get()->setAddress( $address, $type );
314
		}
315
		else if( is_array( $value ) )
316
		{
317
			$this->setAddressFromArray( $address, $value );
318
			$this->get()->setAddress( $address, $type );
319
		}
320
		else if( $value === null )
321
		{
322
			$this->get()->deleteAddress( $type );
323
		}
324
		else
325
		{
326
			throw new \Aimeos\Controller\Frontend\Basket\Exception( sprintf( 'Invalid value for address type "%1$s"', $type ) );
327
		}
328
329
		$this->save();
330
	}
331
332
333
	/**
334
	 * Sets the delivery/payment service item based on the service ID.
335
	 *
336
	 * @param string $type Service type code like 'payment' or 'delivery'
337
	 * @param string $id Unique ID of the service item
338
	 * @param array $attributes Associative list of key/value pairs containing the attributes selected or
339
	 * 	entered by the customer when choosing one of the delivery or payment options
340
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception If there is no price to the service item attached
341
	 */
342
	public function setService( $type, $id, array $attributes = array() )
343
	{
344
		$context = $this->getContext();
345
346
		$serviceManager = \Aimeos\MShop\Factory::createManager( $context, 'service' );
347
		$serviceItem = $this->getDomainItem( 'service', 'service.id', $id, array( 'media', 'price', 'text' ) );
348
349
		$provider = $serviceManager->getProvider( $serviceItem );
350
		$result = $provider->checkConfigFE( $attributes );
351
		$unknown = array_diff_key( $attributes, $result );
352
353
		if( count( $unknown ) > 0 )
354
		{
355
			$msg = sprintf( 'Unknown attributes "%1$s"', implode( '","', array_keys( $unknown ) ) );
356
			throw new \Aimeos\Controller\Frontend\Basket\Exception( $msg );
357
		}
358
359
		foreach( $result as $key => $value )
360
		{
361
			if( $value !== null ) {
362
				throw new \Aimeos\Controller\Frontend\Basket\Exception( $value );
363
			}
364
		}
365
366
		$orderBaseServiceManager = \Aimeos\MShop\Factory::createManager( $context, 'order/base/service' );
367
		$orderServiceItem = $orderBaseServiceManager->createItem();
368
		$orderServiceItem->copyFrom( $serviceItem );
369
370
		$price = $provider->calcPrice( $this->get() );
371
		// remove service rebate of original price
372
		$price->setRebate( '0.00' );
373
		$orderServiceItem->setPrice( $price );
374
375
		$provider->setConfigFE( $orderServiceItem, $attributes );
376
377
		$this->get()->setService( $orderServiceItem, $type );
378
		$this->save();
379
	}
380
381
382
	/**
383
	 * Fills the order address object with the values from the array.
384
	 *
385
	 * @param \Aimeos\MShop\Order\Item\Base\Address\Iface $address Address item to store the values into
386
	 * @param array $map Associative array of key/value pairs. The keys must be the same as when calling toArray() from
387
	 * 	an address item.
388
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception
389
	 */
390
	protected function setAddressFromArray( \Aimeos\MShop\Order\Item\Base\Address\Iface $address, array $map )
391
	{
392
		foreach( $map as $key => $value ) {
393
			$map[$key] = strip_tags( $value ); // prevent XSS
394
		}
395
396
		$errors = $address->fromArray( $map );
397
398
		if( count( $errors ) > 0 )
399
		{
400
			$msg = sprintf( 'Invalid address properties, please check your input' );
401
			throw new \Aimeos\Controller\Frontend\Basket\Exception( $msg, 0, null, $errors );
402
		}
403
	}
404
}
405