Completed
Push — master ( 7818a8...a3fccc )
by Aimeos
04:08
created

Standard::search()   A

Complexity

Conditions 2
Paths 12

Size

Total Lines 50
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 2
eloc 20
c 4
b 0
f 0
nc 12
nop 0
dl 0
loc 50
rs 9.6
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
	 * Adds the required data used in the template
28
	 *
29
	 * @param \Aimeos\MW\View\Iface $view View object
30
	 * @return \Aimeos\MW\View\Iface View object with assigned parameters
31
	 */
32
	public function addData( \Aimeos\MW\View\Iface $view ) : \Aimeos\MW\View\Iface
33
	{
34
		$view->itemSubparts = $this->getSubClientNames();
35
		return $view;
36
	}
37
38
39
	/**
40
	 * Copies a resource
41
	 *
42
	 * @return string|null HTML output
43
	 */
44
	public function copy() : ?string
45
	{
46
		$view = $this->getObject()->addData( $this->getView() );
47
48
		try
49
		{
50
			if( ( $id = $view->param( 'id' ) ) === null ) {
51
				throw new \Aimeos\Admin\JQAdm\Exception( sprintf( 'Required parameter "%1$s" is missing', 'id' ) );
52
			}
53
54
			$manager = \Aimeos\MShop::create( $this->getContext(), 'order/base' );
55
			$view->item = $manager->load( $id );
56
57
			$view->itemData = $this->toArray( $view->item, true );
58
			$view->itemBody = parent::copy();
59
		}
60
		catch( \Exception $e )
61
		{
62
			$this->report( $e, 'copy' );
63
		}
64
65
		return $this->render( $view );
66
	}
67
68
69
	/**
70
	 * Creates a new resource
71
	 *
72
	 * @return string|null HTML output
73
	 */
74
	public function create() : ?string
75
	{
76
		$view = $this->getObject()->addData( $this->getView() );
77
78
		try
79
		{
80
			$data = $view->param( 'item', [] );
81
82
			if( !isset( $view->item ) ) {
83
				$view->item = \Aimeos\MShop::create( $this->getContext(), 'order/base' )->createItem();
84
			}
85
86
			$data['order.siteid'] = $view->item->getSiteId();
87
88
			$view->itemData = array_replace_recursive( $this->toArray( $view->item ), $data );
89
			$view->itemBody = parent::create();
90
		}
91
		catch( \Exception $e )
92
		{
93
			$this->report( $e, 'create' );
94
		}
95
96
		return $this->render( $view );
97
	}
98
99
100
	/**
101
	 * Exports a resource
102
	 *
103
	 * @return string Admin output to display
104
	 */
105
	public function export() : ?string
106
	{
107
		$view = $this->getView();
108
		$context = $this->getContext();
109
110
		try
111
		{
112
			$params = $this->storeSearchParams( $view->param(), 'order' );
113
			$msg = ['sitecode' => $context->getLocale()->getSiteItem()->getCode()];
114
115
			if( isset( $params['filter'] ) ) {
116
				$msg['filter'] = $this->getCriteriaConditions( (array) $params['filter'] );
117
			}
118
119
			if( isset( $params['sort'] ) ) {
120
				$msg['sort'] = $this->getCriteriaSortations( (array) $params['sort'] );
121
			}
122
123
			$queue = $view->param( 'queue', 'order-export' );
124
			$mq = $context->getMessageQueueManager()->get( 'mq-admin' )->getQueue( $queue );
125
			$mq->add( json_encode( $msg ) );
126
127
			$msg = $context->getI18n()->dt( 'admin', 'Your export will be available in a few minutes for download' );
128
			$view->info = $view->get( 'info', [] ) + ['order-item' => $msg];
129
		}
130
		catch( \Exception $e )
131
		{
132
			$this->report( $e, 'export' );
133
		}
134
135
		return $this->search();
136
	}
137
138
139
	/**
140
	 * Returns a single resource
141
	 *
142
	 * @return string|null HTML output
143
	 */
144
	public function get() : ?string
145
	{
146
		$view = $this->getObject()->addData( $this->getView() );
147
148
		try
149
		{
150
			if( ( $id = $view->param( 'id' ) ) === null ) {
151
				throw new \Aimeos\Admin\JQAdm\Exception( sprintf( 'Required parameter "%1$s" is missing', 'id' ) );
152
			}
153
154
			$manager = \Aimeos\MShop::create( $this->getContext(), 'order/base' );
155
			$refs = ['order/base/address', 'order/base/coupon', 'order/base/product', 'order/base/service'];
156
157
			$view->item = $manager->getItem( $id, $refs );
158
			$view->itemData = $this->toArray( $view->item );
159
			$view->itemBody = parent::get();
160
		}
161
		catch( \Exception $e )
162
		{
163
			$this->report( $e, 'get' );
164
		}
165
166
		return $this->render( $view );
167
	}
168
169
170
	/**
171
	 * Saves the data
172
	 *
173
	 * @return string|null HTML output
174
	 */
175
	public function save() : ?string
176
	{
177
		$view = $this->getView();
178
179
		$manager = \Aimeos\MShop::create( $this->getContext(), 'order/base' );
180
		$manager->begin();
181
182
		try
183
		{
184
			$item = $this->fromArray( $view->param( 'item', [] ) );
185
			$view->item = $item->getId() ? $item : $manager->store( clone $item );
186
			$view->itemBody = parent::save();
187
188
			$manager->store( clone $view->item );
189
			$manager->commit();
190
191
			return $this->redirect( 'order', $view->param( 'next' ), $view->item->getId(), 'save' );
192
		}
193
		catch( \Exception $e )
194
		{
195
			$manager->rollback();
196
			$this->report( $e, 'save' );
197
		}
198
199
		return $this->create();
200
	}
201
202
203
	/**
204
	 * Returns a list of resource according to the conditions
205
	 *
206
	 * @return string|null HTML output
207
	 */
208
	public function search() : ?string
209
	{
210
		$view = $this->getView();
211
212
		try
213
		{
214
			$total = 0;
215
			$context = $this->getContext();
216
			$manager = \Aimeos\MShop::create( $context, 'order' );
217
			$params = $this->storeSearchParams( $view->param(), 'order' );
218
219
			$search = $manager->createSearch( false, true );
0 ignored issues
show
Unused Code introduced by
The call to Aimeos\MShop\Common\Manager\Iface::createSearch() has too many arguments starting with true. ( Ignorable by Annotation )

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

219
			/** @scrutinizer ignore-call */ 
220
   $search = $manager->createSearch( false, true );

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
220
			$search->setSortations( [$search->sort( '-', 'order.id' )] );
221
			$search = $this->initCriteria( $search, $params );
222
223
			$view->items = $manager->searchItems( $search, [], $total );
224
			$view->baseItems = $this->getOrderBaseItems( $view->items );
225
			$view->filterAttributes = $manager->getSearchAttributes( true );
226
			$view->filterOperators = $search->getOperators();
227
			$view->itemBody = parent::search();
228
			$view->total = $total;
229
		}
230
		catch( \Exception $e )
231
		{
232
			$this->report( $e, 'search' );
233
		}
234
235
		/** admin/jqadm/order/template-list
236
		 * Relative path to the HTML body template for the order list.
237
		 *
238
		 * The template file contains the HTML code and processing instructions
239
		 * to generate the result shown in the body of the frontend. The
240
		 * configuration string is the path to the template file relative
241
		 * to the templates directory (usually in admin/jqadm/templates).
242
		 *
243
		 * You can overwrite the template file configuration in extensions and
244
		 * provide alternative templates. These alternative templates should be
245
		 * named like the default one but with the string "default" replaced by
246
		 * an unique name. You may use the name of your project for this. If
247
		 * you've implemented an alternative client class as well, "default"
248
		 * should be replaced by the name of the new class.
249
		 *
250
		 * @param string Relative path to the template creating the HTML code
251
		 * @since 2016.04
252
		 * @category Developer
253
		 */
254
		$tplconf = 'admin/jqadm/order/template-list';
255
		$default = 'order/list-standard';
256
257
		return $view->render( $view->config( $tplconf, $default ) );
258
	}
259
260
261
	/**
262
	 * Returns the sub-client given by its name.
263
	 *
264
	 * @param string $type Name of the client type
265
	 * @param string|null $name Name of the sub-client (Default if null)
266
	 * @return \Aimeos\Admin\JQAdm\Iface Sub-client object
267
	 */
268
	public function getSubClient( string $type, string $name = null ) : \Aimeos\Admin\JQAdm\Iface
269
	{
270
		/** admin/jqadm/order/decorators/excludes
271
		 * Excludes decorators added by the "common" option from the order JQAdm client
272
		 *
273
		 * Decorators extend the functionality of a class by adding new aspects
274
		 * (e.g. log what is currently done), executing the methods of the underlying
275
		 * class only in certain conditions (e.g. only for logged in users) or
276
		 * modify what is returned to the caller.
277
		 *
278
		 * This option allows you to remove a decorator added via
279
		 * "client/jqadm/common/decorators/default" before they are wrapped
280
		 * around the JQAdm client.
281
		 *
282
		 *  admin/jqadm/order/decorators/excludes = array( 'decorator1' )
283
		 *
284
		 * This would remove the decorator named "decorator1" from the list of
285
		 * common decorators ("\Aimeos\Admin\JQAdm\Common\Decorator\*") added via
286
		 * "client/jqadm/common/decorators/default" to the JQAdm client.
287
		 *
288
		 * @param array List of decorator names
289
		 * @since 2016.01
290
		 * @category Developer
291
		 * @see admin/jqadm/common/decorators/default
292
		 * @see admin/jqadm/order/decorators/global
293
		 * @see admin/jqadm/order/decorators/local
294
		 */
295
296
		/** admin/jqadm/order/decorators/global
297
		 * Adds a list of globally available decorators only to the order JQAdm client
298
		 *
299
		 * Decorators extend the functionality of a class by adding new aspects
300
		 * (e.g. log what is currently done), executing the methods of the underlying
301
		 * class only in certain conditions (e.g. only for logged in users) or
302
		 * modify what is returned to the caller.
303
		 *
304
		 * This option allows you to wrap global decorators
305
		 * ("\Aimeos\Admin\JQAdm\Common\Decorator\*") around the JQAdm client.
306
		 *
307
		 *  admin/jqadm/order/decorators/global = array( 'decorator1' )
308
		 *
309
		 * This would add the decorator named "decorator1" defined by
310
		 * "\Aimeos\Admin\JQAdm\Common\Decorator\Decorator1" only 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/excludes
317
		 * @see admin/jqadm/order/decorators/local
318
		 */
319
320
		/** admin/jqadm/order/decorators/local
321
		 * Adds a list of local 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 local decorators
329
		 * ("\Aimeos\Admin\JQAdm\Order\Decorator\*") around the JQAdm client.
330
		 *
331
		 *  admin/jqadm/order/decorators/local = array( 'decorator2' )
332
		 *
333
		 * This would add the decorator named "decorator2" defined by
334
		 * "\Aimeos\Admin\JQAdm\Order\Decorator\Decorator2" 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/global
342
		 */
343
		return $this->createSubClient( 'order/' . $type, $name );
344
	}
345
346
347
	/**
348
	 * Returns the base order items (baskets) for the given order items (invoices)
349
	 *
350
	 * @param \Aimeos\Map $orderItems List of order items implementing \Aimeos\MShop\Order\Item\Iface
351
	 * @return \Aimeos\Map List of order base items implementing \Aimeos\MShop\Order\Item\Base\Iface
352
	 */
353
	protected function getOrderBaseItems( \Aimeos\Map $orderItems ) : \Aimeos\Map
354
	{
355
		$baseIds = $orderItems->getBaseId()->toArray();
356
		$manager = \Aimeos\MShop::create( $this->getContext(), 'order/base' );
357
358
		$search = $manager->createSearch()->setSlice( 0, count( $baseIds ) );
359
		$search->setConditions( $search->compare( '==', 'order.base.id', $baseIds ) );
360
361
		$domains = ['order/base/address', 'order/base/coupon', 'order/base/product', 'order/base/service'];
362
		return $manager->searchItems( $search, $domains );
363
	}
364
365
366
	/**
367
	 * Returns the list of sub-client names configured for the client.
368
	 *
369
	 * @return array List of JQAdm client names
370
	 */
371
	protected function getSubClientNames() : array
372
	{
373
		/** admin/jqadm/order/standard/subparts
374
		 * List of JQAdm sub-clients rendered within the order section
375
		 *
376
		 * The output of the frontend is composed of the code generated by the JQAdm
377
		 * clients. Each JQAdm client can consist of serveral (or none) sub-clients
378
		 * that are responsible for rendering certain sub-parts of the output. The
379
		 * sub-clients can contain JQAdm clients themselves and therefore a
380
		 * hierarchical tree of JQAdm clients is composed. Each JQAdm client creates
381
		 * the output that is placed inside the container of its parent.
382
		 *
383
		 * At first, always the JQAdm code generated by the parent is printed, then
384
		 * the JQAdm code of its sub-clients. The order of the JQAdm sub-clients
385
		 * determines the order of the output of these sub-clients inside the parent
386
		 * container. If the configured list of clients is
387
		 *
388
		 *  array( "subclient1", "subclient2" )
389
		 *
390
		 * you can easily change the order of the output by reordering the subparts:
391
		 *
392
		 *  admin/jqadm/<clients>/subparts = array( "subclient1", "subclient2" )
393
		 *
394
		 * You can also remove one or more parts if they shouldn't be rendered:
395
		 *
396
		 *  admin/jqadm/<clients>/subparts = array( "subclient1" )
397
		 *
398
		 * As the clients only generates structural JQAdm, the layout defined via CSS
399
		 * should support adding, removing or reordering content by a fluid like
400
		 * design.
401
		 *
402
		 * @param array List of sub-client names
403
		 * @since 2016.01
404
		 * @category Developer
405
		 */
406
		return $this->getContext()->getConfig()->get( 'admin/jqadm/order/standard/subparts', [] );
407
	}
408
409
410
	/**
411
	 * Creates new and updates existing items using the data array
412
	 *
413
	 * @param array $data Data array
414
	 * @return \Aimeos\MShop\Order\Item\Base\Iface New order item object
415
	 */
416
	protected function fromArray( array $data ) : \Aimeos\MShop\Order\Item\Base\Iface
417
	{
418
		$manager = \Aimeos\MShop::create( $this->getContext(), 'order/base' );
419
		$attrManager = \Aimeos\MShop::create( $this->getContext(), 'order/base/service/attribute' );
420
		$domains = ['order/base/address', 'order/base/product', 'order/base/service'];
421
422
		if( isset( $data['order.base.id'] ) ) {
423
			$basket = $manager->getItem( $data['order.base.id'], $domains )->off();
424
		} else {
425
			$basket = $manager->createItem()->off();
426
		}
427
428
		$basket->fromArray( $data, true );
429
430
		foreach( $basket->getProducts() as $pos => $product )
431
		{
432
			if( isset( $data['product'][$pos]['order.base.product.status'] ) ) {
433
				$product->setStatus( $data['product'][$pos]['order.base.product.status'] );
434
			}
435
		}
436
437
		foreach( $basket->getAddresses() as $type => $addresses )
438
		{
439
			foreach( $addresses as $pos => $address )
440
			{
441
				if( isset( $data['address'][$type][$pos] ) ) {
442
					$list = (array) $data['address'][$type][$pos];
443
					$basket->addAddress( $address->fromArray( $list, true ), $type, $pos );
444
				} else {
445
					$basket->deleteAddress( $type, $pos );
446
				}
447
			}
448
		}
449
450
		foreach( $basket->getServices() as $type => $services )
451
		{
452
			foreach( $services as $serviceId => $service )
453
			{
454
				$list = [];
455
				$attrItems = $service->getAttributeItems();
456
457
				if( isset( $data['service'][$type][$serviceId] ) )
458
				{
459
					foreach( (array) $data['service'][$type][$serviceId] as $key => $pair )
460
					{
461
						foreach( $pair as $pos => $value ) {
462
							$list[$pos][$key] = $value;
463
						}
464
					}
465
466
					foreach( $list as $array )
467
					{
468
						if( isset( $attrItems[$array['order.base.service.attribute.id']] ) )
469
						{
470
							$attrItem = $attrItems[$array['order.base.service.attribute.id']];
471
							unset( $attrItems[$array['order.base.service.attribute.id']] );
472
						}
473
						else
474
						{
475
							$attrItem = $attrManager->createItem();
476
						}
477
478
						$attrItem->fromArray( $array, true );
479
						$attrItem->setParentId( $service->getId() );
480
481
						$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

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