Completed
Push — master ( 45b214...6fb08c )
by Aimeos
03:09
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
	 * Creates a new order base object from the current basket
105
	 *
106
	 * @return \Aimeos\MShop\Order\Item\Base\Iface Order base object including products, addresses and services
107
	 */
108
	public function store()
109
	{
110
		$basket = $this->get();
111
112
		$this->domainManager->begin();
113
		$this->domainManager->store( $basket );
114
		$this->domainManager->commit();
115
116
		return $basket;
117
	}
118
119
120
	/**
121
	 * Returns the order base object for the given ID
122
	 *
123
	 * @param string $id Unique ID of the order base object
124
	 * @param integer $parts Constants which parts of the order base object should be loaded
125
	 * @return \Aimeos\MShop\Order\Item\Base\Iface Order base object including the given parts
126
	 */
127
	public function load( $id, $parts = \Aimeos\MShop\Order\Manager\Base\Base::PARTS_ALL )
128
	{
129
		return $this->domainManager->load( $id, $parts );
130
	}
131
132
133
	/**
134
	 * Adds a categorized product to the basket of the user stored in the session.
135
	 *
136
	 * @param string $prodid ID of the base product to add
137
	 * @param integer $quantity Amount of products that should by added
138
	 * @param array $options Possible options are: 'stock'=>true|false and 'variant'=>true|false
139
	 * 	The 'stock'=>false option allows adding products without being in stock.
140
	 * 	The 'variant'=>false option allows adding the selection product to the basket
141
	 * 	instead of the specific sub-product if the variant-building attribute IDs
142
	 * 	doesn't match a specific sub-product or if the attribute IDs are missing.
143
	 * @param array $variantAttributeIds List of variant-building attribute IDs that identify a specific product
144
	 * 	in a selection products
145
	 * @param array $configAttributeIds  List of attribute IDs that doesn't identify a specific product in a
146
	 * 	selection of products but are stored together with the product (e.g. for configurable products)
147
	 * @param array $hiddenAttributeIds List of attribute IDs that should be stored along with the product in the order
148
	 * @param array $customAttributeValues Associative list of attribute IDs and arbitrary values that should be stored
149
	 * 	along with the product in the order
150
	 * @param string $stocktype Unique code of the stock type to deliver the products from
151
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception If the product isn't available
152
	 */
153
	public function addProduct( $prodid, $quantity = 1, array $options = array(), array $variantAttributeIds = array(),
154
		array $configAttributeIds = array(), array $hiddenAttributeIds = array(), array $customAttributeValues = array(),
155
		$stocktype = 'default' )
156
	{
157
		$context = $this->getContext();
158
		$productManager = \Aimeos\MShop\Factory::createManager( $context, 'product' );
159
		$productItem = $productManager->getItem( $prodid, array( 'media', 'supplier', 'price', 'product', 'text' ) );
160
161
		$orderBaseProductItem = \Aimeos\MShop\Factory::createManager( $context, 'order/base/product' )->createItem();
162
		$orderBaseProductItem->copyFrom( $productItem );
163
		$orderBaseProductItem->setQuantity( $quantity );
164
		$orderBaseProductItem->setStockType( $stocktype );
165
166
		$attr = array();
167
		$prices = $productItem->getRefItems( 'price', 'default', 'default' );
168
169
		$priceManager = \Aimeos\MShop\Factory::createManager( $context, 'price' );
170
		$price = $priceManager->getLowestPrice( $prices, $quantity );
171
172
		$attr = array_merge( $attr, $this->createOrderProductAttributes( $price, $prodid, $quantity, $configAttributeIds, 'config' ) );
173
		$attr = array_merge( $attr, $this->createOrderProductAttributes( $price, $prodid, $quantity, $hiddenAttributeIds, 'hidden' ) );
174
		$attr = array_merge( $attr, $this->createOrderProductAttributes( $price, $prodid, $quantity, array_keys( $customAttributeValues ), 'custom', $customAttributeValues ) );
175
176
		// remove product rebate of original price in favor to rebates granted for the order
177
		$price->setRebate( '0.00' );
178
179
		$orderBaseProductItem->setPrice( $price );
180
		$orderBaseProductItem->setAttributes( $attr );
181
182
		$this->get()->addProduct( $orderBaseProductItem );
183
		$this->save();
184
	}
185
186
187
	/**
188
	 * Deletes a product item from the basket.
189
	 *
190
	 * @param integer $position Position number (key) of the order product item
191
	 */
192
	public function deleteProduct( $position )
193
	{
194
		$product = $this->get()->getProduct( $position );
195
196
		if( $product->getFlags() === \Aimeos\MShop\Order\Item\Base\Product\Base::FLAG_IMMUTABLE )
197
		{
198
			$msg = sprintf( 'Basket item at position "%1$d" cannot be deleted manually', $position );
199
			throw new \Aimeos\Controller\Frontend\Basket\Exception( $msg );
200
		}
201
202
		$this->get()->deleteProduct( $position );
203
		$this->save();
204
	}
205
206
207
	/**
208
	 * Edits the quantity of a product item in the basket.
209
	 *
210
	 * @param integer $position Position number (key) of the order product item
211
	 * @param integer $quantity New quantiy of the product item
212
	 * @param array $options Possible options are: 'stock'=>true|false
213
	 * 	The 'stock'=>false option allows adding products without being in stock.
214
	 * @param string[] $configAttributeCodes Codes of the product config attributes that should be REMOVED
215
	 */
216
	public function editProduct( $position, $quantity, array $options = array(),
217
		array $configAttributeCodes = array() )
218
	{
219
		$product = $this->get()->getProduct( $position );
220
221
		if( $product->getFlags() & \Aimeos\MShop\Order\Item\Base\Product\Base::FLAG_IMMUTABLE )
222
		{
223
			$msg = sprintf( 'Basket item at position "%1$d" cannot be changed', $position );
224
			throw new \Aimeos\Controller\Frontend\Basket\Exception( $msg );
225
		}
226
227
		$product->setQuantity( $quantity );
228
229
		$attributes = $product->getAttributes();
230
		foreach( $attributes as $key => $attribute )
231
		{
232
			if( in_array( $attribute->getCode(), $configAttributeCodes ) ) {
233
				unset( $attributes[$key] );
234
			}
235
		}
236
		$product->setAttributes( $attributes );
237
238
		$productItem = $this->getDomainItem( 'product', 'product.code', $product->getProductCode(), array( 'price', 'text' ) );
239
		$prices = $productItem->getRefItems( 'price', 'default' );
240
		$product->setPrice( $this->calcPrice( $product, $prices, $quantity ) );
241
242
		$this->get()->deleteProduct( $position );
243
		$this->get()->addProduct( $product, $position );
244
245
		$this->save();
246
	}
247
248
249
	/**
250
	 * Adds the given coupon code and updates the basket.
251
	 *
252
	 * @param string $code Coupon code entered by the user
253
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception if the coupon code is invalid or not allowed
254
	 */
255
	public function addCoupon( $code )
256
	{
257
		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'coupon' );
258
		$codeManager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'coupon/code' );
259
260
261
		$search = $codeManager->createSearch( true );
262
		$expr = array(
263
			$search->compare( '==', 'coupon.code.code', $code ),
264
			$search->getConditions(),
265
		);
266
		$search->setConditions( $search->combine( '&&', $expr ) );
267
		$search->setSlice( 0, 1 );
268
269
		$result = $codeManager->searchItems( $search );
270
271
		if( ( $codeItem = reset( $result ) ) === false ) {
272
			throw new \Aimeos\Controller\Frontend\Basket\Exception( sprintf( 'Coupon code "%1$s" is invalid or not available any more', $code ) );
273
		}
274
275
276
		$search = $manager->createSearch( true );
277
		$expr = array(
278
			$search->compare( '==', 'coupon.id', $codeItem->getParentId() ),
279
			$search->getConditions(),
280
		);
281
		$search->setConditions( $search->combine( '&&', $expr ) );
282
		$search->setSlice( 0, 1 );
283
284
		$result = $manager->searchItems( $search );
285
286
		if( ( $item = reset( $result ) ) === false ) {
287
			throw new \Aimeos\Controller\Frontend\Basket\Exception( sprintf( 'Coupon for code "%1$s" is not available any more', $code ) );
288
		}
289
290
291
		$provider = $manager->getProvider( $item, $code );
292
293
		if( $provider->isAvailable( $this->get() ) !== true ) {
294
			throw new \Aimeos\Controller\Frontend\Basket\Exception( sprintf( 'Requirements for coupon code "%1$s" aren\'t met', $code ) );
295
		}
296
297
		$provider->addCoupon( $this->get() );
298
		$this->save();
299
	}
300
301
302
	/**
303
	 * Removes the given coupon code and its effects from the basket.
304
	 *
305
	 * @param string $code Coupon code entered by the user
306
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception if the coupon code is invalid
307
	 */
308
	public function deleteCoupon( $code )
309
	{
310
		$manager = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'coupon' );
311
312
		$search = $manager->createSearch();
313
		$search->setConditions( $search->compare( '==', 'coupon.code.code', $code ) );
314
		$search->setSlice( 0, 1 );
315
316
		$result = $manager->searchItems( $search );
317
318
		if( ( $item = reset( $result ) ) === false ) {
319
			throw new \Aimeos\Controller\Frontend\Basket\Exception( sprintf( 'Coupon code "%1$s" is invalid', $code ) );
320
		}
321
322
		$manager->getProvider( $item, $code )->deleteCoupon( $this->get() );
323
		$this->save();
324
	}
325
326
327
	/**
328
	 * Sets the address of the customer in the basket.
329
	 *
330
	 * @param string $type Address type constant from \Aimeos\MShop\Order\Item\Base\Address\Base
331
	 * @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
332
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception If the billing or delivery address is not of any required type of
333
	 * 	if one of the keys is invalid when using an array with key/value pairs
334
	 */
335
	public function setAddress( $type, $value )
336
	{
337
		$address = \Aimeos\MShop\Factory::createManager( $this->getContext(), 'order/base/address' )->createItem();
338
		$address->setType( $type );
339
340
		if( $value instanceof \Aimeos\MShop\Common\Item\Address\Iface )
341
		{
342
			$address->copyFrom( $value );
343
			$this->get()->setAddress( $address, $type );
344
		}
345
		else if( is_array( $value ) )
346
		{
347
			$this->setAddressFromArray( $address, $value );
348
			$this->get()->setAddress( $address, $type );
349
		}
350
		else if( $value === null )
351
		{
352
			$this->get()->deleteAddress( $type );
353
		}
354
		else
355
		{
356
			throw new \Aimeos\Controller\Frontend\Basket\Exception( sprintf( 'Invalid value for address type "%1$s"', $type ) );
357
		}
358
359
		$this->save();
360
	}
361
362
363
	/**
364
	 * Sets the delivery/payment service item based on the service ID.
365
	 *
366
	 * @param string $type Service type code like 'payment' or 'delivery'
367
	 * @param string $id Unique ID of the service item
368
	 * @param array $attributes Associative list of key/value pairs containing the attributes selected or
369
	 * 	entered by the customer when choosing one of the delivery or payment options
370
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception If there is no price to the service item attached
371
	 */
372
	public function setService( $type, $id, array $attributes = array() )
373
	{
374
		$context = $this->getContext();
375
376
		$serviceManager = \Aimeos\MShop\Factory::createManager( $context, 'service' );
377
		$serviceItem = $this->getDomainItem( 'service', 'service.id', $id, array( 'media', 'price', 'text' ) );
378
379
		$provider = $serviceManager->getProvider( $serviceItem );
380
		$result = $provider->checkConfigFE( $attributes );
381
		$unknown = array_diff_key( $attributes, $result );
382
383
		if( count( $unknown ) > 0 )
384
		{
385
			$msg = sprintf( 'Unknown attributes "%1$s"', implode( '","', array_keys( $unknown ) ) );
386
			throw new \Aimeos\Controller\Frontend\Basket\Exception( $msg );
387
		}
388
389
		foreach( $result as $key => $value )
390
		{
391
			if( $value !== null ) {
392
				throw new \Aimeos\Controller\Frontend\Basket\Exception( $value );
393
			}
394
		}
395
396
		$orderBaseServiceManager = \Aimeos\MShop\Factory::createManager( $context, 'order/base/service' );
397
		$orderServiceItem = $orderBaseServiceManager->createItem();
398
		$orderServiceItem->copyFrom( $serviceItem );
399
400
		$price = $provider->calcPrice( $this->get() );
401
		// remove service rebate of original price
402
		$price->setRebate( '0.00' );
403
		$orderServiceItem->setPrice( $price );
404
405
		$provider->setConfigFE( $orderServiceItem, $attributes );
406
407
		$this->get()->setService( $orderServiceItem, $type );
408
		$this->save();
409
	}
410
411
412
	/**
413
	 * Fills the order address object with the values from the array.
414
	 *
415
	 * @param \Aimeos\MShop\Order\Item\Base\Address\Iface $address Address item to store the values into
416
	 * @param array $map Associative array of key/value pairs. The keys must be the same as when calling toArray() from
417
	 * 	an address item.
418
	 * @throws \Aimeos\Controller\Frontend\Basket\Exception
419
	 */
420
	protected function setAddressFromArray( \Aimeos\MShop\Order\Item\Base\Address\Iface $address, array $map )
421
	{
422
		foreach( $map as $key => $value ) {
423
			$map[$key] = strip_tags( $value ); // prevent XSS
424
		}
425
426
		$errors = $address->fromArray( $map );
427
428
		if( count( $errors ) > 0 )
429
		{
430
			$msg = sprintf( 'Invalid address properties, please check your input' );
431
			throw new \Aimeos\Controller\Frontend\Basket\Exception( $msg, 0, null, $errors );
432
		}
433
	}
434
}
435