Passed
Push — master ( b737b5...ccef29 )
by Aimeos
07:47 queued 02:47
created

Autofill::setServicesDefault()   C

Complexity

Conditions 13
Paths 36

Size

Total Lines 36
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 17
c 1
b 0
f 0
nc 36
nop 1
dl 0
loc 36
rs 6.6166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0
5
 * @copyright Metaways Infosystems GmbH, 2014
6
 * @copyright Aimeos (aimeos.org), 2015-2022
7
 * @package MShop
8
 * @subpackage Plugin
9
 */
10
11
12
namespace Aimeos\MShop\Plugin\Provider\Order;
13
14
15
/**
16
 * Adds address and service items to the basket
17
 *
18
 * This plugins acts if a product is added to the basket or a delivery/payment
19
 * service is removed from the basket. It adds the a delivery/payment service
20
 * item and the customer address(es) to the basket.
21
 *
22
 * The following options are available:
23
 * - address: 1 (add billing address of the logged in customer to the basket)
24
 * - delivery: 1 (add the first delivery option to the basket)
25
 * - deliverycode: '...' and delivery: 1 (add specific delivery option to the basket)
26
 * - payment: 1 (add the first payment option to the basket)
27
 * - paymentcode: '...' and payment: 1 (add specific payment option to the basket)
28
 * - useorder: 1 (use last order of the customer to pre-fill addresses or services)
29
 * - orderservice: 1 (add delivery and payment services from the last order of the customer)
30
 * - orderaddress: 1 (add billing and delivery addresses from the last order of the customer)
31
 *
32
 * This plugin interacts with other plugins that add products or remove services!
33
 * Especially the "ServiceUpdate" plugin may remove a delivery/payment option
34
 * that isn't available any more based on the current basket content.
35
 *
36
 * To trace the execution and interaction of the plugins, set the log level to DEBUG:
37
 *	madmin/log/manager/loglevel = 7
38
 *
39
 * @package MShop
40
 * @subpackage Plugin
41
 */
42
class Autofill
43
	extends \Aimeos\MShop\Plugin\Provider\Factory\Base
44
	implements \Aimeos\MShop\Plugin\Provider\Iface, \Aimeos\MShop\Plugin\Provider\Factory\Iface
45
{
46
	private $beConfig = array(
47
		'address' => array(
48
			'code' => 'address',
49
			'internalcode' => 'address',
50
			'label' => 'Add customer address automatically',
51
			'type' => 'boolean',
52
			'internaltype' => 'boolean',
53
			'default' => '',
54
			'required' => false,
55
		),
56
		'delivery' => array(
57
			'code' => 'delivery',
58
			'internalcode' => 'delivery',
59
			'label' => 'Add delivery option automatically',
60
			'type' => 'boolean',
61
			'internaltype' => 'boolean',
62
			'default' => '',
63
			'required' => false,
64
		),
65
		'deliverycode' => array(
66
			'code' => 'deliverycode',
67
			'internalcode' => 'deliverycode',
68
			'label' => 'Add delivery by code',
69
			'type' => 'string',
70
			'internaltype' => 'string',
71
			'default' => '',
72
			'required' => false,
73
		),
74
		'payment' => array(
75
			'code' => 'payment',
76
			'internalcode' => 'payment',
77
			'label' => 'Add payment option automatically',
78
			'type' => 'boolean',
79
			'internaltype' => 'boolean',
80
			'default' => '',
81
			'required' => false,
82
		),
83
		'paymentcode' => array(
84
			'code' => 'paymentcode',
85
			'internalcode' => 'paymentcode',
86
			'label' => 'Add payment by code',
87
			'type' => 'string',
88
			'internaltype' => 'string',
89
			'default' => '',
90
			'required' => false,
91
		),
92
		'useorder' => array(
93
			'code' => 'useorder',
94
			'internalcode' => 'useorder',
95
			'label' => 'Add from last order',
96
			'type' => 'boolean',
97
			'internaltype' => 'boolean',
98
			'default' => '',
99
			'required' => false,
100
		),
101
		'orderaddress' => array(
102
			'code' => 'orderaddress',
103
			'internalcode' => 'orderaddress',
104
			'label' => 'Add address from last order',
105
			'type' => 'boolean',
106
			'internaltype' => 'boolean',
107
			'default' => '',
108
			'required' => false,
109
		),
110
		'orderservice' => array(
111
			'code' => 'orderservice',
112
			'internalcode' => 'orderservice',
113
			'label' => 'Add delivery/payment from last order',
114
			'type' => 'boolean',
115
			'internaltype' => 'boolean',
116
			'default' => '',
117
			'required' => false,
118
		),
119
	);
120
121
122
	/**
123
	 * Checks the backend configuration attributes for validity.
124
	 *
125
	 * @param array $attributes Attributes added by the shop owner in the administraton interface
126
	 * @return array An array with the attribute keys as key and an error message as values for all attributes that are
127
	 * 	known by the provider but aren't valid
128
	 */
129
	public function checkConfigBE( array $attributes ) : array
130
	{
131
		$errors = parent::checkConfigBE( $attributes );
132
133
		return array_merge( $errors, $this->checkConfig( $this->beConfig, $attributes ) );
134
	}
135
136
137
	/**
138
	 * Returns the configuration attribute definitions of the provider to generate a list of available fields and
139
	 * rules for the value of each field in the administration interface.
140
	 *
141
	 * @return array List of attribute definitions implementing \Aimeos\MW\Common\Critera\Attribute\Iface
142
	 */
143
	public function getConfigBE() : array
144
	{
145
		return $this->getConfigItems( $this->beConfig );
146
	}
147
148
149
	/**
150
	 * Subscribes itself to a publisher
151
	 *
152
	 * @param \Aimeos\MW\Observer\Publisher\Iface $p Object implementing publisher interface
153
	 * @return \Aimeos\MShop\Plugin\Provider\Iface Plugin object for method chaining
154
	 */
155
	public function register( \Aimeos\MW\Observer\Publisher\Iface $p ) : \Aimeos\MW\Observer\Listener\Iface
156
	{
157
		$plugin = $this->object();
158
159
		$p->attach( $plugin, 'addProduct.after' );
160
		$p->attach( $plugin, 'deleteService.after' );
161
162
		return $this;
163
	}
164
165
166
	/**
167
	 * Receives a notification from a publisher object
168
	 *
169
	 * @param \Aimeos\MW\Observer\Publisher\Iface $order Shop basket instance implementing publisher interface
170
	 * @param string $action Name of the action to listen for
171
	 * @param mixed $value Object or value changed in publisher
172
	 * @return mixed Modified value parameter
173
	 * @throws \Aimeos\MShop\Plugin\Provider\Exception if an error occurs
174
	 */
175
	public function update( \Aimeos\MW\Observer\Publisher\Iface $order, string $action, $value = null )
176
	{
177
		\Aimeos\MW\Common\Base::checkClass( \Aimeos\MShop\Order\Item\Base\Iface::class, $order );
178
179
		$context = $this->context();
180
		$services = $order->getServices();
181
		$addresses = $order->getAddresses();
182
183
		if( ( $userid = $context->user() ) !== null
184
			&& (bool) $this->getConfigValue( 'useorder', false ) === true
185
			&& ( $addresses->isEmpty() || $services->isEmpty() )
186
		) {
187
			$orderManager = \Aimeos\MShop::create( $context, 'order' );
188
189
			$search = $orderManager->filter()->add( ['order.base.customerid' => $userid] )
190
				->order( '-order.ctime' )->slice( 0, 1 );
191
192
			if( ( $item = $orderManager->search( $search )->first() ) !== null )
193
			{
194
				$this->setAddresses( $order, $item );
195
				$this->setServices( $order, $item );
196
			}
197
		}
198
199
		$this->setAddressDefault( $order );
200
		$this->setServicesDefault( $order );
201
202
		return $value;
203
	}
204
205
206
	/**
207
	 * Returns the order service item for the given type and code if available.
208
	 *
209
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $order Basket of the customer
210
	 * @param string $type Service type constant from \Aimeos\MShop\Order\Item\Base\Service\Base
211
	 * @param string|null $code Service item code
212
	 * @return \Aimeos\MShop\Order\Item\Base\Service\Iface|null Order service item if available or null otherwise
213
	 */
214
	protected function getServiceItem( \Aimeos\MShop\Order\Item\Base\Iface $order, string $type,
215
		string $code = null ) : ?\Aimeos\MShop\Order\Item\Base\Service\Iface
216
	{
217
		$context = $this->context();
218
		$serviceManager = \Aimeos\MShop::create( $context, 'service' );
219
220
		$filter = $serviceManager->filter( true )->add( ['service.type' => $type] )->order( 'service.position' );
221
222
		if( $code !== null ) {
223
			$filter->add( 'service.code', '==', $code );
224
		}
225
226
		foreach( $serviceManager->search( $filter, ['media', 'price', 'text'] ) as $item )
227
		{
228
			$provider = $serviceManager->getProvider( $item, $item->getType() );
229
230
			if( $provider->isAvailable( $order ) === true )
231
			{
232
				return \Aimeos\MShop::create( $context, 'order/base/service' )->create()
233
					->copyFrom( $item )->setPrice( $provider->calcPrice( $order ) );
234
			}
235
		}
236
237
		return null;
238
	}
239
240
241
	/**
242
	 * Adds the addresses from the given order item to the basket.
243
	 *
244
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $order Basket object
245
	 * @param \Aimeos\MShop\Order\Item\Iface $item Existing order to fetch the addresses from
246
	 * @return \Aimeos\MShop\Order\Item\Base\Iface Updated basket object
247
	 */
248
	protected function setAddresses( \Aimeos\MShop\Order\Item\Base\Iface $order,
249
		\Aimeos\MShop\Order\Item\Iface $item ) : \Aimeos\MShop\Order\Item\Base\Iface
250
	{
251
		if( $order->getAddresses()->isEmpty() && (bool) $this->getConfigValue( 'orderaddress', true ) === true )
252
		{
253
			$manager = \Aimeos\MShop::create( $this->context(), 'order/base/address' );
254
			$search = $manager->filter();
255
			$search->setConditions( $search->compare( '==', 'order.base.address.baseid', $item->getBaseId() ) );
256
			$addresses = [];
257
258
			foreach( $manager->search( $search ) as $address ) {
259
				$addresses[$address->getType()][] = $address->setId( null );
260
			}
261
262
			$order->setAddresses( $addresses );
263
		}
264
265
		return $order;
266
	}
267
268
269
	/**
270
	 * Adds the services from the given order item to the basket.
271
	 *
272
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $order Basket object
273
	 * @param \Aimeos\MShop\Order\Item\Iface $item Existing order to fetch the services from
274
	 * @return \Aimeos\MShop\Order\Item\Base\Iface Updated basket object
275
	 */
276
	protected function setServices( \Aimeos\MShop\Order\Item\Base\Iface $order,
277
		\Aimeos\MShop\Order\Item\Iface $item ) : \Aimeos\MShop\Order\Item\Base\Iface
278
	{
279
		if( $order->getServices()->isEmpty() && $this->getConfigValue( 'orderservice', true ) == true )
280
		{
281
			$manager = \Aimeos\MShop::create( $this->context(), 'order/base/service' );
282
			$search = $manager->filter();
283
			$search->setConditions( $search->compare( '==', 'order.base.service.baseid', $item->getBaseId() ) );
284
			$services = [];
285
286
			foreach( $manager->search( $search ) as $service )
287
			{
288
				$type = $service->getType();
289
290
				if( ( $item = $this->getServiceItem( $order, $type, $service->getCode() ) ) !== null ) {
291
					$services[$type][] = $item->setAttributeItems( [] )->setId( null );
292
				}
293
			}
294
295
			$order->setServices( $services );
296
		}
297
298
		return $order;
299
	}
300
301
302
	/**
303
	 * Adds the default addresses to the basket if they are not available.
304
	 *
305
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $order Basket object
306
	 * @return \Aimeos\MShop\Order\Item\Base\Iface Updated basket object
307
	 */
308
	protected function setAddressDefault( \Aimeos\MShop\Order\Item\Base\Iface $order ) : \Aimeos\MShop\Order\Item\Base\Iface
309
	{
310
		$context = $this->context();
311
		$addresses = $order->getAddresses();
312
		$type = \Aimeos\MShop\Order\Item\Base\Address\Base::TYPE_PAYMENT;
313
314
		if( $context->user() !== null && !isset( $addresses[$type] )
315
			&& (bool) $this->getConfigValue( 'address', false ) === true
316
		) {
317
			$address = \Aimeos\MShop::create( $context, 'customer' )
318
				->get( $context->user() )->getPaymentAddress();
319
320
			$addrItem = \Aimeos\MShop::create( $context, 'order/base/address' )
321
				->create()->copyFrom( $address );
322
323
			$order->addAddress( $addrItem, $type );
324
		}
325
326
		return $order;
327
	}
328
329
330
	/**
331
	 * Adds the default services to the basket if they are not available.
332
	 *
333
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $order Basket object
334
	 * @return \Aimeos\MShop\Order\Item\Base\Iface Updated basket object
335
	 */
336
	protected function setServicesDefault( \Aimeos\MShop\Order\Item\Base\Iface $order ) : \Aimeos\MShop\Order\Item\Base\Iface
337
	{
338
		$type = \Aimeos\MShop\Order\Item\Base\Service\Base::TYPE_DELIVERY;
339
340
		foreach( $order->getService( $type ) as $pos => $service )
341
		{
342
			if( $this->getServiceItem( $order, $type, $service->getCode() ) === null ) {
343
				$order->deleteService( $pos );
344
			}
345
		}
346
347
		if( $order->getService( $type ) === [] && (bool) $this->getConfigValue( 'delivery', false ) === true
348
			&& ( ( $item = $this->getServiceItem( $order, $type, $this->getConfigValue( 'deliverycode' ) ) ) !== null
349
			|| ( $item = $this->getServiceItem( $order, $type ) ) !== null )
350
		) {
351
			$order->addService( $item, $type );
352
		}
353
354
355
		$type = \Aimeos\MShop\Order\Item\Base\Service\Base::TYPE_PAYMENT;
356
357
		foreach( $order->getService( $type ) as $pos => $service )
358
		{
359
			if( $this->getServiceItem( $order, $type, $service->getCode() ) === null ) {
360
				$order->deleteService( $pos );
361
			}
362
		}
363
364
		if( $order->getService( $type ) === [] && (bool) $this->getConfigValue( 'payment', false ) === true
365
			&& ( ( $item = $this->getServiceItem( $order, $type, $this->getConfigValue( 'paymentcode' ) ) ) !== null
366
			|| ( $item = $this->getServiceItem( $order, $type ) ) !== null )
367
		) {
368
			$order->addService( $item, $type );
369
		}
370
371
		return $order;
372
	}
373
}
374