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

Standard   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 542
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
eloc 156
dl 0
loc 542
rs 8.48
c 4
b 0
f 0
wmc 49

13 Methods

Rating   Name   Duplication   Size   Complexity  
A toArray() 0 12 2
A render() 0 25 1
A create() 0 32 4
A getDomains() 0 14 1
A copy() 0 31 4
A fromArray() 0 19 6
A delete() 0 40 5
A getSubClientNames() 0 36 1
A getSubClient() 0 76 1
C getGroupItems() 0 33 13
A get() 0 31 4
A save() 0 31 4
A search() 0 50 3

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\Customer;
12
13
sprintf( 'customer' ); // for translation
14
15
16
/**
17
 * Default implementation of customer 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, 'customer' );
43
			$view->item = $manager->getItem( $id, $this->getDomains() );
44
45
			$view->itemData = $this->toArray( $view->item, true );
46
			$view->itemSubparts = $this->getSubClientNames();
47
			$view->itemGroups = $this->getGroupItems();
48
			$view->itemBody = '';
49
50
			foreach( $this->getSubClients() as $idx => $client )
51
			{
52
				$view->tabindex = ++$idx + 1;
53
				$view->itemBody .= $client->copy();
54
			}
55
		}
56
		catch( \Exception $e )
57
		{
58
			$this->report( $e, 'copy' );
59
		}
60
61
		return $this->render( $view );
62
	}
63
64
65
	/**
66
	 * Creates a new resource
67
	 *
68
	 * @return string|null HTML output
69
	 */
70
	public function create() : ?string
71
	{
72
		$view = $this->getView();
73
		$context = $this->getContext();
74
75
		try
76
		{
77
			$data = $view->param( 'item', [] );
78
79
			if( !isset( $view->item ) ) {
80
				$view->item = \Aimeos\MShop::create( $context, 'customer' )->createItem();
81
			}
82
83
			$data['customer.siteid'] = $view->item->getSiteId();
84
85
			$view->itemSubparts = $this->getSubClientNames();
86
			$view->itemGroups = $this->getGroupItems();
87
			$view->itemData = $data;
88
			$view->itemBody = '';
89
90
			foreach( $this->getSubClients() as $idx => $client )
91
			{
92
				$view->tabindex = ++$idx + 1;
93
				$view->itemBody .= $client->create();
94
			}
95
		}
96
		catch( \Exception $e )
97
		{
98
			$this->report( $e, 'create' );
99
		}
100
101
		return $this->render( $view );
102
	}
103
104
105
	/**
106
	 * Deletes a resource
107
	 *
108
	 * @return string|null HTML output
109
	 */
110
	public function delete() : ?string
111
	{
112
		$view = $this->getView();
113
		$context = $this->getContext();
114
115
		$manager = \Aimeos\MShop::create( $context, 'customer' );
116
		$manager->begin();
117
118
		try
119
		{
120
			if( ( $ids = $view->param( 'id' ) ) === null ) {
121
				throw new \Aimeos\Admin\JQAdm\Exception( sprintf( 'Required parameter "%1$s" is missing', 'id' ) );
122
			}
123
124
			$search = $manager->createSearch()->setSlice( 0, count( (array) $ids ) );
125
			$search->setConditions( $search->compare( '==', 'customer.id', $ids ) );
126
			$items = $manager->searchItems( $search, $this->getDomains() );
127
128
			foreach( $items as $item )
129
			{
130
				$view->item = $item;
131
132
				foreach( $this->getSubClients() as $client ) {
133
					$client->delete();
134
				}
135
			}
136
137
			$manager->deleteItems( $items->toArray() );
138
			$manager->commit();
139
140
			$this->nextAction( $view, 'search', 'customer', null, 'delete' );
141
			return null;
142
		}
143
		catch( \Exception $e )
144
		{
145
			$manager->rollback();
146
			$this->report( $e, 'delete' );
147
		}
148
149
		return $this->search();
150
	}
151
152
153
	/**
154
	 * Returns a single resource
155
	 *
156
	 * @return string|null HTML output
157
	 */
158
	public function get() : ?string
159
	{
160
		$view = $this->getView();
161
		$context = $this->getContext();
162
163
		try
164
		{
165
			if( ( $id = $view->param( 'id' ) ) === null ) {
166
				throw new \Aimeos\Admin\JQAdm\Exception( sprintf( 'Required parameter "%1$s" is missing', 'id' ) );
167
			}
168
169
			$manager = \Aimeos\MShop::create( $context, 'customer' );
170
171
			$view->item = $manager->getItem( $id, $this->getDomains() );
172
			$view->itemGroups = $this->getGroupItems( $view->item );
173
			$view->itemSubparts = $this->getSubClientNames();
174
			$view->itemData = $this->toArray( $view->item );
175
			$view->itemBody = '';
176
177
			foreach( $this->getSubClients() as $idx => $client )
178
			{
179
				$view->tabindex = ++$idx + 1;
180
				$view->itemBody .= $client->get();
181
			}
182
		}
183
		catch( \Exception $e )
184
		{
185
			$this->report( $e, 'get' );
186
		}
187
188
		return $this->render( $view );
189
	}
190
191
192
	/**
193
	 * Saves the data
194
	 *
195
	 * @return string|null HTML output
196
	 */
197
	public function save() : ?string
198
	{
199
		$view = $this->getView();
200
		$context = $this->getContext();
201
202
		$manager = \Aimeos\MShop::create( $context, 'customer' );
203
		$manager->begin();
204
205
		try
206
		{
207
			$item = $this->fromArray( $view->param( 'item', [] ) );
208
			$view->item = $item->getId() ? $item : $manager->saveItem( $item );
0 ignored issues
show
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

208
			$view->item = $item->getId() ? $item : $manager->/** @scrutinizer ignore-call */ saveItem( $item );

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...
209
			$view->itemBody = '';
210
211
			foreach( $this->getSubClients() as $client ) {
212
				$view->itemBody .= $client->save();
213
			}
214
215
			$manager->saveItem( clone $view->item );
216
			$manager->commit();
217
218
			$this->nextAction( $view, $view->param( 'next' ), 'customer', $view->item->getId(), 'save' );
219
			return null;
220
		}
221
		catch( \Exception $e )
222
		{
223
			$manager->rollback();
224
			$this->report( $e, 'save' );
225
		}
226
227
		return $this->create();
228
	}
229
230
231
	/**
232
	 * Returns a list of resource according to the conditions
233
	 *
234
	 * @return string|null HTML output
235
	 */
236
	public function search() : ?string
237
	{
238
		$view = $this->getView();
239
		$context = $this->getContext();
240
241
		try
242
		{
243
			$total = 0;
244
			$params = $this->storeSearchParams( $view->param(), 'customer' );
245
			$manager = \Aimeos\MShop::create( $context, 'customer' );
246
			$search = $this->initCriteria( $manager->createSearch(), $params );
247
248
			$view->items = $manager->searchItems( $search, $this->getDomains(), $total );
249
			$view->filterAttributes = $manager->getSearchAttributes( true );
250
			$view->filterOperators = $search->getOperators();
251
			$view->total = $total;
252
			$view->itemBody = '';
253
254
			foreach( $this->getSubClients() as $client ) {
255
				$view->itemBody .= $client->search();
256
			}
257
		}
258
		catch( \Exception $e )
259
		{
260
			$this->report( $e, 'search' );
261
		}
262
263
		/** admin/jqadm/customer/template-list
264
		 * Relative path to the HTML body template for the customer list.
265
		 *
266
		 * The template file contains the HTML code and processing instructions
267
		 * to generate the result shown in the body of the frontend. The
268
		 * configuration string is the path to the template file relative
269
		 * to the templates directory (usually in admin/jqadm/templates).
270
		 *
271
		 * You can overwrite the template file configuration in extensions and
272
		 * provide alternative templates. These alternative templates should be
273
		 * named like the default one but with the string "default" replaced by
274
		 * an unique name. You may use the name of your project for this. If
275
		 * you've implemented an alternative client class as well, "default"
276
		 * should be replaced by the name of the new class.
277
		 *
278
		 * @param string Relative path to the template creating the HTML code
279
		 * @since 2016.04
280
		 * @category Developer
281
		 */
282
		$tplconf = 'admin/jqadm/customer/template-list';
283
		$default = 'customer/list-standard';
284
285
		return $view->render( $view->config( $tplconf, $default ) );
286
	}
287
288
289
	/**
290
	 * Returns the sub-client given by its name.
291
	 *
292
	 * @param string $type Name of the client type
293
	 * @param string|null $name Name of the sub-client (Default if null)
294
	 * @return \Aimeos\Admin\JQAdm\Iface Sub-client object
295
	 */
296
	public function getSubClient( string $type, string $name = null ) : \Aimeos\Admin\JQAdm\Iface
297
	{
298
		/** admin/jqadm/customer/decorators/excludes
299
		 * Excludes decorators added by the "common" option from the customer JQAdm client
300
		 *
301
		 * Decorators extend the functionality of a class by adding new aspects
302
		 * (e.g. log what is currently done), executing the methods of the underlying
303
		 * class only in certain conditions (e.g. only for logged in users) or
304
		 * modify what is returned to the caller.
305
		 *
306
		 * This option allows you to remove a decorator added via
307
		 * "client/jqadm/common/decorators/default" before they are wrapped
308
		 * around the JQAdm client.
309
		 *
310
		 *  admin/jqadm/customer/decorators/excludes = array( 'decorator1' )
311
		 *
312
		 * This would remove the decorator named "decorator1" from the list of
313
		 * common decorators ("\Aimeos\Admin\JQAdm\Common\Decorator\*") added via
314
		 * "client/jqadm/common/decorators/default" to the JQAdm client.
315
		 *
316
		 * @param array List of decorator names
317
		 * @since 2017.07
318
		 * @category Developer
319
		 * @see admin/jqadm/common/decorators/default
320
		 * @see admin/jqadm/customer/decorators/global
321
		 * @see admin/jqadm/customer/decorators/local
322
		 */
323
324
		/** admin/jqadm/customer/decorators/global
325
		 * Adds a list of globally available decorators only to the customer JQAdm client
326
		 *
327
		 * Decorators extend the functionality of a class by adding new aspects
328
		 * (e.g. log what is currently done), executing the methods of the underlying
329
		 * class only in certain conditions (e.g. only for logged in users) or
330
		 * modify what is returned to the caller.
331
		 *
332
		 * This option allows you to wrap global decorators
333
		 * ("\Aimeos\Admin\JQAdm\Common\Decorator\*") around the JQAdm client.
334
		 *
335
		 *  admin/jqadm/customer/decorators/global = array( 'decorator1' )
336
		 *
337
		 * This would add the decorator named "decorator1" defined by
338
		 * "\Aimeos\Admin\JQAdm\Common\Decorator\Decorator1" only to the JQAdm client.
339
		 *
340
		 * @param array List of decorator names
341
		 * @since 2017.07
342
		 * @category Developer
343
		 * @see admin/jqadm/common/decorators/default
344
		 * @see admin/jqadm/customer/decorators/excludes
345
		 * @see admin/jqadm/customer/decorators/local
346
		 */
347
348
		/** admin/jqadm/customer/decorators/local
349
		 * Adds a list of local decorators only to the customer JQAdm client
350
		 *
351
		 * Decorators extend the functionality of a class by adding new aspects
352
		 * (e.g. log what is currently done), executing the methods of the underlying
353
		 * class only in certain conditions (e.g. only for logged in users) or
354
		 * modify what is returned to the caller.
355
		 *
356
		 * This option allows you to wrap local decorators
357
		 * ("\Aimeos\Admin\JQAdm\Customer\Decorator\*") around the JQAdm client.
358
		 *
359
		 *  admin/jqadm/customer/decorators/local = array( 'decorator2' )
360
		 *
361
		 * This would add the decorator named "decorator2" defined by
362
		 * "\Aimeos\Admin\JQAdm\Customer\Decorator\Decorator2" only to the JQAdm client.
363
		 *
364
		 * @param array List of decorator names
365
		 * @since 2017.07
366
		 * @category Developer
367
		 * @see admin/jqadm/common/decorators/default
368
		 * @see admin/jqadm/customer/decorators/excludes
369
		 * @see admin/jqadm/customer/decorators/global
370
		 */
371
		return $this->createSubClient( 'customer/' . $type, $name );
372
	}
373
374
375
	/**
376
	 * Returns the domain names whose items should be fetched too
377
	 *
378
	 * @return string[] List of domain names
379
	 */
380
	protected function getDomains() : array
381
	{
382
		/** admin/jqadm/customer/domains
383
		 * List of domain items that should be fetched along with the customer
384
		 *
385
		 * If you need to display additional content, you can configure your own
386
		 * list of domains (attribute, media, price, customer, text, etc. are
387
		 * domains) whose items are fetched from the storage.
388
		 *
389
		 * @param array List of domain names
390
		 * @since 2017.07
391
		 * @category Developer
392
		 */
393
		return $this->getContext()->getConfig()->get( 'admin/jqadm/customer/domains', [] );
394
	}
395
396
397
	/**
398
	 * Returns the available group items
399
	 *
400
	 * @param \Aimeos\MShop\Customer\Item\Iface|null $item Customer item that should be updated
401
	 * @return \Aimeos\MShop\Customer\Item\Group\Iface[] Associative list of group IDs as keys and group items as values
402
	 */
403
	protected function getGroupItems( \Aimeos\MShop\Customer\Item\Iface $item = null ) : array
404
	{
405
		$list = [];
406
		$context = $this->getContext();
407
408
		$isSuper = $this->getView()->access( ['super'] );
409
		$isAdmin = $this->getView()->access( ['admin'] );
410
		$isEditor = $this->getView()->access( ['editor'] );
411
412
		$manager = \Aimeos\MShop::create( $context, 'customer/group' );
413
		$search = $manager->createSearch();
414
		$search->setSortations( [$search->sort( '+', 'customer.group.label' )] );
415
416
		foreach( $manager->searchItems( $search ) as $groupId => $groupItem )
417
		{
418
			if( !$isSuper && $groupItem->getCode() === 'super' ) {
419
				continue;
420
			}
421
422
			if( !$isSuper && !$isAdmin && $groupItem->getCode() === 'admin' ) {
423
				continue;
424
			}
425
426
			if( !$isSuper && !$isAdmin && $groupItem->getCode() === 'editor'
427
				&& ( !$isEditor || $item === null || (string) $context->getUserId() !== (string) $item->getId() )
428
			) {
429
				continue;
430
			}
431
432
			$list[$groupId] = $groupItem;
433
		}
434
435
		return $list;
436
	}
437
438
439
	/**
440
	 * Returns the list of sub-client names configured for the client.
441
	 *
442
	 * @return array List of JQAdm client names
443
	 */
444
	protected function getSubClientNames() : array
445
	{
446
		/** admin/jqadm/customer/standard/subparts
447
		 * List of JQAdm sub-clients rendered within the customer section
448
		 *
449
		 * The output of the frontend is composed of the code generated by the JQAdm
450
		 * clients. Each JQAdm client can consist of serveral (or none) sub-clients
451
		 * that are responsible for rendering certain sub-parts of the output. The
452
		 * sub-clients can contain JQAdm clients themselves and therefore a
453
		 * hierarchical tree of JQAdm clients is composed. Each JQAdm client creates
454
		 * the output that is placed inside the container of its parent.
455
		 *
456
		 * At first, always the JQAdm code generated by the parent is printed, then
457
		 * the JQAdm code of its sub-clients. The order of the JQAdm sub-clients
458
		 * determines the order of the output of these sub-clients inside the parent
459
		 * container. If the configured list of clients is
460
		 *
461
		 *  array( "subclient1", "subclient2" )
462
		 *
463
		 * you can easily change the order of the output by reordering the subparts:
464
		 *
465
		 *  admin/jqadm/<clients>/subparts = array( "subclient1", "subclient2" )
466
		 *
467
		 * You can also remove one or more parts if they shouldn't be rendered:
468
		 *
469
		 *  admin/jqadm/<clients>/subparts = array( "subclient1" )
470
		 *
471
		 * As the clients only generates structural JQAdm, the layout defined via CSS
472
		 * should support adding, removing or reordering content by a fluid like
473
		 * design.
474
		 *
475
		 * @param array List of sub-client names
476
		 * @since 2017.07
477
		 * @category Developer
478
		 */
479
		return $this->getContext()->getConfig()->get( 'admin/jqadm/customer/standard/subparts', [] );
480
	}
481
482
483
484
	/**
485
	 * Creates new and updates existing items using the data array
486
	 *
487
	 * @param array $data Data array
488
	 * @return \Aimeos\MShop\Customer\Item\Iface New customer item object
489
	 */
490
	protected function fromArray( array $data ) : \Aimeos\MShop\Customer\Item\Iface
491
	{
492
		$manager = \Aimeos\MShop::create( $this->getContext(), 'customer' );
493
494
		if( isset( $data['customer.id'] ) && $data['customer.id'] != '' ) {
495
			$item = $manager->getItem( $data['customer.id'], $this->getDomains() );
496
		} else {
497
			$item = $manager->createItem();
498
		}
499
500
		$addr = $item->getPaymentAddress();
501
		$label = ( $addr->getFirstname() ? $addr->getFirstname() . ' ' : '' ) . $addr->getLastname();
502
		$label .= ( $addr->getCompany() ? ' (' . $addr->getCompany() . ')' : '' );
503
		$groupIds = $this->getValue( $data, 'customer.groups', [] );
504
505
		return $item->fromArray( $data, true )
506
			->setGroups( array_intersect( array_keys( $this->getGroupItems( $item ) ), $groupIds ) )
507
			->setCode( $item->getCode() ?: $addr->getEmail() )
508
			->setLabel( $label );
509
	}
510
511
512
	/**
513
	 * Constructs the data array for the view from the given item
514
	 *
515
	 * @param \Aimeos\MShop\Customer\Item\Iface $item Customer item object
516
	 * @return string[] Multi-dimensional associative list of item data
517
	 */
518
	protected function toArray( \Aimeos\MShop\Customer\Item\Iface $item, bool $copy = false ) : array
519
	{
520
		$data = $item->toArray( true );
521
522
		if( $copy === true )
523
		{
524
			$data['customer.siteid'] = $this->getContext()->getLocale()->getSiteId();
525
			$data['customer.email'] = '';
526
			$data['customer.id'] = '';
527
		}
528
529
		return $data;
530
	}
531
532
533
	/**
534
	 * Returns the rendered template including the view data
535
	 *
536
	 * @param \Aimeos\MW\View\Iface $view View object with data assigned
537
	 * @return string|null HTML output
538
	 */
539
	protected function render( \Aimeos\MW\View\Iface $view ) : string
540
	{
541
		/** admin/jqadm/customer/template-item
542
		 * Relative path to the HTML body template for the customer item.
543
		 *
544
		 * The template file contains the HTML code and processing instructions
545
		 * to generate the result shown in the body of the frontend. The
546
		 * configuration string is the path to the template file relative
547
		 * to the templates directory (usually in admin/jqadm/templates).
548
		 *
549
		 * You can overwrite the template file configuration in extensions and
550
		 * provide alternative templates. These alternative templates should be
551
		 * named like the default one but with the string "default" replaced by
552
		 * an unique name. You may use the name of your project for this. If
553
		 * you've implemented an alternative client class as well, "default"
554
		 * should be replaced by the name of the new class.
555
		 *
556
		 * @param string Relative path to the template creating the HTML code
557
		 * @since 2016.04
558
		 * @category Developer
559
		 */
560
		$tplconf = 'admin/jqadm/customer/template-item';
561
		$default = 'customer/item-standard';
562
563
		return $view->render( $view->config( $tplconf, $default ) );
564
	}
565
}
566