Completed
Push — master ( 82ed2a...577910 )
by Aimeos
14:13
created

Laravel::aggregate()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 55
rs 8.9818
c 0
b 0
f 0
cc 1
nc 1
nop 4

How to fix   Long Method   

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, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2015-2021
6
 * @package MShop
7
 * @subpackage Customer
8
 */
9
10
11
namespace Aimeos\MShop\Customer\Manager;
12
13
14
/**
15
 * Customer class implementation for Friends of Symfony user bundle.
16
 *
17
 * @package MShop
18
 * @subpackage Customer
19
 */
20
class Laravel
21
	extends \Aimeos\MShop\Customer\Manager\Standard
22
{
23
	private $searchConfig = array(
24
		'customer.id' => array(
25
			'label' => 'Customer ID',
26
			'code' => 'customer.id',
27
			'internalcode' => 'lvu."id"',
28
			'type' => 'integer',
29
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
30
			'public' => false,
31
		),
32
		'customer.siteid' => array(
33
			'code' =>'customer.siteid',
34
			'internalcode' =>'lvu."siteid"',
35
			'label' =>'Customer site ID',
36
			'type' => 'string',
37
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
38
			'public' => false,
39
		),
40
		'customer.code' => array(
41
			'label' => 'Customer username',
42
			'code' => 'customer.code',
43
			'internalcode' => 'lvu."email"',
44
			'type' => 'string',
45
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR
46
		),
47
		'customer.label' => array(
48
			'label' => 'Customer label',
49
			'code' => 'customer.label',
50
			'internalcode' => 'lvu."name"',
51
			'type' => 'string',
52
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR
53
		),
54
		'customer.salutation' => array(
55
			'label' => 'Customer salutation',
56
			'code' => 'customer.salutation',
57
			'internalcode' => 'lvu."salutation"',
58
			'type' => 'string',
59
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
60
		),
61
		'customer.company' => array(
62
			'label' => 'Customer company',
63
			'code' => 'customer.company',
64
			'internalcode' => 'lvu."company"',
65
			'type' => 'string',
66
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
67
		),
68
		'customer.vatid' => array(
69
			'label' => 'Customer VAT ID',
70
			'code' => 'customer.vatid',
71
			'internalcode' => 'lvu."vatid"',
72
			'type' => 'string',
73
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
74
		),
75
		'customer.title' => array(
76
			'label' => 'Customer title',
77
			'code' => 'customer.title',
78
			'internalcode' => 'lvu."title"',
79
			'type' => 'string',
80
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
81
		),
82
		'customer.firstname' => array(
83
			'label' => 'Customer firstname',
84
			'code' => 'customer.firstname',
85
			'internalcode' => 'lvu."firstname"',
86
			'type' => 'string',
87
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
88
		),
89
		'customer.lastname' => array(
90
			'label' => 'Customer lastname',
91
			'code' => 'customer.lastname',
92
			'internalcode' => 'lvu."lastname"',
93
			'type' => 'string',
94
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
95
		),
96
		'customer.address1' => array(
97
			'label' => 'Customer address part one',
98
			'code' => 'customer.address1',
99
			'internalcode' => 'lvu."address1"',
100
			'type' => 'string',
101
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
102
		),
103
		'customer.address2' => array(
104
			'label' => 'Customer address part two',
105
			'code' => 'customer.address2',
106
			'internalcode' => 'lvu."address2"',
107
			'type' => 'string',
108
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
109
		),
110
		'customer.address3' => array(
111
			'label' => 'Customer address part three',
112
			'code' => 'customer.address3',
113
			'internalcode' => 'lvu."address3"',
114
			'type' => 'string',
115
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
116
		),
117
		'customer.postal' => array(
118
			'label' => 'Customer postal',
119
			'code' => 'customer.postal',
120
			'internalcode' => 'lvu."postal"',
121
			'type' => 'string',
122
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
123
		),
124
		'customer.city' => array(
125
			'label' => 'Customer city',
126
			'code' => 'customer.city',
127
			'internalcode' => 'lvu."city"',
128
			'type' => 'string',
129
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
130
		),
131
		'customer.state' => array(
132
			'label' => 'Customer state',
133
			'code' => 'customer.state',
134
			'internalcode' => 'lvu."state"',
135
			'type' => 'string',
136
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
137
		),
138
		'customer.languageid' => array(
139
			'label' => 'Customer language',
140
			'code' => 'customer.languageid',
141
			'internalcode' => 'lvu."langid"',
142
			'type' => 'string',
143
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
144
		),
145
		'customer.countryid' => array(
146
			'label' => 'Customer country',
147
			'code' => 'customer.countryid',
148
			'internalcode' => 'lvu."countryid"',
149
			'type' => 'string',
150
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
151
		),
152
		'customer.telephone' => array(
153
			'label' => 'Customer telephone',
154
			'code' => 'customer.telephone',
155
			'internalcode' => 'lvu."telephone"',
156
			'type' => 'string',
157
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
158
		),
159
		'customer.email' => array(
160
			'label' => 'Customer email',
161
			'code' => 'customer.email',
162
			'internalcode' => 'lvu."email"',
163
			'type' => 'string',
164
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
165
		),
166
		'customer.telefax' => array(
167
			'label' => 'Customer telefax',
168
			'code' => 'customer.telefax',
169
			'internalcode' => 'lvu."telefax"',
170
			'type' => 'string',
171
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
172
		),
173
		'customer.website' => array(
174
			'label' => 'Customer website',
175
			'code' => 'customer.website',
176
			'internalcode' => 'lvu."website"',
177
			'type' => 'string',
178
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
179
		),
180
		'customer.longitude' => array(
181
			'label' => 'Customer longitude',
182
			'code' => 'customer.longitude',
183
			'internalcode' => 'lvu."longitude"',
184
			'type' => 'float',
185
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_FLOAT,
186
		),
187
		'customer.latitude' => array(
188
			'label' => 'Customer latitude',
189
			'code' => 'customer.latitude',
190
			'internalcode' => 'lvu."latitude"',
191
			'type' => 'float',
192
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_FLOAT,
193
		),
194
		'customer.birthday' => array(
195
			'label' => 'Customer birthday',
196
			'code' => 'customer.birthday',
197
			'internalcode' => 'lvu."birthday"',
198
			'type' => 'string',
199
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
200
		),
201
		'customer.password' => array(
202
			'label' => 'Customer password',
203
			'code' => 'customer.password',
204
			'internalcode' => 'lvu."password"',
205
			'type' => 'string',
206
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
207
		),
208
		'customer.status' => array(
209
			'label' => 'Customer status',
210
			'code' => 'customer.status',
211
			'internalcode' => 'lvu."status"',
212
			'type' => 'integer',
213
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT
214
		),
215
		'customer.dateverified' => array(
216
			'label' => 'Customer verification date',
217
			'code' => 'customer.dateverified',
218
			'internalcode' => 'lvu."email_verified_at"',
219
			'type' => 'date',
220
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
221
		),
222
		'customer.ctime' => array(
223
			'label' => 'Customer creation time',
224
			'code' => 'customer.ctime',
225
			'internalcode' => 'lvu."created_at"',
226
			'type' => 'datetime',
227
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
228
		),
229
		'customer.mtime' => array(
230
			'label' => 'Customer modification time',
231
			'code' => 'customer.mtime',
232
			'internalcode' => 'lvu."updated_at"',
233
			'type' => 'datetime',
234
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
235
		),
236
		'customer.editor' => array(
237
			'label' =>'Customer editor',
238
			'code' =>'customer.editor',
239
			'internalcode' => 'lvu."editor"',
240
			'type' => 'string',
241
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
242
		),
243
		'customer:has' => array(
244
			'code' => 'customer:has()',
245
			'internalcode' => ':site AND :key AND lvuli."id"',
246
			'internaldeps' => ['LEFT JOIN "users_list" AS lvuli ON ( lvuli."parentid" = lvu."id" )'],
247
			'label' => 'Customer has list item, parameter(<domain>[,<list type>[,<reference ID>)]]',
248
			'type' => 'null',
249
			'internaltype' => 'null',
250
			'public' => false,
251
		),
252
		'customer:prop' => array(
253
			'code' => 'customer:prop()',
254
			'internalcode' => ':site AND :key AND lvupr."id"',
255
			'internaldeps' => ['LEFT JOIN "users_property" AS lvupr ON ( lvupr."parentid" = lvu."id" )'],
256
			'label' => 'Customer has property item, parameter(<property type>[,<language code>[,<property value>]])',
257
			'type' => 'null',
258
			'internaltype' => 'null',
259
			'public' => false,
260
		),
261
	);
262
263
264
	/**
265
	 * Initializes the object.
266
	 *
267
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context object
268
	 */
269
	public function __construct( \Aimeos\MShop\Context\Item\Iface $context )
270
	{
271
		parent::__construct( $context );
272
273
		$level = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
274
		$level = $context->getConfig()->get( 'mshop/customer/manager/sitemode', $level );
275
276
277
		$this->searchConfig['customer:has']['function'] = function( &$source, array $params ) use ( $level ) {
278
279
			$keys = [];
280
281
			foreach( (array) ( $params[1] ?? '' ) as $type ) {
282
				foreach( (array) ( $params[2] ?? '' ) as $id ) {
283
					$keys[] = $params[0] . '|' . ( $type ? $type . '|' : '' ) . $id;
284
				}
285
			}
286
287
			$sitestr = $this->getSiteString( 'lvuli."siteid"', $level );
288
			$keystr = $this->toExpression( 'lvuli."key"', $keys, ( $params[2] ?? null ) ? '==' : '=~' );
289
			$source = str_replace( [':site', ':key'], [$sitestr, $keystr], $source );
290
291
			return $params;
292
		};
293
294
295
		$this->searchConfig['customer:prop']['function'] = function( &$source, array $params ) use ( $level ) {
296
297
			$keys = [];
298
			$langs = array_key_exists( 1, $params ) ? ( $params[1] ?? 'null' ) : '';
299
300
			foreach( (array) $langs as $lang ) {
301
				foreach( (array) ( $params[2] ?? '' ) as $id ) {
302
					$keys[] = $params[0] . '|' . ( $lang === null ? 'null|' : ( $lang ? $lang . '|' : '' ) ) . ( $id != '' ? md5( $id ) : '' );
303
				}
304
			}
305
306
			$sitestr = $this->getSiteString( 'lvupr."siteid"', $level );
307
			$keystr = $this->toExpression( 'lvupr."key"', $keys, ( $params[2] ?? null ) ? '==' : '=~' );
308
			$source = str_replace( [':site', ':key'], [$sitestr, $keystr], $source );
309
310
			return $params;
311
		};
312
	}
313
314
315
	/**
316
	 * Counts the number items that are available for the values of the given key.
317
	 *
318
	 * @param \Aimeos\MW\Criteria\Iface $search Search criteria
319
	 * @param array|string $key Search key or list of key to aggregate items for
320
	 * @param string|null $value Search key for aggregating the value column
321
	 * @param string|null $type Type of the aggregation, empty string for count or "sum" or "avg" (average)
322
	 * @return \Aimeos\Map List of the search keys as key and the number of counted items as value
323
	 */
324
	public function aggregate( \Aimeos\MW\Criteria\Iface $search, $key, string $value = null, string $type = null ) : \Aimeos\Map
325
	{
326
		/** mshop/customer/manager/laravel//aggregate/mysql
327
		 * Counts the number of records grouped by the values in the key column and matched by the given criteria
328
		 *
329
		 * @see mshop/customer/manager/laravel//aggregate/ansi
330
		 */
331
332
		/** mshop/customer/manager/laravel//aggregate/ansi
333
		 * Counts the number of records grouped by the values in the key column and matched by the given criteria
334
		 *
335
		 * Groups all records by the values in the key column and counts their
336
		 * occurence. The matched records can be limited by the given criteria
337
		 * from the customer database. The records must be from one of the sites
338
		 * that are configured via the context item. If the current site is part
339
		 * of a tree of sites, the statement can count all records from the
340
		 * current site and the complete sub-tree of sites.
341
		 *
342
		 * As the records can normally be limited by criteria from sub-managers,
343
		 * their tables must be joined in the SQL context. This is done by
344
		 * using the "internaldeps" property from the definition of the ID
345
		 * column of the sub-managers. These internal dependencies specify
346
		 * the JOIN between the tables and the used columns for joining. The
347
		 * ":joins" placeholder is then replaced by the JOIN strings from
348
		 * the sub-managers.
349
		 *
350
		 * To limit the records matched, conditions can be added to the given
351
		 * criteria object. It can contain comparisons like column names that
352
		 * must match specific values which can be combined by AND, OR or NOT
353
		 * operators. The resulting string of SQL conditions replaces the
354
		 * ":cond" placeholder before the statement is sent to the database
355
		 * server.
356
		 *
357
		 * This statement doesn't return any records. Instead, it returns pairs
358
		 * of the different values found in the key column together with the
359
		 * number of records that have been found for that key values.
360
		 *
361
		 * The SQL statement should conform to the ANSI standard to be
362
		 * compatible with most relational database systems. This also
363
		 * includes using double quotes for table and column names.
364
		 *
365
		 * @param string SQL statement for aggregating customer items
366
		 * @since 2021.04
367
		 * @category Developer
368
		 * @see mshop/customer/manager/laravel//insert/ansi
369
		 * @see mshop/customer/manager/laravel//update/ansi
370
		 * @see mshop/customer/manager/laravel//newid/ansi
371
		 * @see mshop/customer/manager/laravel//delete/ansi
372
		 * @see mshop/customer/manager/laravel//search/ansi
373
		 * @see mshop/customer/manager/laravel//count/ansi
374
		 */
375
376
		$cfgkey = 'mshop/customer/manager/laravel/aggregate' . $type;
377
		return $this->aggregateBase( $search, $key, $cfgkey, ['customer'], $value );
378
	}
379
380
381
	/**
382
	 * Removes old entries from the storage.
383
	 *
384
	 * @param iterable $siteids List of IDs for sites whose entries should be deleted
385
	 * @return \Aimeos\MShop\Common\Manager\Iface Same object for fluent interface
386
	 */
387
	public function clear( iterable $siteids ) : \Aimeos\MShop\Common\Manager\Iface
388
	{
389
		$path = 'mshop/customer/manager/submanagers';
390
		$default = ['address', 'group', 'lists', 'property'];
391
392
		foreach( $this->getContext()->getConfig()->get( $path, $default ) as $domain ) {
393
			$this->getObject()->getSubManager( $domain )->clear( $siteids );
394
		}
395
396
		return $this->clearBase( $siteids, 'mshop/customer/manager/laravel/delete' );
397
	}
398
399
400
	/**
401
	 * Removes multiple items.
402
	 *
403
	 * @param \Aimeos\MShop\Common\Item\Iface[]|string[] $itemIds List of item objects or IDs of the items
404
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
405
	 */
406
	public function delete( $itemIds ) : \Aimeos\MShop\Common\Manager\Iface
407
	{
408
		$path = 'mshop/customer/manager/laravel/delete';
409
		return $this->deleteItemsBase( $itemIds, $path )->deleteRefItems( $itemIds );
410
	}
411
412
413
	/**
414
	 * Returns the list attributes that can be used for searching.
415
	 *
416
	 * @param bool $withsub Return also attributes of sub-managers if true
417
	 * @return array List of attribute items implementing \Aimeos\MW\Criteria\Attribute\Iface
418
	 */
419
	public function getSearchAttributes( bool $withsub = true ) : array
420
	{
421
		$path = 'mshop/customer/manager/submanagers';
422
		return $this->getSearchAttributesBase( $this->searchConfig, $path, ['address'], $withsub );
423
	}
424
425
426
	/**
427
	 * Saves a customer item object.
428
	 *
429
	 * @param \Aimeos\MShop\Customer\Item\Iface $item Customer item object
430
	 * @param boolean $fetch True if the new ID should be returned in the item
431
	 * @return \Aimeos\MShop\Customer\Item\Iface $item Updated item including the generated ID
432
	 */
433
	public function saveItem( \Aimeos\MShop\Customer\Item\Iface $item, bool $fetch = true ) : \Aimeos\MShop\Customer\Item\Iface
434
	{
435
		self::checkClass( '\\Aimeos\\MShop\\Customer\\Item\\Iface', $item );
436
437
		$item = $this->addGroups( $item );
438
439
		if( !$item->isModified() )
440
		{
441
			$item = $this->savePropertyItems( $item, 'customer' );
442
			$item = $this->saveAddressItems( $item, 'customer' );
0 ignored issues
show
Documentation introduced by
$item is of type object<Aimeos\MShop\Comm...Item\PropertyRef\Iface>, but the function expects a object<Aimeos\MShop\Common\Item\AddressRef\Iface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
443
			return $this->saveListItems( $item, 'customer' );
0 ignored issues
show
Documentation introduced by
$item is of type object<Aimeos\MShop\Common\Item\AddressRef\Iface>, but the function expects a object<Aimeos\MShop\Common\Item\ListsRef\Iface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
444
		}
445
446
		$context = $this->getContext();
447
		$dbm = $context->getDatabaseManager();
448
		$dbname = $this->getResourceName();
449
		$conn = $dbm->acquire( $dbname );
450
451
		try
452
		{
453
			$id = $item->getId();
454
			$date = date( 'Y-m-d H:i:s' );
455
			$billingAddress = $item->getPaymentAddress();
456
			$columns = $this->getObject()->getSaveAttributes();
457
458
			if( $id === null )
459
			{
460
				/** mshop/customer/manager/laravel/insert
461
				 * Inserts a new customer record into the database table
462
				 *
463
				 * Items with no ID yet (i.e. the ID is NULL) will be created in
464
				 * the database and the newly created ID retrieved afterwards
465
				 * using the "newid" SQL statement.
466
				 *
467
				 * The SQL statement must be a string suitable for being used as
468
				 * prepared statement. It must include question marks for binding
469
				 * the values from the customer item to the statement before they are
470
				 * sent to the database server. The number of question marks must
471
				 * be the same as the number of columns listed in the INSERT
472
				 * statement. The order of the columns must correspond to the
473
				 * order in the save() method, so the correct values are
474
				 * bound to the columns.
475
				 *
476
				 * The SQL statement should conform to the ANSI standard to be
477
				 * compatible with most relational database systems. This also
478
				 * includes using double quotes for table and column names.
479
				 *
480
				 * @param string SQL statement for inserting records
481
				 * @since 2015.01
482
				 * @category Developer
483
				 * @see mshop/customer/manager/laravel/update
484
				 * @see mshop/customer/manager/laravel/newid
485
				 * @see mshop/customer/manager/laravel/delete
486
				 * @see mshop/customer/manager/laravel/search
487
				 * @see mshop/customer/manager/laravel/count
488
				 */
489
				$path = 'mshop/customer/manager/laravel/insert';
490
				$sql = $this->addSqlColumns( array_keys( $columns ), $this->getSqlConfig( $path ) );
0 ignored issues
show
Bug introduced by
It seems like $this->getSqlConfig($path) targeting Aimeos\MShop\Common\Manager\Base::getSqlConfig() can also be of type array; however, Aimeos\MShop\Common\Manager\Base::addSqlColumns() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
491
			}
492
			else
493
			{
494
				/** mshop/customer/manager/laravel/update
495
				 * Updates an existing customer record in the database
496
				 *
497
				 * Items which already have an ID (i.e. the ID is not NULL) will
498
				 * be updated in the database.
499
				 *
500
				 * The SQL statement must be a string suitable for being used as
501
				 * prepared statement. It must include question marks for binding
502
				 * the values from the customer item to the statement before they are
503
				 * sent to the database server. The order of the columns must
504
				 * correspond to the order in the save() method, so the
505
				 * correct values are bound to the columns.
506
				 *
507
				 * The SQL statement should conform to the ANSI standard to be
508
				 * compatible with most relational database systems. This also
509
				 * includes using double quotes for table and column names.
510
				 *
511
				 * @param string SQL statement for updating records
512
				 * @since 2015.01
513
				 * @category Developer
514
				 * @see mshop/customer/manager/laravel/insert
515
				 * @see mshop/customer/manager/laravel/newid
516
				 * @see mshop/customer/manager/laravel/delete
517
				 * @see mshop/customer/manager/laravel/search
518
				 * @see mshop/customer/manager/laravel/count
519
				 */
520
				$path = 'mshop/customer/manager/laravel/update';
521
				$sql = $this->addSqlColumns( array_keys( $columns ), $this->getSqlConfig( $path ), false );
0 ignored issues
show
Bug introduced by
It seems like $this->getSqlConfig($path) targeting Aimeos\MShop\Common\Manager\Base::getSqlConfig() can also be of type array; however, Aimeos\MShop\Common\Manager\Base::addSqlColumns() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
522
			}
523
524
			$idx = 1;
525
			$stmt = $this->getCachedStatement( $conn, $path, $sql );
526
527
			foreach( $columns as $name => $entry ) {
528
				$stmt->bind( $idx++, $item->get( $name ), $entry->getInternalType() );
529
			}
530
531
			$stmt->bind( $idx++, $item->getLabel() );
532
			$stmt->bind( $idx++, $item->getCode() );
533
			$stmt->bind( $idx++, $billingAddress->getCompany() );
534
			$stmt->bind( $idx++, $billingAddress->getVatID() );
535
			$stmt->bind( $idx++, $billingAddress->getSalutation() );
536
			$stmt->bind( $idx++, $billingAddress->getTitle() );
537
			$stmt->bind( $idx++, $billingAddress->getFirstname() );
538
			$stmt->bind( $idx++, $billingAddress->getLastname() );
539
			$stmt->bind( $idx++, $billingAddress->getAddress1() );
540
			$stmt->bind( $idx++, $billingAddress->getAddress2() );
541
			$stmt->bind( $idx++, $billingAddress->getAddress3() );
542
			$stmt->bind( $idx++, $billingAddress->getPostal() );
543
			$stmt->bind( $idx++, $billingAddress->getCity() );
544
			$stmt->bind( $idx++, $billingAddress->getState() );
545
			$stmt->bind( $idx++, $billingAddress->getCountryId() );
546
			$stmt->bind( $idx++, $billingAddress->getLanguageId() );
547
			$stmt->bind( $idx++, $billingAddress->getTelephone() );
548
			$stmt->bind( $idx++, $billingAddress->getTelefax() );
549
			$stmt->bind( $idx++, $billingAddress->getWebsite() );
550
			$stmt->bind( $idx++, $billingAddress->getLongitude(), \Aimeos\MW\DB\Statement\Base::PARAM_FLOAT );
551
			$stmt->bind( $idx++, $billingAddress->getLatitude(), \Aimeos\MW\DB\Statement\Base::PARAM_FLOAT );
552
			$stmt->bind( $idx++, $billingAddress->getBirthday() );
553
			$stmt->bind( $idx++, $item->getStatus(), \Aimeos\MW\DB\Statement\Base::PARAM_INT );
554
			$stmt->bind( $idx++, $item->getDateVerified() );
555
			$stmt->bind( $idx++, $item->getPassword() );
556
			$stmt->bind( $idx++, $date ); // Modification time
557
			$stmt->bind( $idx++, $context->getEditor() );
558
			$stmt->bind( $idx++, $context->getLocale()->getSiteId() );
559
560
			if( $id !== null ) {
561
				$stmt->bind( $idx, $id, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
562
				$item->setId( $id );
563
			} else {
564
				$stmt->bind( $idx, $date ); // Creation time
565
			}
566
567
			$stmt->execute()->finish();
568
569
			if( $id === null && $fetch === true )
570
			{
571
				/** mshop/customer/manager/laravel/newid
572
				 * Retrieves the ID generated by the database when inserting a new record
573
				 *
574
				 * As soon as a new record is inserted into the database table,
575
				 * the database server generates a new and unique identifier for
576
				 * that record. This ID can be used for retrieving, updating and
577
				 * deleting that specific record from the table again.
578
				 *
579
				 * For MySQL:
580
				 *  SELECT LAST_INSERT_ID()
581
				 * For PostgreSQL:
582
				 *  SELECT currval('seq_mcus_id')
583
				 * For SQL Server:
584
				 *  SELECT SCOPE_IDENTITY()
585
				 * For Oracle:
586
				 *  SELECT "seq_mcus_id".CURRVAL FROM DUAL
587
				 *
588
				 * There's no way to retrive the new ID by a SQL statements that
589
				 * fits for most database servers as they implement their own
590
				 * specific way.
591
				 *
592
				 * @param string SQL statement for retrieving the last inserted record ID
593
				 * @since 2015.01
594
				 * @category Developer
595
				 * @see mshop/customer/manager/laravel/insert
596
				 * @see mshop/customer/manager/laravel/update
597
				 * @see mshop/customer/manager/laravel/delete
598
				 * @see mshop/customer/manager/laravel/search
599
				 * @see mshop/customer/manager/laravel/count
600
				 */
601
				$path = 'mshop/customer/manager/laravel/newid';
602
				$item->setId( $this->newId( $conn, $path ) );
603
			}
604
605
			$dbm->release( $conn, $dbname );
606
		}
607
		catch( \Exception $e )
608
		{
609
			$dbm->release( $conn, $dbname );
610
			throw $e;
611
		}
612
613
		$item = $this->savePropertyItems( $item, 'customer' );
614
		$item = $this->saveAddressItems( $item, 'customer' );
0 ignored issues
show
Documentation introduced by
$item is of type object<Aimeos\MShop\Comm...Item\PropertyRef\Iface>, but the function expects a object<Aimeos\MShop\Common\Item\AddressRef\Iface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
615
		return $this->saveListItems( $item, 'customer' );
0 ignored issues
show
Documentation introduced by
$item is of type object<Aimeos\MShop\Common\Item\AddressRef\Iface>, but the function expects a object<Aimeos\MShop\Common\Item\ListsRef\Iface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
616
	}
617
618
619
	/**
620
	 * Returns the item objects matched by the given search criteria.
621
	 *
622
	 * @param \Aimeos\MW\Criteria\Iface $search Search criteria object
623
	 * @param integer &$total Number of items that are available in total
624
	 * @return \Aimeos\Map List of items implementing \Aimeos\MShop\Customer\Item\Iface
625
	 * @throws \Aimeos\MShop\Customer\Exception If creating items failed
626
	 */
627
	public function search( \Aimeos\MW\Criteria\Iface $search, array $ref = [], int &$total = null ) : \Aimeos\Map
628
	{
629
		$dbm = $this->getContext()->getDatabaseManager();
630
		$dbname = $this->getResourceName();
631
		$conn = $dbm->acquire( $dbname );
632
		$map = [];
633
634
		try
635
		{
636
			$level = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
637
			$cfgPathSearch = 'mshop/customer/manager/laravel/search';
638
			$cfgPathCount = 'mshop/customer/manager/laravel/count';
639
			$ref[] = 'customer/group';
640
			$required = ['customer'];
641
642
			$results = $this->searchItemsBase( $conn, $search, $cfgPathSearch, $cfgPathCount, $required, $total, $level );
643
644
			while( ( $row = $results->fetch() ) !== null ) {
645
				$map[(string) $row['customer.id']] = $row;
646
			}
647
648
			$dbm->release( $conn, $dbname );
649
		}
650
		catch( \Exception $e )
651
		{
652
			$dbm->release( $conn, $dbname );
653
			throw $e;
654
		}
655
656
		$addrItems = [];
657
		if( in_array( 'customer/address', $ref, true ) ) {
658
			$addrItems = $this->getAddressItems( array_keys( $map ), 'customer' );
659
		}
660
661
		$propItems = []; $name = 'customer/property';
662
		if( isset( $ref[$name] ) || in_array( $name, $ref, true ) )
663
		{
664
			$propTypes = isset( $ref[$name] ) && is_array( $ref[$name] ) ? $ref[$name] : null;
665
			$propItems = $this->getPropertyItems( array_keys( $map ), 'customer', $propTypes );
0 ignored issues
show
Bug introduced by
It seems like $propTypes defined by isset($ref[$name]) && is...]) ? $ref[$name] : null on line 664 can also be of type string; however, Aimeos\MShop\Common\Mana...its::getPropertyItems() does only seem to accept null|array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
666
		}
667
668
		return $this->buildItems( $map, $ref, 'customer', $addrItems, $propItems );
669
	}
670
671
672
	/**
673
	 * Returns a new manager for customer extensions
674
	 *
675
	 * @param string $manager Name of the sub manager type in lower case
676
	 * @param string|null $name Name of the implementation, will be from configuration (or Default) if null
677
	 * @return mixed Manager for different extensions, e.g stock, tags, locations, etc.
678
	 */
679
	public function getSubManager( string $manager, string $name = null ) : \Aimeos\MShop\Common\Manager\Iface
680
	{
681
		return $this->getSubManagerBase( 'customer', $manager, ( $name === null ? 'Laravel' : $name ) );
682
	}
683
}
684