Completed
Push — master ( a13539...bd1b6d )
by Aimeos
04:27
created

Standard   C

Complexity

Total Complexity 55

Size/Duplication

Total Lines 595
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 185
dl 0
loc 595
rs 6
c 4
b 0
f 0
wmc 55

12 Methods

Rating   Name   Duplication   Size   Complexity  
A getSubClient() 0 76 1
A search() 0 58 3
A export() 0 30 4
A copy() 0 30 4
A render() 0 25 1
C toArray() 0 62 14
A get() 0 31 4
A create() 0 31 4
C fromArray() 0 73 14
A getSubClientNames() 0 36 1
A save() 0 31 4
A getOrderBaseItems() 0 9 1

How to fix   Complexity   

Complex Class

Complex classes like Standard often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Standard, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2017-2020
6
 * @package Admin
7
 * @subpackage JQAdm
8
 */
9
10
11
namespace Aimeos\Admin\JQAdm\Order;
12
13
sprintf( 'order' ); // for translation
14
15
16
/**
17
 * Default implementation of order JQAdm client.
18
 *
19
 * @package Admin
20
 * @subpackage JQAdm
21
 */
22
class Standard
23
	extends \Aimeos\Admin\JQAdm\Common\Admin\Factory\Base
24
	implements \Aimeos\Admin\JQAdm\Common\Admin\Factory\Iface
25
{
26
	/**
27
	 * Copies a resource
28
	 *
29
	 * @return string|null HTML output
30
	 */
31
	public function copy() : ?string
32
	{
33
		$view = $this->getView();
34
		$context = $this->getContext();
35
36
		try
37
		{
38
			if( ( $id = $view->param( 'id' ) ) === null ) {
39
				throw new \Aimeos\Admin\JQAdm\Exception( sprintf( 'Required parameter "%1$s" is missing', 'id' ) );
40
			}
41
42
			$manager = \Aimeos\MShop::create( $context, 'order/base' );
43
			$view->item = $manager->load( $id );
44
45
			$view->itemData = $this->toArray( $view->item, true );
46
			$view->itemSubparts = $this->getSubClientNames();
47
			$view->itemBody = '';
48
49
			foreach( $this->getSubClients() as $idx => $client )
50
			{
51
				$view->tabindex = ++$idx + 1;
52
				$view->itemBody .= $client->copy();
53
			}
54
		}
55
		catch( \Exception $e )
56
		{
57
			$this->report( $e, 'copy' );
58
		}
59
60
		return $this->render( $view );
61
	}
62
63
64
	/**
65
	 * Creates a new resource
66
	 *
67
	 * @return string|null HTML output
68
	 */
69
	public function create() : ?string
70
	{
71
		$view = $this->getView();
72
		$context = $this->getContext();
73
74
		try
75
		{
76
			$data = $view->param( 'item', [] );
77
78
			if( !isset( $view->item ) ) {
79
				$view->item = \Aimeos\MShop::create( $context, 'order/base' )->createItem();
80
			}
81
82
			$data['order.siteid'] = $view->item->getSiteId();
83
84
			$view->itemSubparts = $this->getSubClientNames();
85
			$view->itemData = $data;
86
			$view->itemBody = '';
87
88
			foreach( $this->getSubClients() as $idx => $client )
89
			{
90
				$view->tabindex = ++$idx + 1;
91
				$view->itemBody .= $client->create();
92
			}
93
		}
94
		catch( \Exception $e )
95
		{
96
			$this->report( $e, 'create' );
97
		}
98
99
		return $this->render( $view );
100
	}
101
102
103
	/**
104
	 * Exports a resource
105
	 *
106
	 * @return string Admin output to display
107
	 */
108
	public function export() : ?string
109
	{
110
		$view = $this->getView();
111
		$context = $this->getContext();
112
113
		try
114
		{
115
			$params = $this->storeSearchParams( $view->param(), 'order' );
116
			$msg = ['sitecode' => $context->getLocale()->getSiteItem()->getCode()];
117
118
			if( isset( $params['filter'] ) ) {
119
				$msg['filter'] = $this->getCriteriaConditions( (array) $params['filter'] );
120
			}
121
122
			if( isset( $params['sort'] ) ) {
123
				$msg['sort'] = $this->getCriteriaSortations( (array) $params['sort'] );
124
			}
125
126
			$mq = $context->getMessageQueueManager()->get( 'mq-admin' )->getQueue( 'order-export' );
127
			$mq->add( json_encode( $msg ) );
128
129
			$msg = $context->getI18n()->dt( 'admin', 'Your export will be available in a few minutes for download' );
130
			$view->info = $view->get( 'info', [] ) + ['order-item' => $msg];
131
		}
132
		catch( \Exception $e )
133
		{
134
			$this->report( $e, 'export' );
135
		}
136
137
		return $this->search();
138
	}
139
140
141
	/**
142
	 * Returns a single resource
143
	 *
144
	 * @return string|null HTML output
145
	 */
146
	public function get() : ?string
147
	{
148
		$view = $this->getView();
149
		$context = $this->getContext();
150
151
		try
152
		{
153
			if( ( $id = $view->param( 'id' ) ) === null ) {
154
				throw new \Aimeos\Admin\JQAdm\Exception( sprintf( 'Required parameter "%1$s" is missing', 'id' ) );
155
			}
156
157
			$manager = \Aimeos\MShop::create( $context, 'order/base' );
158
			$refs = ['order/base/address', 'order/base/coupon', 'order/base/product', 'order/base/service'];
159
160
			$view->item = $manager->getItem( $id, $refs );
161
			$view->itemSubparts = $this->getSubClientNames();
162
			$view->itemData = $this->toArray( $view->item );
163
			$view->itemBody = '';
164
165
			foreach( $this->getSubClients() as $idx => $client )
166
			{
167
				$view->tabindex = ++$idx + 1;
168
				$view->itemBody .= $client->get();
169
			}
170
		}
171
		catch( \Exception $e )
172
		{
173
			$this->report( $e, 'get' );
174
		}
175
176
		return $this->render( $view );
177
	}
178
179
180
	/**
181
	 * Saves the data
182
	 *
183
	 * @return string|null HTML output
184
	 */
185
	public function save() : ?string
186
	{
187
		$view = $this->getView();
188
		$context = $this->getContext();
189
190
		$manager = \Aimeos\MShop::create( $context, 'order/base' );
191
		$manager->begin();
192
193
		try
194
		{
195
			$item = $this->fromArray( $view->param( 'item', [] ) );
196
			$view->item = $item->getId() ? $item : $manager->store( clone $item );
197
			$view->itemBody = '';
198
199
			foreach( $this->getSubClients() as $client ) {
200
				$view->itemBody .= $client->save();
201
			}
202
203
			$manager->store( clone $view->item );
204
			$manager->commit();
205
206
			$this->nextAction( $view, $view->param( 'next' ), 'order', $view->item->getId(), 'save' );
207
			return null;
208
		}
209
		catch( \Exception $e )
210
		{
211
			$manager->rollback();
212
			$this->report( $e, 'save' );
213
		}
214
215
		return $this->create();
216
	}
217
218
219
	/**
220
	 * Returns a list of resource according to the conditions
221
	 *
222
	 * @return string|null HTML output
223
	 */
224
	public function search() : ?string
225
	{
226
		$view = $this->getView();
227
		$context = $this->getContext();
228
229
		try
230
		{
231
			$total = 0;
232
			$params = $this->storeSearchParams( $view->param(), 'order' );
233
			$manager = \Aimeos\MShop::create( $context, 'order' );
234
235
			$search = $manager->createSearch();
236
			$search->setSortations( [$search->sort( '-', 'order.id' )] );
237
			$search = $this->initCriteria( $search, $params );
238
			$search->setConditions( $search->combine( '&&', [
239
				$search->compare( '!=', 'order.base.product.siteid', '' ),
240
				$search->getConditions()
241
			] ) );
242
243
			$view->items = $manager->searchItems( $search, [], $total );
244
			$view->baseItems = $this->getOrderBaseItems( $view->items );
245
			$view->filterAttributes = $manager->getSearchAttributes( true );
246
			$view->filterOperators = $search->getOperators();
247
			$view->total = $total;
248
			$view->itemBody = '';
249
250
			foreach( $this->getSubClients() as $client ) {
251
				$view->itemBody .= $client->search();
252
			}
253
		}
254
		catch( \Exception $e )
255
		{
256
			$this->report( $e, 'search' );
257
		}
258
259
		/** admin/jqadm/order/template-list
260
		 * Relative path to the HTML body template for the order list.
261
		 *
262
		 * The template file contains the HTML code and processing instructions
263
		 * to generate the result shown in the body of the frontend. The
264
		 * configuration string is the path to the template file relative
265
		 * to the templates directory (usually in admin/jqadm/templates).
266
		 *
267
		 * You can overwrite the template file configuration in extensions and
268
		 * provide alternative templates. These alternative templates should be
269
		 * named like the default one but with the string "default" replaced by
270
		 * an unique name. You may use the name of your project for this. If
271
		 * you've implemented an alternative client class as well, "default"
272
		 * should be replaced by the name of the new class.
273
		 *
274
		 * @param string Relative path to the template creating the HTML code
275
		 * @since 2016.04
276
		 * @category Developer
277
		 */
278
		$tplconf = 'admin/jqadm/order/template-list';
279
		$default = 'order/list-standard';
280
281
		return $view->render( $view->config( $tplconf, $default ) );
282
	}
283
284
285
	/**
286
	 * Returns the sub-client given by its name.
287
	 *
288
	 * @param string $type Name of the client type
289
	 * @param string|null $name Name of the sub-client (Default if null)
290
	 * @return \Aimeos\Admin\JQAdm\Iface Sub-client object
291
	 */
292
	public function getSubClient( string $type, string $name = null ) : \Aimeos\Admin\JQAdm\Iface
293
	{
294
		/** admin/jqadm/order/decorators/excludes
295
		 * Excludes decorators added by the "common" option from the order JQAdm client
296
		 *
297
		 * Decorators extend the functionality of a class by adding new aspects
298
		 * (e.g. log what is currently done), executing the methods of the underlying
299
		 * class only in certain conditions (e.g. only for logged in users) or
300
		 * modify what is returned to the caller.
301
		 *
302
		 * This option allows you to remove a decorator added via
303
		 * "client/jqadm/common/decorators/default" before they are wrapped
304
		 * around the JQAdm client.
305
		 *
306
		 *  admin/jqadm/order/decorators/excludes = array( 'decorator1' )
307
		 *
308
		 * This would remove the decorator named "decorator1" from the list of
309
		 * common decorators ("\Aimeos\Admin\JQAdm\Common\Decorator\*") added via
310
		 * "client/jqadm/common/decorators/default" to the JQAdm client.
311
		 *
312
		 * @param array List of decorator names
313
		 * @since 2016.01
314
		 * @category Developer
315
		 * @see admin/jqadm/common/decorators/default
316
		 * @see admin/jqadm/order/decorators/global
317
		 * @see admin/jqadm/order/decorators/local
318
		 */
319
320
		/** admin/jqadm/order/decorators/global
321
		 * Adds a list of globally available decorators only to the order JQAdm client
322
		 *
323
		 * Decorators extend the functionality of a class by adding new aspects
324
		 * (e.g. log what is currently done), executing the methods of the underlying
325
		 * class only in certain conditions (e.g. only for logged in users) or
326
		 * modify what is returned to the caller.
327
		 *
328
		 * This option allows you to wrap global decorators
329
		 * ("\Aimeos\Admin\JQAdm\Common\Decorator\*") around the JQAdm client.
330
		 *
331
		 *  admin/jqadm/order/decorators/global = array( 'decorator1' )
332
		 *
333
		 * This would add the decorator named "decorator1" defined by
334
		 * "\Aimeos\Admin\JQAdm\Common\Decorator\Decorator1" only to the JQAdm client.
335
		 *
336
		 * @param array List of decorator names
337
		 * @since 2016.01
338
		 * @category Developer
339
		 * @see admin/jqadm/common/decorators/default
340
		 * @see admin/jqadm/order/decorators/excludes
341
		 * @see admin/jqadm/order/decorators/local
342
		 */
343
344
		/** admin/jqadm/order/decorators/local
345
		 * Adds a list of local decorators only to the order JQAdm client
346
		 *
347
		 * Decorators extend the functionality of a class by adding new aspects
348
		 * (e.g. log what is currently done), executing the methods of the underlying
349
		 * class only in certain conditions (e.g. only for logged in users) or
350
		 * modify what is returned to the caller.
351
		 *
352
		 * This option allows you to wrap local decorators
353
		 * ("\Aimeos\Admin\JQAdm\Order\Decorator\*") around the JQAdm client.
354
		 *
355
		 *  admin/jqadm/order/decorators/local = array( 'decorator2' )
356
		 *
357
		 * This would add the decorator named "decorator2" defined by
358
		 * "\Aimeos\Admin\JQAdm\Order\Decorator\Decorator2" only to the JQAdm client.
359
		 *
360
		 * @param array List of decorator names
361
		 * @since 2016.01
362
		 * @category Developer
363
		 * @see admin/jqadm/common/decorators/default
364
		 * @see admin/jqadm/order/decorators/excludes
365
		 * @see admin/jqadm/order/decorators/global
366
		 */
367
		return $this->createSubClient( 'order/' . $type, $name );
368
	}
369
370
371
	/**
372
	 * Returns the base order items (baskets) for the given order items (invoices)
373
	 *
374
	 * @param \Aimeos\Map $orderItems List of order items implementing \Aimeos\MShop\Order\Item\Iface
375
	 * @return \Aimeos\Map List of order base items implementing \Aimeos\MShop\Order\Item\Base\Iface
376
	 */
377
	protected function getOrderBaseItems( \Aimeos\Map $orderItems ) : \Aimeos\Map
378
	{
379
		$baseIds = $orderItems->getBaseId()->toArray();
380
		$manager = \Aimeos\MShop::create( $this->getContext(), 'order/base' );
381
382
		$search = $manager->createSearch()->setSlice( 0, count( $baseIds ) );
383
		$search->setConditions( $search->compare( '==', 'order.base.id', $baseIds ) );
384
385
		return $manager->searchItems( $search, ['order/base/address', 'order/base/coupon', 'order/base/service'] );
386
	}
387
388
389
	/**
390
	 * Returns the list of sub-client names configured for the client.
391
	 *
392
	 * @return array List of JQAdm client names
393
	 */
394
	protected function getSubClientNames() : array
395
	{
396
		/** admin/jqadm/order/standard/subparts
397
		 * List of JQAdm sub-clients rendered within the order section
398
		 *
399
		 * The output of the frontend is composed of the code generated by the JQAdm
400
		 * clients. Each JQAdm client can consist of serveral (or none) sub-clients
401
		 * that are responsible for rendering certain sub-parts of the output. The
402
		 * sub-clients can contain JQAdm clients themselves and therefore a
403
		 * hierarchical tree of JQAdm clients is composed. Each JQAdm client creates
404
		 * the output that is placed inside the container of its parent.
405
		 *
406
		 * At first, always the JQAdm code generated by the parent is printed, then
407
		 * the JQAdm code of its sub-clients. The order of the JQAdm sub-clients
408
		 * determines the order of the output of these sub-clients inside the parent
409
		 * container. If the configured list of clients is
410
		 *
411
		 *  array( "subclient1", "subclient2" )
412
		 *
413
		 * you can easily change the order of the output by reordering the subparts:
414
		 *
415
		 *  admin/jqadm/<clients>/subparts = array( "subclient1", "subclient2" )
416
		 *
417
		 * You can also remove one or more parts if they shouldn't be rendered:
418
		 *
419
		 *  admin/jqadm/<clients>/subparts = array( "subclient1" )
420
		 *
421
		 * As the clients only generates structural JQAdm, the layout defined via CSS
422
		 * should support adding, removing or reordering content by a fluid like
423
		 * design.
424
		 *
425
		 * @param array List of sub-client names
426
		 * @since 2016.01
427
		 * @category Developer
428
		 */
429
		return $this->getContext()->getConfig()->get( 'admin/jqadm/order/standard/subparts', [] );
430
	}
431
432
433
	/**
434
	 * Creates new and updates existing items using the data array
435
	 *
436
	 * @param array $data Data array
437
	 * @return \Aimeos\MShop\Order\Item\Base\Iface New order item object
438
	 */
439
	protected function fromArray( array $data ) : \Aimeos\MShop\Order\Item\Base\Iface
440
	{
441
		$manager = \Aimeos\MShop::create( $this->getContext(), 'order/base' );
442
		$attrManager = \Aimeos\MShop::create( $this->getContext(), 'order/base/service/attribute' );
443
444
		if( isset( $data['order.base.id'] ) ) {
445
			$basket = $manager->load( $data['order.base.id'] )->off();
446
		} else {
447
			$basket = $manager->createItem()->off();
448
		}
449
450
		$basket->fromArray( $data, true );
451
452
		foreach( $basket->getProducts() as $pos => $product )
453
		{
454
			if( isset( $data['product'][$pos]['order.base.product.status'] ) ) {
455
				$product->setStatus( $data['product'][$pos]['order.base.product.status'] );
456
			}
457
		}
458
459
		foreach( $basket->getAddresses() as $type => $addresses )
460
		{
461
			foreach( $addresses as $pos => $address )
462
			{
463
				if( isset( $data['address'][$type][$pos] ) ) {
464
					$list = (array) $data['address'][$type][$pos];
465
					$basket->addAddress( $address->fromArray( $list, true ), $type, $pos );
466
				} else {
467
					$basket->deleteAddress( $type, $pos );
468
				}
469
			}
470
		}
471
472
		foreach( $basket->getServices() as $type => $services )
473
		{
474
			foreach( $services as $serviceId => $service )
475
			{
476
				$list = [];
477
				$attrItems = $service->getAttributeItems();
478
479
				if( isset( $data['service'][$type][$serviceId] ) )
480
				{
481
					foreach( (array) $data['service'][$type][$serviceId] as $key => $pair )
482
					{
483
						foreach( $pair as $pos => $value ) {
484
							$list[$pos][$key] = $value;
485
						}
486
					}
487
488
					foreach( $list as $array )
489
					{
490
						if( isset( $attrItems[$array['order.base.service.attribute.id']] ) )
491
						{
492
							$attrItem = $attrItems[$array['order.base.service.attribute.id']];
493
							unset( $attrItems[$array['order.base.service.attribute.id']] );
494
						}
495
						else
496
						{
497
							$attrItem = $attrManager->createItem();
498
						}
499
500
						$attrItem->fromArray( $array, true );
501
						$attrItem->setParentId( $service->getId() );
502
503
						$item = $attrManager->saveItem( $attrItem );
0 ignored issues
show
Unused Code introduced by
The assignment to $item is dead and can be removed.
Loading history...
Bug introduced by
The method saveItem() does not exist on Aimeos\MShop\Common\Manager\Iface. Did you maybe mean saveItems()? ( Ignorable by Annotation )

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

503
						/** @scrutinizer ignore-call */ 
504
      $item = $attrManager->saveItem( $attrItem );

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...
504
					}
505
				}
506
507
				$attrManager->deleteItems( $attrItems->toArray() );
508
			}
509
		}
510
511
		return $basket;
512
	}
513
514
515
	/**
516
	 * Constructs the data array for the view from the given item
517
	 *
518
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $item Order base item object
519
	 * @return string[] Multi-dimensional associative list of item data
520
	 */
521
	protected function toArray( \Aimeos\MShop\Order\Item\Base\Iface $item, bool $copy = false ) : array
522
	{
523
		$siteId = $this->getContext()->getLocale()->getSiteId();
524
		$data = $item->toArray( true );
525
526
		if( $item->getCustomerId() != '' )
527
		{
528
			$manager = \Aimeos\MShop::create( $this->getContext(), 'customer' );
529
530
			try {
531
				$data += $manager->getItem( $item->getCustomerId() )->toArray();
532
			} catch( \Exception $e ) {};
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
533
		}
534
535
536
		if( $copy === true )
537
		{
538
			$data['order.base.siteid'] = $siteId;
539
			$data['order.base.id'] = '';
540
		}
541
542
		foreach( $item->getAddresses() as $type => $addresses )
543
		{
544
			foreach( $addresses as $pos => $addrItem )
545
			{
546
				$list = $addrItem->toArray( true );
547
548
				foreach( $list as $key => $value ) {
549
					$data['address'][$type][$pos][$key] = $value;
550
				}
551
552
				if( $copy === true )
553
				{
554
					$data['address'][$type][$pos]['order.base.address.siteid'] = $siteId;
555
					$data['address'][$type][$pos]['order.base.address.id'] = '';
556
				}
557
			}
558
		}
559
560
		if( $copy !== true )
561
		{
562
			foreach( $item->getServices() as $type => $services )
563
			{
564
				foreach( $services as $serviceItem )
565
				{
566
					$serviceId = $serviceItem->getServiceId();
567
568
					foreach( $serviceItem->getAttributeItems() as $attrItem )
569
					{
570
						foreach( $attrItem->toArray( true ) as $key => $value ) {
571
							$data['service'][$type][$serviceId][$key][] = $value;
572
						}
573
					}
574
				}
575
			}
576
		}
577
578
		foreach( $item->getProducts() as $pos => $productItem ) {
579
			$data['product'][$pos]['order.base.product.status'] = $productItem->getStatus();
580
		}
581
582
		return $data;
583
	}
584
585
586
	/**
587
	 * Returns the rendered template including the view data
588
	 *
589
	 * @param \Aimeos\MW\View\Iface $view View object with data assigned
590
	 * @return string|null HTML output
591
	 */
592
	protected function render( \Aimeos\MW\View\Iface $view ) : string
593
	{
594
		/** admin/jqadm/order/template-item
595
		 * Relative path to the HTML body template for the order item.
596
		 *
597
		 * The template file contains the HTML code and processing instructions
598
		 * to generate the result shown in the body of the frontend. The
599
		 * configuration string is the path to the template file relative
600
		 * to the templates directory (usually in admin/jqadm/templates).
601
		 *
602
		 * You can overwrite the template file configuration in extensions and
603
		 * provide alternative templates. These alternative templates should be
604
		 * named like the default one but with the string "default" replaced by
605
		 * an unique name. You may use the name of your project for this. If
606
		 * you've implemented an alternative client class as well, "default"
607
		 * should be replaced by the name of the new class.
608
		 *
609
		 * @param string Relative path to the template creating the HTML code
610
		 * @since 2016.04
611
		 * @category Developer
612
		 */
613
		$tplconf = 'admin/jqadm/order/template-item';
614
		$default = 'order/item-standard';
615
616
		return $view->render( $view->config( $tplconf, $default ) );
617
	}
618
}
619