Passed
Push — master ( 1d7a51...2f8c90 )
by Aimeos
06:29 queued 03:55
created

src/MShop/Customer/Manager/Laravel.php (1 issue)

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

529
			$stmt->bind( $idx++, $item->get( $name ), /** @scrutinizer ignore-type */ $entry->getInternalType() );
Loading history...
530
		}
531
532
		$stmt->bind( $idx++, $item->getLabel() );
533
		$stmt->bind( $idx++, $item->getCode() );
534
		$stmt->bind( $idx++, $billingAddress->getCompany() );
535
		$stmt->bind( $idx++, $billingAddress->getVatID() );
536
		$stmt->bind( $idx++, $billingAddress->getSalutation() );
537
		$stmt->bind( $idx++, $billingAddress->getTitle() );
538
		$stmt->bind( $idx++, $billingAddress->getFirstname() );
539
		$stmt->bind( $idx++, $billingAddress->getLastname() );
540
		$stmt->bind( $idx++, $billingAddress->getAddress1() );
541
		$stmt->bind( $idx++, $billingAddress->getAddress2() );
542
		$stmt->bind( $idx++, $billingAddress->getAddress3() );
543
		$stmt->bind( $idx++, $billingAddress->getPostal() );
544
		$stmt->bind( $idx++, $billingAddress->getCity() );
545
		$stmt->bind( $idx++, $billingAddress->getState() );
546
		$stmt->bind( $idx++, $billingAddress->getCountryId() );
547
		$stmt->bind( $idx++, $billingAddress->getLanguageId() );
548
		$stmt->bind( $idx++, $billingAddress->getTelephone() );
549
		$stmt->bind( $idx++, $billingAddress->getTelefax() );
550
		$stmt->bind( $idx++, $billingAddress->getMobile() );
551
		$stmt->bind( $idx++, $billingAddress->getWebsite() );
552
		$stmt->bind( $idx++, $billingAddress->getLongitude(), \Aimeos\Base\DB\Statement\Base::PARAM_FLOAT );
553
		$stmt->bind( $idx++, $billingAddress->getLatitude(), \Aimeos\Base\DB\Statement\Base::PARAM_FLOAT );
554
		$stmt->bind( $idx++, $billingAddress->getBirthday() );
555
		$stmt->bind( $idx++, $item->getStatus(), \Aimeos\Base\DB\Statement\Base::PARAM_INT );
556
		$stmt->bind( $idx++, $item->getDateVerified() );
557
		$stmt->bind( $idx++, $item->getPassword() );
558
		$stmt->bind( $idx++, $date ); // Modification time
559
		$stmt->bind( $idx++, $context->editor() );
560
561
		if( $id !== null ) {
562
			$stmt->bind( $idx++, $context->locale()->getSiteId() . '%' );
563
			$stmt->bind( $idx, $id, \Aimeos\Base\DB\Statement\Base::PARAM_INT );
564
			$item->setId( $id );
565
		} else {
566
			$stmt->bind( $idx++, $this->siteId( $item->getSiteId(), \Aimeos\MShop\Locale\Manager\Base::SITE_SUBTREE ) );
567
			$stmt->bind( $idx, $date ); // Creation time
568
		}
569
570
		$stmt->execute()->finish();
571
572
		if( $id === null && $fetch === true )
573
		{
574
			/** mshop/customer/manager/laravel/newid
575
			 * Retrieves the ID generated by the database when inserting a new record
576
			 *
577
			 * As soon as a new record is inserted into the database table,
578
			 * the database server generates a new and unique identifier for
579
			 * that record. This ID can be used for retrieving, updating and
580
			 * deleting that specific record from the table again.
581
			 *
582
			 * For MySQL:
583
			 *  SELECT LAST_INSERT_ID()
584
			 * For PostgreSQL:
585
			 *  SELECT currval('seq_mcus_id')
586
			 * For SQL Server:
587
			 *  SELECT SCOPE_IDENTITY()
588
			 * For Oracle:
589
			 *  SELECT "seq_mcus_id".CURRVAL FROM DUAL
590
			 *
591
			 * There's no way to retrive the new ID by a SQL statements that
592
			 * fits for most database servers as they implement their own
593
			 * specific way.
594
			 *
595
			 * @param string SQL statement for retrieving the last inserted record ID
596
			 * @since 2015.01
597
			 * @category Developer
598
			 * @see mshop/customer/manager/laravel/insert
599
			 * @see mshop/customer/manager/laravel/update
600
			 * @see mshop/customer/manager/laravel/delete
601
			 * @see mshop/customer/manager/laravel/search
602
			 * @see mshop/customer/manager/laravel/count
603
			 */
604
			$path = 'mshop/customer/manager/laravel/newid';
605
			$item->setId( $this->newId( $conn, $path ) );
606
		}
607
608
		$item = $this->savePropertyItems( $item, 'customer' );
609
		$item = $this->saveAddressItems( $item, 'customer' );
610
		return $this->saveListItems( $item, 'customer' );
611
	}
612
613
614
	/**
615
	 * Returns the item objects matched by the given search criteria.
616
	 *
617
	 * @param \Aimeos\Base\Criteria\Iface $search Search criteria object
618
	 * @param integer &$total Number of items that are available in total
619
	 * @return \Aimeos\Map List of items implementing \Aimeos\MShop\Customer\Item\Iface
620
	 * @throws \Aimeos\MShop\Customer\Exception If creating items failed
621
	 */
622
	public function search( \Aimeos\Base\Criteria\Iface $search, array $ref = [], int &$total = null ) : \Aimeos\Map
623
	{
624
		$conn = $this->context()->db( $this->getResourceName() );
625
		$map = [];
626
627
		$level = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
628
		$level = $this->context()->config()->get( 'mshop/customer/manager/sitemode', $level );
629
630
		$cfgPathSearch = 'mshop/customer/manager/laravel/search';
631
		$cfgPathCount = 'mshop/customer/manager/laravel/count';
632
		$ref[] = 'customer/group';
633
		$required = ['customer'];
634
635
		$results = $this->searchItemsBase( $conn, $search, $cfgPathSearch, $cfgPathCount, $required, $total, $level );
636
637
		while( ( $row = $results->fetch() ) !== null ) {
638
			$map[(string) $row['customer.id']] = $row;
639
		}
640
641
		$addrItems = [];
642
		if( in_array( 'customer/address', $ref, true ) ) {
643
			$addrItems = $this->getAddressItems( array_keys( $map ), 'customer' );
644
		}
645
646
		$propItems = []; $name = 'customer/property';
647
		if( isset( $ref[$name] ) || in_array( $name, $ref, true ) )
648
		{
649
			$propTypes = isset( $ref[$name] ) && is_array( $ref[$name] ) ? $ref[$name] : null;
650
			$propItems = $this->getPropertyItems( array_keys( $map ), 'customer', $propTypes );
651
		}
652
653
		return $this->buildItems( $map, $ref, 'customer', $addrItems, $propItems );
654
	}
655
656
657
	/**
658
	 * Returns a new manager for customer extensions
659
	 *
660
	 * @param string $manager Name of the sub manager type in lower case
661
	 * @param string|null $name Name of the implementation, will be from configuration (or Default) if null
662
	 * @return mixed Manager for different extensions, e.g stock, tags, locations, etc.
663
	 */
664
	public function getSubManager( string $manager, string $name = null ) : \Aimeos\MShop\Common\Manager\Iface
665
	{
666
		return $this->getSubManagerBase( 'customer', $manager, $name ?: 'Laravel' );
667
	}
668
}
669