Passed
Push — master ( 7a5ddf...5d4f58 )
by Aimeos
10:45 queued 06:39
created

Standard::createItemBase()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 8
dl 0
loc 6
rs 10
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * @license LGPLv3, https://opensource.org/licenses/LGPL-3.0
5
 * @copyright Metaways Infosystems GmbH, 2011
6
 * @copyright Aimeos (aimeos.org), 2015-2022
7
 * @package MShop
8
 * @subpackage Order
9
 */
10
11
12
namespace Aimeos\MShop\Order\Manager\Base;
13
14
15
/**
16
 * Default implementation for order base manager.
17
 *
18
 * @package MShop
19
 * @subpackage Order
20
 */
21
class Standard extends Base
22
	implements \Aimeos\MShop\Order\Manager\Base\Iface, \Aimeos\MShop\Common\Manager\Factory\Iface
23
{
24
	private $searchConfig = array(
25
		'order.base.id' => array(
26
			'code' => 'order.base.id',
27
			'internalcode' => 'mordba."id"',
28
			'internaldeps' => array( 'LEFT JOIN "mshop_order_base" AS mordba ON ( mord."baseid" = mordba."id" )' ),
29
			'label' => 'Order ID',
30
			'type' => 'integer',
31
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_INT,
32
			'public' => false,
33
		),
34
		'order.base.siteid' => array(
35
			'code' => 'order.base.siteid',
36
			'internalcode' => 'mordba."siteid"',
37
			'label' => 'Order site ID',
38
			'type' => 'string',
39
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
40
			'public' => false,
41
		),
42
		'order.base.sitecode' => array(
43
			'code' => 'order.base.sitecode',
44
			'internalcode' => 'mordba."sitecode"',
45
			'label' => 'Order site code',
46
			'type' => 'string',
47
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
48
			'public' => false,
49
		),
50
		'order.base.customerid' => array(
51
			'code' => 'order.base.customerid',
52
			'internalcode' => 'mordba."customerid"',
53
			'label' => 'Order customer ID',
54
			'type' => 'string',
55
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
56
		),
57
		'order.base.customerref' => array(
58
			'code' => 'order.base.customerref',
59
			'internalcode' => 'mordba."customerref"',
60
			'label' => 'Order customer reference',
61
			'type' => 'string',
62
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
63
		),
64
		'order.base.languageid' => array(
65
			'code' => 'order.base.languageid',
66
			'internalcode' => 'mordba."langid"',
67
			'label' => 'Order language code',
68
			'type' => 'string',
69
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
70
		),
71
		'order.base.currencyid' => array(
72
			'code' => 'order.base.currencyid',
73
			'internalcode' => 'mordba."currencyid"',
74
			'label' => 'Order currencyid code',
75
			'type' => 'string',
76
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
77
		),
78
		'order.base.price' => array(
79
			'code' => 'order.base.price',
80
			'internalcode' => 'mordba."price"',
81
			'label' => 'Order price amount',
82
			'type' => 'string',
83
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
84
		),
85
		'order.base.costs' => array(
86
			'code' => 'order.base.costs',
87
			'internalcode' => 'mordba."costs"',
88
			'label' => 'Order shipping amount',
89
			'type' => 'string',
90
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
91
		),
92
		'order.base.rebate' => array(
93
			'code' => 'order.base.rebate',
94
			'internalcode' => 'mordba."rebate"',
95
			'label' => 'Order rebate amount',
96
			'type' => 'string',
97
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
98
		),
99
		'order.base.taxvalue' => array(
100
			'code' => 'order.base.taxvalue',
101
			'internalcode' => 'mordba."tax"',
102
			'label' => 'Order tax amount',
103
			'type' => 'string',
104
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
105
		),
106
		'order.base.taxflag' => array(
107
			'code' => 'order.base.taxflag',
108
			'internalcode' => 'mordba."taxflag"',
109
			'label' => 'Order tax flag (0=net, 1=gross)',
110
			'type' => 'string',
111
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_INT,
112
		),
113
		'order.base.comment' => array(
114
			'code' => 'order.base.comment',
115
			'internalcode' => 'mordba."comment"',
116
			'label' => 'Order comment',
117
			'type' => 'string',
118
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
119
		),
120
		'order.base.ctime' => array(
121
			'code' => 'order.base.ctime',
122
			'internalcode' => 'mordba."ctime"',
123
			'label' => 'Order create date/time',
124
			'type' => 'datetime',
125
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
126
			'public' => false,
127
		),
128
		'order.base.mtime' => array(
129
			'code' => 'order.base.mtime',
130
			'internalcode' => 'mordba."mtime"',
131
			'label' => 'Order modify date/time',
132
			'type' => 'datetime',
133
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
134
			'public' => false,
135
		),
136
		'order.base.editor' => array(
137
			'code' => 'order.base.editor',
138
			'internalcode' => 'mordba."editor"',
139
			'label' => 'Order editor',
140
			'type' => 'string',
141
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
142
			'public' => false,
143
		),
144
	);
145
146
147
	/**
148
	 * Initializes the object.
149
	 *
150
	 * @param \Aimeos\MShop\ContextIface $context Context object
151
	 */
152
	public function __construct( \Aimeos\MShop\ContextIface $context )
153
	{
154
		parent::__construct( $context );
155
		$this->setResourceName( 'db-order' );
156
	}
157
158
159
	/**
160
	 * Counts the number items that are available for the values of the given key.
161
	 *
162
	 * @param \Aimeos\Base\Criteria\Iface $search Search criteria
163
	 * @param array|string $key Search key or list of keys to aggregate items for
164
	 * @param string|null $value Search key for aggregating the value column
165
	 * @param string|null $type Type of the aggregation, empty string for count or "sum" or "avg" (average)
166
	 * @return \Aimeos\Map List of the search keys as key and the number of counted items as value
167
	 */
168
	public function aggregate( \Aimeos\Base\Criteria\Iface $search, $key, string $value = null, string $type = null ) : \Aimeos\Map
169
	{
170
		/** mshop/order/manager/base/aggregate/mysql
171
		 * Counts the number of records grouped by the values in the key column and matched by the given criteria
172
		 *
173
		 * @see mshop/order/manager/base/aggregate/ansi
174
		 */
175
176
		/** mshop/order/manager/base/aggregate/ansi
177
		 * Counts the number of records grouped by the values in the key column and matched by the given criteria
178
		 *
179
		 * Groups all records by the values in the key column and counts their
180
		 * occurence. The matched records can be limited by the given criteria
181
		 * from the order database. The records must be from one of the sites
182
		 * that are configured via the context item. If the current site is part
183
		 * of a tree of sites, the statement can count all records from the
184
		 * current site and the complete sub-tree of sites.
185
		 *
186
		 * As the records can normally be limited by criteria from sub-managers,
187
		 * their tables must be joined in the SQL context. This is done by
188
		 * using the "internaldeps" property from the definition of the ID
189
		 * column of the sub-managers. These internal dependencies specify
190
		 * the JOIN between the tables and the used columns for joining. The
191
		 * ":joins" placeholder is then replaced by the JOIN strings from
192
		 * the sub-managers.
193
		 *
194
		 * To limit the records matched, conditions can be added to the given
195
		 * criteria object. It can contain comparisons like column names that
196
		 * must match specific values which can be combined by AND, OR or NOT
197
		 * operators. The resulting string of SQL conditions replaces the
198
		 * ":cond" placeholder before the statement is sent to the database
199
		 * server.
200
		 *
201
		 * This statement doesn't return any records. Instead, it returns pairs
202
		 * of the different values found in the key column together with the
203
		 * number of records that have been found for that key values.
204
		 *
205
		 * The SQL statement should conform to the ANSI standard to be
206
		 * compatible with most relational database systems. This also
207
		 * includes using double quotes for table and column names.
208
		 *
209
		 * @param string SQL statement for aggregating order items
210
		 * @since 2014.09
211
		 * @category Developer
212
		 * @see mshop/order/manager/base/insert/ansi
213
		 * @see mshop/order/manager/base/update/ansi
214
		 * @see mshop/order/manager/base/newid/ansi
215
		 * @see mshop/order/manager/base/delete/ansi
216
		 * @see mshop/order/manager/base/search/ansi
217
		 * @see mshop/order/manager/base/count/ansi
218
		 */
219
220
		/** mshop/order/manager/base/aggregateavg/mysql
221
		 * Computes the average of all values grouped by the key column and matched by the given criteria
222
		 *
223
		 * @param string SQL statement for aggregating the order base items and computing the average value
224
		 * @since 2017.10
225
		 * @category Developer
226
		 * @see mshop/order/manager/base/aggregateavg/ansi
227
		 * @see mshop/order/manager/base/aggregate/mysql
228
		 */
229
230
		/** mshop/order/manager/base/aggregateavg/ansi
231
		 * Computes the average of all values grouped by the key column and matched by the given criteria
232
		 *
233
		 * @param string SQL statement for aggregating the order base items and computing the average value
234
		 * @since 2017.10
235
		 * @category Developer
236
		 * @see mshop/order/manager/base/aggregate/ansi
237
		 */
238
239
		/** mshop/order/manager/base/aggregatesum/mysql
240
		 * Computes the sum of all values grouped by the key column and matched by the given criteria
241
		 *
242
		 * @param string SQL statement for aggregating the order base items and computing the sum
243
		 * @since 2017.10
244
		 * @category Developer
245
		 * @see mshop/order/manager/base/aggregatesum/ansi
246
		 * @see mshop/order/manager/base/aggregate/mysql
247
		 */
248
249
		/** mshop/order/manager/base/aggregatesum/ansi
250
		 * Computes the sum of all values grouped by the key column and matched by the given criteria
251
		 *
252
		 * @param string SQL statement for aggregating the order base items and computing the sum
253
		 * @since 2017.10
254
		 * @category Developer
255
		 * @see mshop/order/manager/base/aggregate/ansi
256
		 */
257
258
		$cfgkey = 'mshop/order/manager/base/aggregate';
259
		return $this->aggregateBase( $search, $key, $cfgkey, ['order.base'], $value, $type );
260
	}
261
262
263
	/**
264
	 * Removes old entries from the storage.
265
	 *
266
	 * @param iterable $siteids List of IDs for sites whose entries should be deleted
267
	 * @return \Aimeos\MShop\Order\Manager\Base\Iface Manager object for chaining method calls
268
	 */
269
	public function clear( iterable $siteids ) : \Aimeos\MShop\Common\Manager\Iface
270
	{
271
		$path = 'mshop/order/manager/base/submanagers';
272
		$default = array( 'address', 'coupon', 'product', 'service' );
273
274
		foreach( $this->context()->config()->get( $path, $default ) as $domain ) {
275
			$this->object()->getSubManager( $domain )->clear( $siteids );
276
		}
277
278
		return $this->clearBase( $siteids, 'mshop/order/manager/base/delete' );
279
	}
280
281
282
	/**
283
	 * Creates a new empty item instance
284
	 *
285
	 * @param array $values Values the item should be initialized with
286
	 * @return \Aimeos\MShop\Order\Item\Base\Iface New order base item object
287
	 */
288
	public function create( array $values = [] ) : \Aimeos\MShop\Common\Item\Iface
289
	{
290
		$context = $this->context();
291
		$locale = $context->locale();
292
293
		$values['order.base.siteid'] = $values['order.base.siteid'] ?? $locale->getSiteId();
294
		$priceManager = \Aimeos\MShop::create( $context, 'price' );
295
296
		$base = $this->createItemBase( $priceManager->create(), clone $locale, $values );
297
298
		\Aimeos\MShop::create( $context, 'plugin' )->register( $base, 'order' );
299
300
		return $base;
301
	}
302
303
304
	/**
305
	 * Creates a search critera object
306
	 *
307
	 * @param bool|null $default Add default criteria or NULL for relaxed default criteria
308
	 * @param bool $site TRUE to add site criteria to show orders with available products only
309
	 * @return \Aimeos\Base\Criteria\Iface New search criteria object
310
	 */
311
	public function filter( ?bool $default = false, bool $site = false ) : \Aimeos\Base\Criteria\Iface
312
	{
313
		$search = parent::filter( $default );
314
		$context = $this->context();
315
316
		if( $default !== false ) {
317
			$search->add( ['order.base.customerid' => $context->user()] );
318
		}
319
320
		if( $site === true )
321
		{
322
			$level = \Aimeos\MShop\Locale\Manager\Base::SITE_SUBTREE;
323
			$search->add( $this->siteCondition( 'order.base.product.siteid', $level ) );
324
		}
325
326
		return $search;
327
	}
328
329
330
	/**
331
	 * Removes multiple items.
332
	 *
333
	 * @param \Aimeos\MShop\Common\Item\Iface[]|string[] $itemIds List of item objects or IDs of the items
334
	 * @return \Aimeos\MShop\Order\Manager\Base\Iface Manager object for chaining method calls
335
	 */
336
	public function delete( $itemIds ) : \Aimeos\MShop\Common\Manager\Iface
337
	{
338
		/** mshop/order/manager/base/delete/mysql
339
		 * Deletes the items matched by the given IDs from the database
340
		 *
341
		 * @see mshop/order/manager/base/delete/ansi
342
		 */
343
344
		/** mshop/order/manager/base/delete/ansi
345
		 * Deletes the items matched by the given IDs from the database
346
		 *
347
		 * Removes the records specified by the given IDs from the order database.
348
		 * The records must be from the site that is configured via the
349
		 * context item.
350
		 *
351
		 * The ":cond" placeholder is replaced by the name of the ID column and
352
		 * the given ID or list of IDs while the site ID is bound to the question
353
		 * mark.
354
		 *
355
		 * The SQL statement should conform to the ANSI standard to be
356
		 * compatible with most relational database systems. This also
357
		 * includes using double quotes for table and column names.
358
		 *
359
		 * @param string SQL statement for deleting items
360
		 * @since 2014.03
361
		 * @category Developer
362
		 * @see mshop/order/manager/base/insert/ansi
363
		 * @see mshop/order/manager/base/update/ansi
364
		 * @see mshop/order/manager/base/newid/ansi
365
		 * @see mshop/order/manager/base/search/ansi
366
		 * @see mshop/order/manager/base/count/ansi
367
		 */
368
		$path = 'mshop/order/manager/base/delete';
369
370
		return $this->deleteItemsBase( $itemIds, $path );
371
	}
372
373
374
	/**
375
	 * Returns the order base item specified by the given ID.
376
	 *
377
	 * @param string $id Unique id of the order base
378
	 * @param string[] $ref List of domains to fetch list items and referenced items for
379
	 * @param bool|null $default Add default criteria or NULL for relaxed default criteria
380
	 * @return \Aimeos\MShop\Order\Item\Base\Iface Returns Order base item of the given id
381
	 * @throws \Aimeos\MShop\Exception If item couldn't be found
382
	 */
383
	public function get( string $id, array $ref = [], ?bool $default = false ) : \Aimeos\MShop\Common\Item\Iface
384
	{
385
		return $this->getItemBase( 'order.base.id', $id, $ref, $default );
386
	}
387
388
389
	/**
390
	 * Returns the available manager types
391
	 *
392
	 * @param bool $withsub Return also the resource type of sub-managers if true
393
	 * @return string[] Type of the manager and submanagers, subtypes are separated by slashes
394
	 */
395
	public function getResourceType( bool $withsub = true ) : array
396
	{
397
		$path = 'mshop/order/manager/base/submanagers';
398
		return $this->getResourceTypeBase( 'order/base', $path, ['address', 'coupon', 'product', 'service'], $withsub );
399
	}
400
401
402
	/**
403
	 * Returns the attributes that can be used for searching.
404
	 *
405
	 * @param bool $withsub Return also attributes of sub-managers if true
406
	 * @return \Aimeos\Base\Criteria\Attribute\Iface[] List of search attribute items
407
	 */
408
	public function getSearchAttributes( bool $withsub = true ) : array
409
	{
410
		/** mshop/order/manager/base/submanagers
411
		 * List of manager names that can be instantiated by the order base manager
412
		 *
413
		 * Managers provide a generic interface to the underlying storage.
414
		 * Each manager has or can have sub-managers caring about particular
415
		 * aspects. Each of these sub-managers can be instantiated by its
416
		 * parent manager using the getSubManager() method.
417
		 *
418
		 * The search keys from sub-managers can be normally used in the
419
		 * manager as well. It allows you to search for items of the manager
420
		 * using the search keys of the sub-managers to further limit the
421
		 * retrieved list of items.
422
		 *
423
		 * @param array List of sub-manager names
424
		 * @since 2014.03
425
		 * @category Developer
426
		 */
427
		$path = 'mshop/order/manager/base/submanagers';
428
		$default = array( 'address', 'coupon', 'product', 'service' );
429
430
		return $this->getSearchAttributesBase( $this->searchConfig, $path, $default, $withsub );
431
	}
432
433
434
	/**
435
	 * Returns a new manager for order base extensions.
436
	 *
437
	 * @param string $manager Name of the sub manager type in lower case
438
	 * @param string|null $name Name of the implementation, will be from configuration (or Default) if null
439
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager for different extensions, e.g address, coupon, product, service, etc.
440
	 */
441
	public function getSubManager( string $manager, string $name = null ) : \Aimeos\MShop\Common\Manager\Iface
442
	{
443
		/** mshop/order/manager/base/name
444
		 * Class name of the used order base manager implementation
445
		 *
446
		 * Each default order base manager can be replaced by an alternative imlementation.
447
		 * To use this implementation, you have to set the last part of the class
448
		 * name as configuration value so the manager factory knows which class it
449
		 * has to instantiate.
450
		 *
451
		 * For example, if the name of the default class is
452
		 *
453
		 *  \Aimeos\MShop\Order\Manager\Base\Standard
454
		 *
455
		 * and you want to replace it with your own version named
456
		 *
457
		 *  \Aimeos\MShop\Order\Manager\Base\Mybase
458
		 *
459
		 * then you have to set the this configuration option:
460
		 *
461
		 *  mshop/order/manager/base/name = Mybase
462
		 *
463
		 * The value is the last part of your own class name and it's case sensitive,
464
		 * so take care that the configuration value is exactly named like the last
465
		 * part of the class name.
466
		 *
467
		 * The allowed characters of the class name are A-Z, a-z and 0-9. No other
468
		 * characters are possible! You should always start the last part of the class
469
		 * name with an upper case character and continue only with lower case characters
470
		 * or numbers. Avoid chamel case names like "MyBase"!
471
		 *
472
		 * @param string Last part of the class name
473
		 * @since 2014.03
474
		 * @category Developer
475
		 */
476
477
		/** mshop/order/manager/base/decorators/excludes
478
		 * Excludes decorators added by the "common" option from the order base manager
479
		 *
480
		 * Decorators extend the functionality of a class by adding new aspects
481
		 * (e.g. log what is currently done), executing the methods of the underlying
482
		 * class only in certain conditions (e.g. only for logged in users) or
483
		 * modify what is returned to the caller.
484
		 *
485
		 * This option allows you to remove a decorator added via
486
		 * "mshop/common/manager/decorators/default" before they are wrapped
487
		 * around the order base manager.
488
		 *
489
		 *  mshop/order/manager/base/decorators/excludes = array( 'decorator1' )
490
		 *
491
		 * This would remove the decorator named "decorator1" from the list of
492
		 * common decorators ("\Aimeos\MShop\Common\Manager\Decorator\*") added via
493
		 * "mshop/common/manager/decorators/default" for the order base manager.
494
		 *
495
		 * @param array List of decorator names
496
		 * @since 2014.03
497
		 * @category Developer
498
		 * @see mshop/common/manager/decorators/default
499
		 * @see mshop/order/manager/base/decorators/global
500
		 * @see mshop/order/manager/base/decorators/local
501
		 */
502
503
		/** mshop/order/manager/base/decorators/global
504
		 * Adds a list of globally available decorators only to the order base manager
505
		 *
506
		 * Decorators extend the functionality of a class by adding new aspects
507
		 * (e.g. log what is currently done), executing the methods of the underlying
508
		 * class only in certain conditions (e.g. only for logged in users) or
509
		 * modify what is returned to the caller.
510
		 *
511
		 * This option allows you to wrap global decorators
512
		 * ("\Aimeos\MShop\Common\Manager\Decorator\*") around the order base
513
		 * manager.
514
		 *
515
		 *  mshop/order/manager/base/decorators/global = array( 'decorator1' )
516
		 *
517
		 * This would add the decorator named "decorator1" defined by
518
		 * "\Aimeos\MShop\Common\Manager\Decorator\Decorator1" only to the order
519
		 * base manager.
520
		 *
521
		 * @param array List of decorator names
522
		 * @since 2014.03
523
		 * @category Developer
524
		 * @see mshop/common/manager/decorators/default
525
		 * @see mshop/order/manager/base/decorators/excludes
526
		 * @see mshop/order/manager/base/decorators/local
527
		 */
528
529
		/** mshop/order/manager/base/decorators/local
530
		 * Adds a list of local decorators only to the order base manager
531
		 *
532
		 * Decorators extend the functionality of a class by adding new aspects
533
		 * (e.g. log what is currently done), executing the methods of the underlying
534
		 * class only in certain conditions (e.g. only for logged in users) or
535
		 * modify what is returned to the caller.
536
		 *
537
		 * This option allows you to wrap local decorators
538
		 * ("\Aimeos\MShop\Order\Manager\Base\Decorator\*") around the order base
539
		 * manager.
540
		 *
541
		 *  mshop/order/manager/base/decorators/local = array( 'decorator2' )
542
		 *
543
		 * This would add the decorator named "decorator2" defined by
544
		 * "\Aimeos\MShop\Order\Manager\Base\Decorator\Decorator2" only to the
545
		 * order base manager.
546
		 *
547
		 * @param array List of decorator names
548
		 * @since 2014.03
549
		 * @category Developer
550
		 * @see mshop/common/manager/decorators/default
551
		 * @see mshop/order/manager/base/decorators/excludes
552
		 * @see mshop/order/manager/base/decorators/global
553
		 */
554
555
		return $this->getSubManagerBase( 'order', 'base/' . $manager, $name );
556
	}
557
558
559
	/**
560
	 * Adds or updates an order base item in the storage.
561
	 *
562
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $item Order base object (sub-items are not saved)
563
	 * @param bool $fetch True if the new ID should be returned in the item
564
	 * @return \Aimeos\MShop\Order\Item\Base\Iface $item Updated item including the generated ID
565
	 */
566
	public function saveItem( \Aimeos\MShop\Order\Item\Base\Iface $item, bool $fetch = true ) : \Aimeos\MShop\Order\Item\Base\Iface
567
	{
568
		if( !$item->isModified() && !$item->locale()->isModified() ) {
569
			return $this->saveBasket( $item );
570
		}
571
572
		$context = $this->context();
573
		$conn = $context->db( $this->getResourceName() );
574
575
		$id = $item->getId();
576
		$date = date( 'Y-m-d H:i:s' );
577
		$columns = $this->object()->getSaveAttributes();
578
579
		if( $id === null )
580
		{
581
			/** mshop/order/manager/base/insert/mysql
582
			 * Inserts a new order record into the database table
583
			 *
584
			 * @see mshop/order/manager/base/insert/ansi
585
			 */
586
587
			/** mshop/order/manager/base/insert/ansi
588
			 * Inserts a new order record into the database table
589
			 *
590
			 * Items with no ID yet (i.e. the ID is NULL) will be created in
591
			 * the database and the newly created ID retrieved afterwards
592
			 * using the "newid" SQL statement.
593
			 *
594
			 * The SQL statement must be a string suitable for being used as
595
			 * prepared statement. It must include question marks for binding
596
			 * the values from the order item to the statement before they are
597
			 * sent to the database server. The number of question marks must
598
			 * be the same as the number of columns listed in the INSERT
599
			 * statement. The order of the columns must correspond to the
600
			 * order in the save() method, so the correct values are
601
			 * bound to the columns.
602
			 *
603
			 * The SQL statement should conform to the ANSI standard to be
604
			 * compatible with most relational database systems. This also
605
			 * includes using double quotes for table and column names.
606
			 *
607
			 * @param string SQL statement for inserting records
608
			 * @since 2014.03
609
			 * @category Developer
610
			 * @see mshop/order/manager/base/update/ansi
611
			 * @see mshop/order/manager/base/newid/ansi
612
			 * @see mshop/order/manager/base/delete/ansi
613
			 * @see mshop/order/manager/base/search/ansi
614
			 * @see mshop/order/manager/base/count/ansi
615
			 */
616
			$path = 'mshop/order/manager/base/insert';
617
			$sql = $this->addSqlColumns( array_keys( $columns ), $this->getSqlConfig( $path ) );
0 ignored issues
show
Bug introduced by
It seems like $this->getSqlConfig($path) can also be of type array; however, parameter $sql of Aimeos\MShop\Common\Manager\Base::addSqlColumns() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

617
			$sql = $this->addSqlColumns( array_keys( $columns ), /** @scrutinizer ignore-type */ $this->getSqlConfig( $path ) );
Loading history...
618
		}
619
		else
620
		{
621
			/** mshop/order/manager/base/update/mysql
622
			 * Updates an existing order record in the database
623
			 *
624
			 * @see mshop/order/manager/base/update/ansi
625
			 */
626
627
			/** mshop/order/manager/base/update/ansi
628
			 * Updates an existing order record in the database
629
			 *
630
			 * Items which already have an ID (i.e. the ID is not NULL) will
631
			 * be updated in the database.
632
			 *
633
			 * The SQL statement must be a string suitable for being used as
634
			 * prepared statement. It must include question marks for binding
635
			 * the values from the order item to the statement before they are
636
			 * sent to the database server. The order of the columns must
637
			 * correspond to the order in the save() method, so the
638
			 * correct values are bound to the columns.
639
			 *
640
			 * The SQL statement should conform to the ANSI standard to be
641
			 * compatible with most relational database systems. This also
642
			 * includes using double quotes for table and column names.
643
			 *
644
			 * @param string SQL statement for updating records
645
			 * @since 2014.03
646
			 * @category Developer
647
			 * @see mshop/order/manager/base/insert/ansi
648
			 * @see mshop/order/manager/base/newid/ansi
649
			 * @see mshop/order/manager/base/delete/ansi
650
			 * @see mshop/order/manager/base/search/ansi
651
			 * @see mshop/order/manager/base/count/ansi
652
			 */
653
			$path = 'mshop/order/manager/base/update';
654
			$sql = $this->addSqlColumns( array_keys( $columns ), $this->getSqlConfig( $path ), false );
655
		}
656
657
		$priceItem = $item->getPrice();
658
		$localeItem = $context->locale();
659
660
		$idx = 1;
661
		$stmt = $this->getCachedStatement( $conn, $path, $sql );
662
663
		foreach( $columns as $name => $entry ) {
664
			$stmt->bind( $idx++, $item->get( $name ), $entry->getInternalType() );
665
		}
666
667
		$stmt->bind( $idx++, $item->getCustomerId() );
668
		$stmt->bind( $idx++, $localeItem->getSiteItem()->getCode() );
669
		$stmt->bind( $idx++, $item->locale()->getLanguageId() );
670
		$stmt->bind( $idx++, $priceItem->getCurrencyId() );
671
		$stmt->bind( $idx++, $priceItem->getValue() );
672
		$stmt->bind( $idx++, $priceItem->getCosts() );
673
		$stmt->bind( $idx++, $priceItem->getRebate() );
674
		$stmt->bind( $idx++, $priceItem->getTaxValue() );
675
		$stmt->bind( $idx++, $priceItem->getTaxFlag(), \Aimeos\Base\DB\Statement\Base::PARAM_INT );
676
		$stmt->bind( $idx++, $item->getCustomerReference() );
677
		$stmt->bind( $idx++, $item->getComment() );
678
		$stmt->bind( $idx++, $date ); // mtime
679
		$stmt->bind( $idx++, $context->editor() );
680
		$stmt->bind( $idx++, $localeItem->getSiteId() );
681
682
		if( $id !== null ) {
683
			$stmt->bind( $idx++, $id, \Aimeos\Base\DB\Statement\Base::PARAM_INT );
684
		} else {
685
			$stmt->bind( $idx++, $date ); // ctime
686
		}
687
688
		$stmt->execute()->finish();
689
690
		if( $id === null && $fetch === true )
691
		{
692
			/** mshop/order/manager/base/newid/mysql
693
			 * Retrieves the ID generated by the database when inserting a new record
694
			 *
695
			 * @see mshop/order/manager/base/newid/ansi
696
			 */
697
698
			/** mshop/order/manager/base/newid/ansi
699
			 * Retrieves the ID generated by the database when inserting a new record
700
			 *
701
			 * As soon as a new record is inserted into the database table,
702
			 * the database server generates a new and unique identifier for
703
			 * that record. This ID can be used for retrieving, updating and
704
			 * deleting that specific record from the table again.
705
			 *
706
			 * For MySQL:
707
			 *  SELECT LAST_INSERT_ID()
708
			 * For PostgreSQL:
709
			 *  SELECT currval('seq_mord_id')
710
			 * For SQL Server:
711
			 *  SELECT SCOPE_IDENTITY()
712
			 * For Oracle:
713
			 *  SELECT "seq_mord_id".CURRVAL FROM DUAL
714
			 *
715
			 * There's no way to retrive the new ID by a SQL statements that
716
			 * fits for most database servers as they implement their own
717
			 * specific way.
718
			 *
719
			 * @param string SQL statement for retrieving the last inserted record ID
720
			 * @since 2014.03
721
			 * @category Developer
722
			 * @see mshop/order/manager/base/insert/ansi
723
			 * @see mshop/order/manager/base/update/ansi
724
			 * @see mshop/order/manager/base/delete/ansi
725
			 * @see mshop/order/manager/base/search/ansi
726
			 * @see mshop/order/manager/base/count/ansi
727
			 */
728
			$path = 'mshop/order/manager/base/newid';
729
			$id = $this->newId( $conn, $path );
730
		}
731
732
		$item->setId( $id );
733
734
		return $this->saveBasket( $item );
735
	}
736
737
738
	/**
739
	 * Search for orders based on the given criteria.
740
	 *
741
	 * @param \Aimeos\Base\Criteria\Iface $search Search criteria object
742
	 * @param string[] $ref List of domains to fetch list items and referenced items for, e.g.
743
	 *	"order/base/address", "order/base/coupon", "order/base/product", "order/base/service"
744
	 * @param int|null &$total Number of items that are available in total
745
	 * @return \Aimeos\Map List of items implementing \Aimeos\MShop\Order\Item\Base\Iface with ids as keys
746
	 */
747
	public function search( \Aimeos\Base\Criteria\Iface $search, array $ref = [], int &$total = null ) : \Aimeos\Map
748
	{
749
		$context = $this->context();
750
		$priceManager = \Aimeos\MShop::create( $context, 'price' );
751
		$localeManager = \Aimeos\MShop::create( $context, 'locale' );
752
753
		$conn = $context->db( $this->getResourceName() );
754
		$map = $items = $custItems = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $items is dead and can be removed.
Loading history...
755
756
			$required = array( 'order.base' );
757
758
			$level = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
759
			$level = $context->config()->get( 'mshop/order/manager/sitemode', $level );
760
761
			/** mshop/order/manager/base/search/mysql
762
			 * Retrieves the records matched by the given criteria in the database
763
			 *
764
			 * @see mshop/order/manager/base/search/ansi
765
			 */
766
767
			/** mshop/order/manager/base/search/ansi
768
			 * Retrieves the records matched by the given criteria in the database
769
			 *
770
			 * Fetches the records matched by the given criteria from the order
771
			 * database. The records must be from one of the sites that are
772
			 * configured via the context item. If the current site is part of
773
			 * a tree of sites, the SELECT statement can retrieve all records
774
			 * from the current site and the complete sub-tree of sites.
775
			 *
776
			 * As the records can normally be limited by criteria from sub-managers,
777
			 * their tables must be joined in the SQL context. This is done by
778
			 * using the "internaldeps" property from the definition of the ID
779
			 * column of the sub-managers. These internal dependencies specify
780
			 * the JOIN between the tables and the used columns for joining. The
781
			 * ":joins" placeholder is then replaced by the JOIN strings from
782
			 * the sub-managers.
783
			 *
784
			 * To limit the records matched, conditions can be added to the given
785
			 * criteria object. It can contain comparisons like column names that
786
			 * must match specific values which can be combined by AND, OR or NOT
787
			 * operators. The resulting string of SQL conditions replaces the
788
			 * ":cond" placeholder before the statement is sent to the database
789
			 * server.
790
			 *
791
			 * If the records that are retrieved should be ordered by one or more
792
			 * columns, the generated string of column / sort direction pairs
793
			 * replaces the ":order" placeholder. In case no ordering is required,
794
			 * the complete ORDER BY part including the "\/*-orderby*\/...\/*orderby-*\/"
795
			 * markers is removed to speed up retrieving the records. Columns of
796
			 * sub-managers can also be used for ordering the result set but then
797
			 * no index can be used.
798
			 *
799
			 * The number of returned records can be limited and can start at any
800
			 * number between the begining and the end of the result set. For that
801
			 * the ":size" and ":start" placeholders are replaced by the
802
			 * corresponding values from the criteria object. The default values
803
			 * are 0 for the start and 100 for the size value.
804
			 *
805
			 * The SQL statement should conform to the ANSI standard to be
806
			 * compatible with most relational database systems. This also
807
			 * includes using double quotes for table and column names.
808
			 *
809
			 * @param string SQL statement for searching items
810
			 * @since 2014.03
811
			 * @category Developer
812
			 * @see mshop/order/manager/base/insert/ansi
813
			 * @see mshop/order/manager/base/update/ansi
814
			 * @see mshop/order/manager/base/newid/ansi
815
			 * @see mshop/order/manager/base/delete/ansi
816
			 * @see mshop/order/manager/base/count/ansi
817
			 */
818
			$cfgPathSearch = 'mshop/order/manager/base/search';
819
820
			/** mshop/order/manager/base/count/mysql
821
			 * Counts the number of records matched by the given criteria in the database
822
			 *
823
			 * @see mshop/order/manager/base/count/ansi
824
			 */
825
826
			/** mshop/order/manager/base/count/ansi
827
			 * Counts the number of records matched by the given criteria in the database
828
			 *
829
			 * Counts all records matched by the given criteria from the order
830
			 * database. The records must be from one of the sites that are
831
			 * configured via the context item. If the current site is part of
832
			 * a tree of sites, the statement can count all records from the
833
			 * current site and the complete sub-tree of sites.
834
			 *
835
			 * As the records can normally be limited by criteria from sub-managers,
836
			 * their tables must be joined in the SQL context. This is done by
837
			 * using the "internaldeps" property from the definition of the ID
838
			 * column of the sub-managers. These internal dependencies specify
839
			 * the JOIN between the tables and the used columns for joining. The
840
			 * ":joins" placeholder is then replaced by the JOIN strings from
841
			 * the sub-managers.
842
			 *
843
			 * To limit the records matched, conditions can be added to the given
844
			 * criteria object. It can contain comparisons like column names that
845
			 * must match specific values which can be combined by AND, OR or NOT
846
			 * operators. The resulting string of SQL conditions replaces the
847
			 * ":cond" placeholder before the statement is sent to the database
848
			 * server.
849
			 *
850
			 * Both, the strings for ":joins" and for ":cond" are the same as for
851
			 * the "search" SQL statement.
852
			 *
853
			 * Contrary to the "search" statement, it doesn't return any records
854
			 * but instead the number of records that have been found. As counting
855
			 * thousands of records can be a long running task, the maximum number
856
			 * of counted records is limited for performance reasons.
857
			 *
858
			 * The SQL statement should conform to the ANSI standard to be
859
			 * compatible with most relational database systems. This also
860
			 * includes using double quotes for table and column names.
861
			 *
862
			 * @param string SQL statement for counting items
863
			 * @since 2014.03
864
			 * @category Developer
865
			 * @see mshop/order/manager/base/insert/ansi
866
			 * @see mshop/order/manager/base/update/ansi
867
			 * @see mshop/order/manager/base/newid/ansi
868
			 * @see mshop/order/manager/base/delete/ansi
869
			 * @see mshop/order/manager/base/search/ansi
870
			 */
871
			$cfgPathCount = 'mshop/order/manager/base/count';
872
873
			$results = $this->searchItemsBase( $conn, $search, $cfgPathSearch, $cfgPathCount,
874
				$required, $total, $level );
875
876
			while( ( $row = $results->fetch() ) !== null ) {
877
				$map[$row['order.base.id']] = $row;
878
			}
879
880
		if( ( isset( $ref['customer'] ) || in_array( 'customer', $ref ) )
881
			&& !( $ids = map( $map )->col( 'order.base.customerid' )->filter() )->empty()
882
		) {
883
			$manager = \Aimeos\MShop::create( $context, 'customer' );
884
			$search = $manager->filter()->slice( 0, count( $ids ) )->add( ['customer.id' => $ids] );
885
			$custItems = $manager->search( $search, $ref );
886
		}
887
888
		foreach( $map as $id => $row )
889
		{
890
			// don't use fromArray() or set*() methods to avoid recalculation of tax value
891
			$price = $priceManager->create( [
892
				'price.currencyid' => $row['order.base.currencyid'],
893
				'price.value' => $row['order.base.price'],
894
				'price.costs' => $row['order.base.costs'],
895
				'price.rebate' => $row['order.base.rebate'],
896
				'price.taxflag' => $row['order.base.taxflag'],
897
				'price.taxvalue' => $row['order.base.taxvalue'],
898
			] );
899
900
			// you may need the site object! take care!
901
			$localeItem = $localeManager->create( [
902
				'locale.currencyid' => $row['order.base.currencyid'],
903
				'locale.languageid' => $row['order.base.languageid'],
904
				'locale.siteid' => $row['order.base.siteid'],
905
			] );
906
907
			$map[$id] = [$price, $localeItem, $row, $custItems[$row['order.base.customerid'] ?? null] ?? null];
908
		}
909
910
		return $this->buildItems( $map, $ref );
911
	}
912
913
914
	/**
915
	 * Creates a new basket containing the items from the order excluding the coupons.
916
	 * If the last parameter is ture, the items will be marked as new and
917
	 * modified so an additional order is stored when the basket is saved.
918
	 *
919
	 * @param string $id Base ID of the order to load
920
	 * @param array $ref Basket parts that should be loaded too
921
	 * @param bool $fresh Create a new basket by copying the existing one and remove IDs
922
	 * @param bool $default True to use default criteria, false for no limitation
923
	 * @return \Aimeos\MShop\Order\Item\Base\Iface Basket including all items
924
	 */
925
	public function load( string $id, array $ref = ['order/base/address', 'order/base/coupon', 'order/base/product', 'order/base/service'],
926
		bool $fresh = false, bool $default = false ) : \Aimeos\MShop\Order\Item\Base\Iface
927
	{
928
		$search = $this->object()->filter( $default );
929
		$expr = [
930
			$search->compare( '==', 'order.base.id', $id ),
931
			$search->getConditions(),
932
		];
933
		$search->setConditions( $search->and( $expr ) );
934
935
		$context = $this->context();
936
		$conn = $context->db( $this->getResourceName() );
937
938
		$sitelevel = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
939
		$sitelevel = $context->config()->get( 'mshop/order/manager/sitemode', $sitelevel );
940
941
		$cfgPathSearch = 'mshop/order/manager/base/search';
942
		$cfgPathCount = 'mshop/order/manager/base/count';
943
		$required = array( 'order.base' );
944
		$total = null;
945
946
		$results = $this->searchItemsBase( $conn, $search, $cfgPathSearch, $cfgPathCount, $required, $total, $sitelevel );
947
948
		if( ( $row = $results->fetch() ) === null )
949
		{
950
			$msg = $this->context()->translate( 'mshop', 'Order base item with order ID "%1$s" not found' );
951
			throw new \Aimeos\MShop\Order\Exception( sprintf( $msg, $id ) );
952
		}
953
		$results->finish();
954
955
		$priceManager = \Aimeos\MShop::create( $context, 'price' );
956
		$localeManager = \Aimeos\MShop::create( $context, 'locale' );
957
958
		$price = $priceManager->create( [
959
			'price.currencyid' => $row['order.base.currencyid'],
960
			'price.value' => $row['order.base.price'],
961
			'price.costs' => $row['order.base.costs'],
962
			'price.rebate' => $row['order.base.rebate'],
963
			'price.taxflag' => $row['order.base.taxflag'],
964
			'price.taxvalue' => $row['order.base.taxvalue'],
965
		] );
966
967
		// you may need the site object! take care!
968
		$localeItem = $localeManager->create( [
969
			'locale.languageid' => $row['order.base.languageid'],
970
			'locale.currencyid' => $row['order.base.currencyid'],
971
			'locale.siteid' => $row['order.base.siteid'],
972
		] );
973
974
		if( $fresh === false ) {
975
			$basket = $this->loadItems( $id, $price, $localeItem, $row, $ref );
976
		} else {
977
			$basket = $this->loadFresh( $id, $price, $localeItem, $row, $ref );
978
		}
979
980
		return $basket;
981
	}
982
983
984
	/**
985
	 * Creates the order base item objects from the map and adds the referenced items
986
	 *
987
	 * @param array $map Associative list of order base IDs as keys and list of price/locale/row as values
988
	 * @param string[] $ref Domain items that should be added as well, e.g.
989
	 *	"order/base/address", "order/base/coupon", "order/base/product", "order/base/service"
990
	 * @return \Aimeos\Map List of items implementing \Aimeos\MShop\Order\Item\Base\Iface with IDs as keys
991
	 */
992
	protected function buildItems( array $map, array $ref ) : \Aimeos\Map
993
	{
994
		$items = [];
995
		$baseIds = array_keys( $map );
996
		$addressMap = $couponMap = $productMap = $serviceMap = [];
997
998
		if( in_array( 'order/base/address', $ref ) ) {
999
			$addressMap = $this->getAddresses( $baseIds );
1000
		}
1001
1002
		if( in_array( 'order/base/product', $ref ) ) {
1003
			$productMap = $this->getProducts( $baseIds );
1004
		}
1005
1006
		if( in_array( 'order/base/coupon', $ref ) ) {
1007
			$couponMap = $this->getCoupons( $baseIds, false, $productMap );
1008
		}
1009
1010
		if( in_array( 'order/base/service', $ref ) ) {
1011
			$serviceMap = $this->getServices( $baseIds );
1012
		}
1013
1014
		foreach( $map as $id => $list )
1015
		{
1016
			list( $price, $locale, $row, $custItem ) = $list;
1017
1018
			$addresses = $addressMap[$id] ?? [];
1019
			$coupons = $couponMap[$id] ?? [];
1020
			$products = $productMap[$id] ?? [];
1021
			$services = $serviceMap[$id] ?? [];
1022
1023
			$item = $this->createItemBase( $price, $locale, $row, $products, $addresses, $services, $coupons, $custItem );
1024
1025
			if( $item = $this->applyFilter( $item ) ) {
1026
				$items[$id] = $item;
1027
			}
1028
		}
1029
1030
		return map( $items );
1031
	}
1032
1033
1034
	/**
1035
	 * Returns a new and empty order base item (shopping basket).
1036
	 *
1037
	 * @param \Aimeos\MShop\Price\Item\Iface $price Default price of the basket (usually 0.00)
1038
	 * @param \Aimeos\MShop\Locale\Item\Iface $locale Locale item containing the site, language and currency
1039
	 * @param array $values Associative list of key/value pairs containing, e.g. the order or user ID
1040
	 * @param \Aimeos\MShop\Order\Item\Base\Product\Iface[] $products List of ordered product items
1041
	 * @param \Aimeos\MShop\Order\Item\Base\Address\Iface[] $addresses List of order address items
1042
	 * @param \Aimeos\MShop\Order\Item\Base\Service\Iface[] $services List of order serviceitems
1043
	 * @param \Aimeos\MShop\Order\Item\Base\Product\Iface[] $coupons Associative list of coupon codes as keys and items as values
1044
	 * @param \Aimeos\MShop\Customer\Item\Iface|null $custItem Customer item object if requested
1045
	 * @return \Aimeos\MShop\Order\Item\Base\Iface Order base object
1046
	 */
1047
	protected function createItemBase( \Aimeos\MShop\Price\Item\Iface $price, \Aimeos\MShop\Locale\Item\Iface $locale,
1048
		array $values = [], array $products = [], array $addresses = [], array $services = [], array $coupons = [],
1049
		?\Aimeos\MShop\Customer\Item\Iface $custItem = null ) : \Aimeos\MShop\Order\Item\Base\Iface
1050
	{
1051
		return new \Aimeos\MShop\Order\Item\Base\Standard( $price, $locale,
1052
			$values, $products, $addresses, $services, $coupons, $custItem );
1053
	}
1054
1055
1056
	/**
1057
	 * Saves the modified basket content
1058
	 *
1059
	 * @param \Aimeos\MShop\Order\Item\Base\Iface $basket Basket content
1060
	 * @return \Aimeos\MShop\Order\Item\Base\Iface Saved basket content
1061
	 */
1062
	protected function saveBasket( \Aimeos\MShop\Order\Item\Base\Iface $basket ) : \Aimeos\MShop\Order\Item\Base\Iface
1063
	{
1064
		$this->saveAddresses( $basket );
1065
		$this->saveServices( $basket );
1066
		$this->saveProducts( $basket );
1067
		$this->saveCoupons( $basket );
1068
1069
		return $basket;
1070
	}
1071
}
1072