Passed
Push — master ( e9e003...b12354 )
by Aimeos
19:22 queued 10:06
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-2022
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 $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.email' => array(
160
			'label' => 'Customer email',
161
			'code' => 'customer.email',
162
			'internalcode' => 'mcus."email"',
163
			'type' => 'string',
164
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
165
		),
166
		'customer.telefax' => array(
167
			'label' => 'Customer telefax',
168
			'code' => 'customer.telefax',
169
			'internalcode' => 'mcus."telefax"',
170
			'type' => 'string',
171
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
172
		),
173
		'customer.website' => array(
174
			'label' => 'Customer website',
175
			'code' => 'customer.website',
176
			'internalcode' => 'mcus."website"',
177
			'type' => 'string',
178
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
179
		),
180
		'customer.longitude' => array(
181
			'label' => 'Customer longitude',
182
			'code' => 'customer.longitude',
183
			'internalcode' => 'mcus."longitude"',
184
			'type' => 'float',
185
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_FLOAT,
186
		),
187
		'customer.latitude' => array(
188
			'label' => 'Customer latitude',
189
			'code' => 'customer.latitude',
190
			'internalcode' => 'mcus."latitude"',
191
			'type' => 'float',
192
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_FLOAT,
193
		),
194
		'customer.birthday' => array(
195
			'label' => 'Customer birthday',
196
			'code' => 'customer.birthday',
197
			'internalcode' => 'mcus."birthday"',
198
			'type' => 'string',
199
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
200
		),
201
		'customer.password' => array(
202
			'label' => 'Customer password',
203
			'code' => 'customer.password',
204
			'internalcode' => 'mcus."password"',
205
			'type' => 'string',
206
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
207
		),
208
		'customer.status' => array(
209
			'label' => 'Customer status',
210
			'code' => 'customer.status',
211
			'internalcode' => 'mcus."status"',
212
			'type' => 'integer',
213
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_INT
214
		),
215
		'customer.dateverified' => array(
216
			'label' => 'Customer verification date',
217
			'code' => 'customer.dateverified',
218
			'internalcode' => 'mcus."email_verified_at"',
219
			'type' => 'date',
220
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
221
		),
222
		'customer.ctime' => array(
223
			'label' => 'Customer creation time',
224
			'code' => 'customer.ctime',
225
			'internalcode' => 'mcus."created_at"',
226
			'type' => 'datetime',
227
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
228
		),
229
		'customer.mtime' => array(
230
			'label' => 'Customer modification time',
231
			'code' => 'customer.mtime',
232
			'internalcode' => 'mcus."updated_at"',
233
			'type' => 'datetime',
234
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
235
		),
236
		'customer.editor' => array(
237
			'label' =>'Customer editor',
238
			'code' =>'customer.editor',
239
			'internalcode' => 'mcus."editor"',
240
			'type' => 'string',
241
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_STR,
242
		),
243
		'customer:has' => array(
244
			'code' => 'customer:has()',
245
			'internalcode' => ':site AND :key AND mcusli."id"',
246
			'internaldeps' => ['LEFT JOIN "users_list" AS mcusli ON ( mcusli."parentid" = mcus."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 mcuspr."id"',
255
			'internaldeps' => ['LEFT JOIN "users_property" AS mcuspr ON ( mcuspr."parentid" = mcus."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->config()->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->siteString( 'mcusli."siteid"', $level );
288
			$keystr = $this->toExpression( 'mcusli."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 $val ) {
302
					$keys[] = substr( $params[0] . '|' . ( $lang === null ? 'null|' : ( $lang ? $lang . '|' : '' ) ) . $val, 0, 255 );
303
				}
304
			}
305
306
			$sitestr = $this->siteString( 'mcuspr."siteid"', $level );
307
			$keystr = $this->toExpression( 'mcuspr."key"', $keys, '=~' );
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->context()->config()->get( $path, $default ) as $domain ) {
393
			$this->object()->getSubManager( $domain )->clear( $siteids );
394
		}
395
396
		return $this->clearBase( $siteids, 'mshop/customer/manager/laravel/clear' );
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' );
443
			return $this->saveListItems( $item, 'customer' );
444
		}
445
446
		$context = $this->context();
447
		$dbm = $context->db();
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->object()->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 ) );
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 );
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() );
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

528
				$stmt->bind( $idx++, $item->get( $name ), /** @scrutinizer ignore-type */ $entry->getInternalType() );
Loading history...
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\Base\DB\Statement\Base::PARAM_FLOAT );
551
			$stmt->bind( $idx++, $billingAddress->getLatitude(), \Aimeos\Base\DB\Statement\Base::PARAM_FLOAT );
552
			$stmt->bind( $idx++, $billingAddress->getBirthday() );
553
			$stmt->bind( $idx++, $item->getStatus(), \Aimeos\Base\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->editor() );
558
			$stmt->bind( $idx++, $context->locale()->getSiteId() );
559
560
			if( $id !== null ) {
561
				$stmt->bind( $idx, $id, \Aimeos\Base\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' );
615
		return $this->saveListItems( $item, 'customer' );
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->context()->db();
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 );
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 ?: 'Laravel' );
682
	}
683
}
684