Passed
Push — master ( dac304...9a84ac )
by Aimeos
02:40
created

Standard::data()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 5
c 0
b 0
f 0
nc 1
nop 3
dl 0
loc 9
rs 10
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2015-2024
6
 * @package Client
7
 * @subpackage Html
8
 */
9
10
11
namespace Aimeos\Client\Html\Basket\Standard;
12
13
14
/**
15
 * Default implementation of standard basket HTML client.
16
 *
17
 * @package Client
18
 * @subpackage Html
19
 */
20
class Standard
21
	extends \Aimeos\Client\Html\Basket\Base
22
	implements \Aimeos\Client\Html\Common\Client\Factory\Iface
23
{
24
	/** client/html/basket/standard/name
25
	 * Class name of the used basket standard client implementation
26
	 *
27
	 * Each default HTML client can be replace by an alternative imlementation.
28
	 * To use this implementation, you have to set the last part of the class
29
	 * name as configuration value so the client factory knows which class it
30
	 * has to instantiate.
31
	 *
32
	 * For example, if the name of the default class is
33
	 *
34
	 *  \Aimeos\Client\Html\Basket\Standard\Standard
35
	 *
36
	 * and you want to replace it with your own version named
37
	 *
38
	 *  \Aimeos\Client\Html\Basket\Standard\Mybasket
39
	 *
40
	 * then you have to set the this configuration option:
41
	 *
42
	 *  client/html/basket/standard/name = Mybasket
43
	 *
44
	 * The value is the last part of your own class name and it's case sensitive,
45
	 * so take care that the configuration value is exactly named like the last
46
	 * part of the class name.
47
	 *
48
	 * The allowed characters of the class name are A-Z, a-z and 0-9. No other
49
	 * characters are possible! You should always start the last part of the class
50
	 * name with an upper case character and continue only with lower case characters
51
	 * or numbers. Avoid chamel case names like "MyBasket"!
52
	 *
53
	 * @param string Last part of the class name
54
	 * @since 2014.03
55
	 */
56
57
58
	/**
59
	 * Sets the necessary parameter values in the view.
60
	 *
61
	 * @param \Aimeos\Base\View\Iface $view The view object which generates the HTML output
62
	 * @param array &$tags Result array for the list of tags that are associated to the output
63
	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
64
	 * @return \Aimeos\Base\View\Iface Modified view object
65
	 */
66
	public function data( \Aimeos\Base\View\Iface $view, array &$tags = [], ?string &$expire = null ) : \Aimeos\Base\View\Iface
67
	{
68
		$context = $this->context();
69
		$site = $context->locale()->getSiteItem()->getCode();
70
71
		$view->standardBackUrl = $context->session()->get( 'aimeos/catalog/last/' . $site );
72
		$view->standardBasket = \Aimeos\Controller\Frontend::create( $this->context(), 'basket' )->get();
73
74
		return parent::data( $view, $tags, $expire );
75
	}
76
77
78
	/**
79
	 * Sets the necessary parameter values in the view.
80
	 *
81
	 * A view must be available and this method doesn't generate any output
82
	 * besides setting view variables if necessary.
83
	 */
84
	public function init()
85
	{
86
		$view = $this->view();
87
		$val = $view->param( 'b_check', 0 );
88
89
		try
90
		{
91
			switch( $view->param( 'b_action' ) )
92
			{
93
				case 'add':
94
					$this->addProducts( $view );
95
					break;
96
				case 'coupon-delete':
97
					$this->deleteCoupon( $view );
98
					break;
99
				case 'delete':
100
					$this->deleteProducts( $view );
101
					break;
102
				case 'save':
103
					$this->saveBasket( $view );
104
					break;
105
				default:
106
					$this->updateProducts( $view );
107
					$this->addCoupon( $view );
108
			}
109
		}
110
		catch( \Exception $e )
111
		{
112
			try {
113
				$view->standardCheckout = $this->save( $val );
114
			} catch( \Exception $ex ) {
115
				$view->standardCheckout = false;
116
			}
117
118
			throw $e;
119
		}
120
121
		$view->standardCheckout = $this->save( $val );
122
	}
123
124
125
	/**
126
	 * Adds the coupon specified by the view parameters from the basket.
127
	 *
128
	 * @param \Aimeos\Base\View\Iface $view View object
129
	 */
130
	protected function addCoupon( \Aimeos\Base\View\Iface $view )
131
	{
132
		if( ( $coupon = $view->param( 'b_coupon' ) ) != '' )
133
		{
134
			$context = $this->context();
135
			$cntl = \Aimeos\Controller\Frontend::create( $context, 'basket' );
136
			$code = $cntl->get()->getCoupons()->keys()->first();
137
138
			/** client/html/basket/standard/coupon/overwrite
139
			 * Replace previous coupon codes each time the user enters a new one
140
			 *
141
			 * If you want to allow only one coupon code per order and replace a
142
			 * previously entered one automatically, this configuration option
143
			 * should be set to true.
144
			 *
145
			 * @param boolean True to overwrite a previous coupon, false to keep them
146
			 * @since 2020.04
147
			 */
148
			if( $code && $context->config()->get( 'client/html/basket/standard/coupon/overwrite', false ) ) {
149
				$cntl->deleteCoupon( $code );
150
			}
151
152
			$cntl->addCoupon( $coupon );
153
			$this->clearCached();
154
		}
155
	}
156
157
158
	/**
159
	 * Adds the products specified by the view parameters to the basket.
160
	 *
161
	 * @param \Aimeos\Base\View\Iface $view View object
162
	 */
163
	protected function addProducts( \Aimeos\Base\View\Iface $view )
164
	{
165
		$context = $this->context();
166
		$basketCntl = \Aimeos\Controller\Frontend::create( $context, 'basket' );
167
		$productCntl = \Aimeos\Controller\Frontend::create( $context, 'product' )->uses( $this->call( 'domains' ) );
168
169
		if( ( $prodid = $view->param( 'b_prodid', '' ) ) !== '' && $view->param( 'b_quantity', 0 ) > 0 )
170
		{
171
			$basketCntl->addProduct(
172
				$productCntl->get( $prodid ),
173
				(float) $view->param( 'b_quantity', 0 ),
174
				(array) $view->param( 'b_attrvarid', [] ),
175
				$this->getAttributeMap( $view->param( 'b_attrconfid', [] ) ),
176
				array_filter( (array) $view->param( 'b_attrcustid', [] ) ),
177
				(string) $view->param( 'b_stocktype', 'default' ),
178
				$view->param( 'b_siteid' )
179
			);
180
		}
181
		else
182
		{
183
			foreach( (array) $view->param( 'b_prod', [] ) as $values )
184
			{
185
				if( ( $values['prodid'] ?? null ) && ( $values['quantity'] ?? 0 ) > 0 )
186
				{
187
					$basketCntl->addProduct( $productCntl->get( $values['prodid'] ),
188
						(float) ( $values['quantity'] ?? 0 ),
189
						array_filter( (array) ( $values['attrvarid'] ?? [] ) ),
190
						$this->getAttributeMap( (array) ( $values['attrconfid'] ?? [] ) ),
191
						array_filter( (array) ( $values['attrcustid'] ?? [] ) ),
192
						(string) ( $values['stocktype'] ?? 'default' ),
193
						$values['siteid'] ?? null
194
					);
195
				}
196
			}
197
		}
198
199
		$this->clearCached();
200
	}
201
202
203
	/**
204
	 * Removes the coupon specified by the view parameters from the basket.
205
	 *
206
	 * @param \Aimeos\Base\View\Iface $view View object
207
	 */
208
	protected function deleteCoupon( \Aimeos\Base\View\Iface $view )
209
	{
210
		if( ( $coupon = $view->param( 'b_coupon' ) ) != '' )
211
		{
212
			\Aimeos\Controller\Frontend::create( $this->context(), 'basket' )->deleteCoupon( $coupon );
213
			$this->clearCached();
214
		}
215
	}
216
217
218
	/**
219
	 * Removes the products specified by the view parameters from the basket.
220
	 *
221
	 * @param \Aimeos\Base\View\Iface $view View object
222
	 */
223
	protected function deleteProducts( \Aimeos\Base\View\Iface $view )
224
	{
225
		$controller = \Aimeos\Controller\Frontend::create( $this->context(), 'basket' );
226
		$products = (array) $view->param( 'b_position', [] );
227
228
		foreach( $products as $position ) {
229
			$controller->deleteProduct( $position );
230
		}
231
232
		$this->clearCached();
233
	}
234
235
236
	/**
237
	 * Returns the name of the domains that should be fetched together with the product
238
	 *
239
	 * @return array Domain names
240
	 */
241
	protected function domains() : array
242
	{
243
		return ['attribute', 'catalog', 'media', 'price', 'product', 'text', 'locale/site'];
244
	}
245
246
247
	/**
248
	 * Returns the configurable attribute values as ID/quantity pairs
249
	 *
250
	 * @param array $values Associative list which "id" and "qty" keys
251
	 * @return array Pairs of config attribute ID/quantity pairs
252
	 */
253
	protected function getAttributeMap( array $values )
254
	{
255
		$list = [];
256
		$confIds = ( isset( $values['id'] ) ? array_filter( (array) $values['id'] ) : [] );
257
		$confQty = ( isset( $values['qty'] ) ? array_filter( (array) $values['qty'] ) : [] );
258
259
		foreach( $confIds as $idx => $id )
260
		{
261
			if( isset( $confQty[$idx] ) && $confQty[$idx] > 0 ) {
262
				$list[$id] = $confQty[$idx];
263
			}
264
		}
265
266
		return $list;
267
	}
268
269
270
	/**
271
	 * Saves the basket content and checks before checkout
272
	 *
273
	 * @param int $val One of the allowed values (0, 1 or 2)
274
	 * @return bool TRUE if checkout is possible, FALSE if not
275
	 * @throws \Aimeos\MShop\Exception If checkout isn't possible
276
	 */
277
	protected function save( int $val ) : bool
278
	{
279
		$view = $this->view();
280
		$controller = \Aimeos\Controller\Frontend::create( $this->context(), 'basket' )->save();
281
282
		/** client/html/basket/standard/check
283
		 * Alters the behavior of the product checks before continuing with the checkout
284
		 *
285
		 * By default, the product related checks are performed every time the basket
286
		 * is shown. They test if there are any products in the basket and execute all
287
		 * basket plugins that have been registered for the "check.before" and "check.after"
288
		 * events.
289
		 *
290
		 * Using this configuration setting, you can either disable all checks completely
291
		 * (0) or display a "Check" button instead of the "Checkout" button (2). In the
292
		 * later case, customers have to click on the "Check" button first to perform
293
		 * the checks and if everything is OK, the "Checkout" button will be displayed
294
		 * that allows the customers to continue the checkout process. If one of the
295
		 * checks fails, the customers have to fix the related basket item and must click
296
		 * on the "Check" button again before they can continue.
297
		 *
298
		 * Available values are:
299
		 *  0 = no product related checks
300
		 *  1 = checks are performed every time when the basket is displayed
301
		 *  2 = checks are performed only when clicking on the "check" button
302
		 *
303
		 * @param integer One of the allowed values (0, 1 or 2)
304
		 * @since 2016.08
305
		 */
306
		$check = (int) $view->config( 'client/html/basket/standard/check', 1 );
307
308
		switch( $check )
309
		{
310
			case 2: if( $val == 0 ) { break; }
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
311
			case 1: $controller->get()->check( ['order/product'] );
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment if this fall-through is intended.
Loading history...
312
			default: return true;
313
		}
314
315
		return false;
316
	}
317
318
319
	/**
320
	 * Saves the basket of the user permanently
321
	 *
322
	 * @param \Aimeos\Base\View\Iface $view View object
323
	 */
324
	protected function saveBasket( \Aimeos\Base\View\Iface $view )
325
	{
326
		$context = $this->context();
327
328
		if( ( $userId = $context->user() ) === null )
329
		{
330
			$msg = $view->translate( 'client', 'You must log in first' );
331
			$view->errors = array_merge( $view->get( 'errors', [] ), [$msg] );
332
333
			return;
334
		}
335
336
		$manager = \Aimeos\MShop::create( $context, 'order/basket' );
337
338
		$item = $manager->create()->setId( md5( microtime( true ) . getmypid() . rand() ) )
339
			->setCustomerId( $userId )->setName( $view->param( 'b_name', date( 'Y-m-d H:i:s' ) ) )
340
			->setItem( \Aimeos\Controller\Frontend::create( $context, 'basket' )->get() );
341
342
		$manager->save( $item );
343
344
		$msg = $view->translate( 'client', 'Basket saved sucessfully' );
345
		$view->infos = array_merge( $view->get( 'infos', [] ), [$msg] );
346
	}
347
348
349
	/**
350
	 * Edits the products specified by the view parameters to the basket.
351
	 *
352
	 * @param \Aimeos\Base\View\Iface $view View object
353
	 */
354
	protected function updateProducts( \Aimeos\Base\View\Iface $view )
355
	{
356
		$controller = \Aimeos\Controller\Frontend::create( $this->context(), 'basket' );
357
		$products = (array) $view->param( 'b_prod', [] );
358
359
		if( ( $position = $view->param( 'b_position', '' ) ) !== '' )
360
		{
361
			$products[] = array(
362
				'position' => $position,
363
				'quantity' => $view->param( 'b_quantity', 1 )
364
			);
365
		}
366
367
		foreach( $products as $values )
368
		{
369
			$controller->updateProduct(
370
				( isset( $values['position'] ) ? (int) $values['position'] : 0 ),
371
				( isset( $values['quantity'] ) ? (float) $values['quantity'] : 1 )
372
			);
373
		}
374
375
		$this->clearCached();
376
	}
377
378
	/** client/html/basket/standard/template-body
379
	 * Relative path to the HTML body template of the basket standard client.
380
	 *
381
	 * The template file contains the HTML code and processing instructions
382
	 * to generate the result shown in the body of the frontend. The
383
	 * configuration string is the path to the template file relative
384
	 * to the templates directory (usually in templates/client/html).
385
	 *
386
	 * You can overwrite the template file configuration in extensions and
387
	 * provide alternative templates. These alternative templates should be
388
	 * named like the default one but suffixed by
389
	 * an unique name. You may use the name of your project for this. If
390
	 * you've implemented an alternative client class as well, it
391
	 * should be suffixed by the name of the new class.
392
	 *
393
	 * @param string Relative path to the template creating code for the HTML page body
394
	 * @since 2014.03
395
	 * @see client/html/basket/standard/template-header
396
	 */
397
398
	/** client/html/basket/standard/template-header
399
	 * Relative path to the HTML header template of the basket standard client.
400
	 *
401
	 * The template file contains the HTML code and processing instructions
402
	 * to generate the HTML code that is inserted into the HTML page header
403
	 * of the rendered page in the frontend. The configuration string is the
404
	 * path to the template file relative to the templates directory (usually
405
	 * in templates/client/html).
406
	 *
407
	 * You can overwrite the template file configuration in extensions and
408
	 * provide alternative templates. These alternative templates should be
409
	 * named like the default one but suffixed by
410
	 * an unique name. You may use the name of your project for this. If
411
	 * you've implemented an alternative client class as well, it
412
	 * should be suffixed by the name of the new class.
413
	 *
414
	 * @param string Relative path to the template creating code for the HTML page head
415
	 * @since 2014.03
416
	 * @see client/html/basket/standard/template-body
417
	 */
418
419
	/** client/html/basket/standard/decorators/excludes
420
	 * Excludes decorators added by the "common" option from the basket standard html client
421
	 *
422
	 * Decorators extend the functionality of a class by adding new aspects
423
	 * (e.g. log what is currently done), executing the methods of the underlying
424
	 * class only in certain conditions (e.g. only for logged in users) or
425
	 * modify what is returned to the caller.
426
	 *
427
	 * This option allows you to remove a decorator added via
428
	 * "client/html/common/decorators/default" before they are wrapped
429
	 * around the html client.
430
	 *
431
	 *  client/html/basket/standard/decorators/excludes = array( 'decorator1' )
432
	 *
433
	 * This would remove the decorator named "decorator1" from the list of
434
	 * common decorators ("\Aimeos\Client\Html\Common\Decorator\*") added via
435
	 * "client/html/common/decorators/default" to the html client.
436
	 *
437
	 * @param array List of decorator names
438
	 * @since 2014.05
439
	 * @see client/html/common/decorators/default
440
	 * @see client/html/basket/standard/decorators/global
441
	 * @see client/html/basket/standard/decorators/local
442
	 */
443
444
	/** client/html/basket/standard/decorators/global
445
	 * Adds a list of globally available decorators only to the basket standard html client
446
	 *
447
	 * Decorators extend the functionality of a class by adding new aspects
448
	 * (e.g. log what is currently done), executing the methods of the underlying
449
	 * class only in certain conditions (e.g. only for logged in users) or
450
	 * modify what is returned to the caller.
451
	 *
452
	 * This option allows you to wrap global decorators
453
	 * ("\Aimeos\Client\Html\Common\Decorator\*") around the html client.
454
	 *
455
	 *  client/html/basket/standard/decorators/global = array( 'decorator1' )
456
	 *
457
	 * This would add the decorator named "decorator1" defined by
458
	 * "\Aimeos\Client\Html\Common\Decorator\Decorator1" only to the html client.
459
	 *
460
	 * @param array List of decorator names
461
	 * @since 2014.05
462
	 * @see client/html/common/decorators/default
463
	 * @see client/html/basket/standard/decorators/excludes
464
	 * @see client/html/basket/standard/decorators/local
465
	 */
466
467
	/** client/html/basket/standard/decorators/local
468
	 * Adds a list of local decorators only to the basket standard html client
469
	 *
470
	 * Decorators extend the functionality of a class by adding new aspects
471
	 * (e.g. log what is currently done), executing the methods of the underlying
472
	 * class only in certain conditions (e.g. only for logged in users) or
473
	 * modify what is returned to the caller.
474
	 *
475
	 * This option allows you to wrap local decorators
476
	 * ("\Aimeos\Client\Html\Basket\Decorator\*") around the html client.
477
	 *
478
	 *  client/html/basket/standard/decorators/local = array( 'decorator2' )
479
	 *
480
	 * This would add the decorator named "decorator2" defined by
481
	 * "\Aimeos\Client\Html\Basket\Decorator\Decorator2" only to the html client.
482
	 *
483
	 * @param array List of decorator names
484
	 * @since 2014.05
485
	 * @see client/html/common/decorators/default
486
	 * @see client/html/basket/standard/decorators/excludes
487
	 * @see client/html/basket/standard/decorators/global
488
	 */
489
}
490