Passed
Push — master ( c52784...833dd8 )
by Aimeos
28:15 queued 16:05
created

FosUser   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 602
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 330
dl 0
loc 602
rs 9.68
c 0
b 0
f 0
wmc 34

8 Methods

Rating   Name   Duplication   Size   Complexity  
A delete() 0 4 1
B saveItem() 0 176 8
A createItemBase() 0 12 2
B __construct() 0 42 11
A getSubManager() 0 3 2
A clear() 0 10 2
A getSearchAttributes() 0 4 1
B search() 0 30 7
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 Friends of Symfony user bundle.
16
 *
17
 * @package MShop
18
 * @subpackage Customer
19
 */
20
class FosUser
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."username"',
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."username_canonical"',
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."enabled"',
212
			'type' => 'integer',
213
			'internaltype' => \Aimeos\Base\DB\Statement\Base::PARAM_BOOL,
214
		),
215
		'customer.dateverified'=> array(
216
			'label' => 'Customer verification date',
217
			'code' => 'customer.dateverified',
218
			'internalcode' => 'mcus."vdate"',
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."ctime"',
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."mtime"',
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 "fos_user_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 "fos_user_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\ContextIface $context Context object
268
	 */
269
	public function __construct( \Aimeos\MShop\ContextIface $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, ( $params[2] ?? null ) ? '==' : '=~' );
308
			$source = str_replace( [':site', ':key'], [$sitestr, $keystr], $source );
309
310
			return $params;
311
		};
312
	}
313
314
315
	/**
316
	 * Removes old entries from the storage.
317
	 *
318
	 * @param iterable $siteids List of IDs for sites whose entries should be deleted
319
	 * @return \Aimeos\MShop\Common\Manager\Iface Same object for fluent interface
320
	 */
321
	public function clear( iterable $siteids ) : \Aimeos\MShop\Common\Manager\Iface
322
	{
323
		$path = 'mshop/customer/manager/submanagers';
324
		$default = ['address', 'group', 'lists', 'property'];
325
326
		foreach( $this->context()->config()->get( $path, $default ) as $domain ) {
327
			$this->object()->getSubManager( $domain )->clear( $siteids );
328
		}
329
330
		return $this->clearBase( $siteids, 'mshop/customer/manager/fosuser/clear' );
331
	}
332
333
334
	/**
335
	 * Removes multiple items.
336
	 *
337
	 * @param \Aimeos\MShop\Common\Item\Iface[]|string[] $itemIds List of item objects or IDs of the items
338
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object for chaining method calls
339
	 */
340
	public function delete( $itemIds ) : \Aimeos\MShop\Common\Manager\Iface
341
	{
342
		$path = 'mshop/customer/manager/fosuser/delete';
343
		return $this->deleteItemsBase( $itemIds, $path )->deleteRefItems( $itemIds );
344
	}
345
346
347
	/**
348
	 * Returns the list attributes that can be used for searching.
349
	 *
350
	 * @param bool $withsub Return also attributes of sub-managers if true
351
	 * @return array List of attribute items implementing \Aimeos\Base\Criteria\Attribute\Iface
352
	 */
353
	public function getSearchAttributes( bool $withsub = true ) : array
354
	{
355
		$path = 'mshop/customer/manager/submanagers';
356
		return $this->getSearchAttributesBase( $this->searchConfig, $path, ['address'], $withsub );
357
	}
358
359
360
	/**
361
	 * Saves a customer item object.
362
	 *
363
	 * @param \Aimeos\MShop\Customer\Item\Iface $item Customer item object
364
	 * @param bool $fetch True if the new ID should be returned in the item
365
	 * @return \Aimeos\MShop\Customer\Item\Iface $item Updated item including the generated ID
366
	 */
367
	protected function saveItem( \Aimeos\MShop\Customer\Item\Iface $item, bool $fetch = true ) : \Aimeos\MShop\Customer\Item\Iface
368
	{
369
		$item = $this->addGroups( $item );
370
371
		if( !$item->isModified() )
372
		{
373
			$item = $this->savePropertyItems( $item, 'customer', $fetch );
374
			$item = $this->saveAddressItems( $item, 'customer', $fetch );
375
			return $this->saveListItems( $item, 'customer', $fetch );
376
		}
377
378
		$context = $this->context();
379
		$conn = $context->db( $this->getResourceName() );
380
381
		$id = $item->getId();
382
		$date = date( 'Y-m-d H:i:s' );
383
		$billingAddress = $item->getPaymentAddress();
384
		$columns = $this->object()->getSaveAttributes();
385
386
		if( $id === null )
387
		{
388
			/** mshop/customer/manager/fosuser/insert
389
			 * Inserts a new customer record into the database table
390
			 *
391
			 * Items with no ID yet (i.e. the ID is NULL) will be created in
392
			 * the database and the newly created ID retrieved afterwards
393
			 * using the "newid" SQL statement.
394
			 *
395
			 * The SQL statement must be a string suitable for being used as
396
			 * prepared statement. It must include question marks for binding
397
			 * the values from the customer item to the statement before they are
398
			 * sent to the database server. The number of question marks must
399
			 * be the same as the number of columns listed in the INSERT
400
			 * statement. The order of the columns must correspond to the
401
			 * order in the save() method, so the correct values are
402
			 * bound to the columns.
403
			 *
404
			 * The SQL statement should conform to the ANSI standard to be
405
			 * compatible with most relational database systems. This also
406
			 * includes using double quotes for table and column names.
407
			 *
408
			 * @param string SQL statement for inserting records
409
			 * @since 2015.01
410
			 * @category Developer
411
			 * @see mshop/customer/manager/fosuser/update
412
			 * @see mshop/customer/manager/fosuser/newid
413
			 * @see mshop/customer/manager/fosuser/delete
414
			 * @see mshop/customer/manager/fosuser/search
415
			 * @see mshop/customer/manager/fosuser/count
416
			 */
417
			$path = 'mshop/customer/manager/fosuser/insert';
418
			$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

418
			$sql = $this->addSqlColumns( array_keys( $columns ), /** @scrutinizer ignore-type */ $this->getSqlConfig( $path ) );
Loading history...
419
		}
420
		else
421
		{
422
			/** mshop/customer/manager/fosuser/update
423
			 * Updates an existing customer record in the database
424
			 *
425
			 * Items which already have an ID (i.e. the ID is not NULL) will
426
			 * be updated in the database.
427
			 *
428
			 * The SQL statement must be a string suitable for being used as
429
			 * prepared statement. It must include question marks for binding
430
			 * the values from the customer item to the statement before they are
431
			 * sent to the database server. The order of the columns must
432
			 * correspond to the order in the save() method, so the
433
			 * correct values are bound to the columns.
434
			 *
435
			 * The SQL statement should conform to the ANSI standard to be
436
			 * compatible with most relational database systems. This also
437
			 * includes using double quotes for table and column names.
438
			 *
439
			 * @param string SQL statement for updating records
440
			 * @since 2015.01
441
			 * @category Developer
442
			 * @see mshop/customer/manager/fosuser/insert
443
			 * @see mshop/customer/manager/fosuser/newid
444
			 * @see mshop/customer/manager/fosuser/delete
445
			 * @see mshop/customer/manager/fosuser/search
446
			 * @see mshop/customer/manager/fosuser/count
447
			 */
448
			$path = 'mshop/customer/manager/fosuser/update';
449
			$sql = $this->addSqlColumns( array_keys( $columns ), $this->getSqlConfig( $path ), false );
450
		}
451
452
		$idx = 1;
453
		$stmt = $this->getCachedStatement( $conn, $path, $sql );
454
455
		foreach( $columns as $name => $entry ) {
456
			$stmt->bind( $idx++, $item->get( $name ), $entry->getInternalType() );
0 ignored issues
show
Bug introduced by
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

456
			$stmt->bind( $idx++, $item->get( $name ), /** @scrutinizer ignore-type */ $entry->getInternalType() );
Loading history...
457
		}
458
459
		$stmt->bind( $idx++, $item->getCode() ); // canonical username
460
		$stmt->bind( $idx++, $item->getCode() ); // username
461
		$stmt->bind( $idx++, $billingAddress->getCompany() );
462
		$stmt->bind( $idx++, $billingAddress->getVatID() );
463
		$stmt->bind( $idx++, $billingAddress->getSalutation() );
464
		$stmt->bind( $idx++, $billingAddress->getTitle() );
465
		$stmt->bind( $idx++, $billingAddress->getFirstname() );
466
		$stmt->bind( $idx++, $billingAddress->getLastname() );
467
		$stmt->bind( $idx++, $billingAddress->getAddress1() );
468
		$stmt->bind( $idx++, $billingAddress->getAddress2() );
469
		$stmt->bind( $idx++, $billingAddress->getAddress3() );
470
		$stmt->bind( $idx++, $billingAddress->getPostal() );
471
		$stmt->bind( $idx++, $billingAddress->getCity() );
472
		$stmt->bind( $idx++, $billingAddress->getState() );
473
		$stmt->bind( $idx++, $billingAddress->getCountryId() );
474
		$stmt->bind( $idx++, $billingAddress->getLanguageId() );
475
		$stmt->bind( $idx++, $billingAddress->getTelephone() );
476
		$stmt->bind( $idx++, $billingAddress->getEmail() );
477
		$stmt->bind( $idx++, $billingAddress->getEmail() );
478
		$stmt->bind( $idx++, $billingAddress->getTelefax() );
479
		$stmt->bind( $idx++, $billingAddress->getWebsite() );
480
		$stmt->bind( $idx++, $billingAddress->getLongitude(), \Aimeos\Base\DB\Statement\Base::PARAM_FLOAT );
481
		$stmt->bind( $idx++, $billingAddress->getLatitude(), \Aimeos\Base\DB\Statement\Base::PARAM_FLOAT );
482
		$stmt->bind( $idx++, $billingAddress->getBirthday() );
483
		$stmt->bind( $idx++, ( $item->getStatus() > 0 ? true : false ), \Aimeos\Base\DB\Statement\Base::PARAM_BOOL );
484
		$stmt->bind( $idx++, $item->getDateVerified() );
485
		$stmt->bind( $idx++, $item->getPassword() );
486
		$stmt->bind( $idx++, $date ); // Modification time
487
		$stmt->bind( $idx++, $context->editor() );
488
		$stmt->bind( $idx++, serialize( $item->getRoles() ) );
489
		$stmt->bind( $idx++, $item->getSalt() );
490
491
		if( $id !== null ) {
492
			$stmt->bind( $idx++, $context->locale()->getSiteId() . '%' );
493
			$stmt->bind( $idx, $id, \Aimeos\Base\DB\Statement\Base::PARAM_INT );
494
			$billingAddress->setId( $id ); // enforce ID to be present
495
		} else {
496
			$stmt->bind( $idx++, $this->siteId( $item->getSiteId(), \Aimeos\MShop\Locale\Manager\Base::SITE_SUBTREE ) );
497
			$stmt->bind( $idx, $date ); // Creation time
498
		}
499
500
		$stmt->execute()->finish();
501
502
		if( $id === null && $fetch === true )
503
		{
504
			/** mshop/customer/manager/fosuser/newid
505
			 * Retrieves the ID generated by the database when inserting a new record
506
			 *
507
			 * As soon as a new record is inserted into the database table,
508
			 * the database server generates a new and unique identifier for
509
			 * that record. This ID can be used for retrieving, updating and
510
			 * deleting that specific record from the table again.
511
			 *
512
			 * For MySQL:
513
			 *  SELECT LAST_INSERT_ID()
514
			 * For PostgreSQL:
515
			 *  SELECT currval('seq_mcus_id')
516
			 * For SQL Server:
517
			 *  SELECT SCOPE_IDENTITY()
518
			 * For Oracle:
519
			 *  SELECT "seq_mcus_id".CURRVAL FROM DUAL
520
			 *
521
			 * There's no way to retrive the new ID by a SQL statements that
522
			 * fits for most database servers as they implement their own
523
			 * specific way.
524
			 *
525
			 * @param string SQL statement for retrieving the last inserted record ID
526
			 * @since 2015.01
527
			 * @category Developer
528
			 * @see mshop/customer/manager/fosuser/insert
529
			 * @see mshop/customer/manager/fosuser/update
530
			 * @see mshop/customer/manager/fosuser/delete
531
			 * @see mshop/customer/manager/fosuser/search
532
			 * @see mshop/customer/manager/fosuser/count
533
			 */
534
			$path = 'mshop/customer/manager/fosuser/newid';
535
			$id = $this->newId( $conn, $path );
536
		}
537
538
		$item->setId( $id );
539
540
		$item = $this->savePropertyItems( $item, 'customer' );
541
		$item = $this->saveAddressItems( $item, 'customer' );
542
		return $this->saveListItems( $item, 'customer' );
543
	}
544
545
546
	/**
547
	 * Returns the item objects matched by the given search criteria.
548
	 *
549
	 * @param \Aimeos\Base\Criteria\Iface $search Search criteria object
550
	 * @param integer &$total Number of items that are available in total
551
	 * @return \Aimeos\Map List of items implementing \Aimeos\MShop\Customer\Item\Iface
552
	 * @throws \Aimeos\MShop\Customer\Exception If creating items failed
553
	 */
554
	public function search( \Aimeos\Base\Criteria\Iface $search, array $ref = [], int &$total = null ) : \Aimeos\Map
555
	{
556
		$map = [];
557
		$conn = $this->context()->db( $this->getResourceName() );
558
559
		$level = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
560
		$cfgPathSearch = 'mshop/customer/manager/fosuser/search';
561
		$cfgPathCount = 'mshop/customer/manager/fosuser/count';
562
		$ref[] = 'customer/group';
563
		$required = ['customer'];
564
565
		$results = $this->searchItemsBase( $conn, $search, $cfgPathSearch, $cfgPathCount, $required, $total, $level );
566
567
		while( ( $row = $results->fetch() ) !== null ) {
568
			$map[(string) $row['customer.id']] = $row;
569
		}
570
571
		$addrItems = [];
572
		if( in_array( 'customer/address', $ref, true ) ) {
573
			$addrItems = $this->getAddressItems( array_keys( $map ), 'customer' );
574
		}
575
576
		$propItems = []; $name = 'customer/property';
577
		if( isset( $ref[$name] ) || in_array( $name, $ref, true ) )
578
		{
579
			$propTypes = isset( $ref[$name] ) && is_array( $ref[$name] ) ? $ref[$name] : null;
580
			$propItems = $this->getPropertyItems( array_keys( $map ), 'customer', $propTypes );
581
		}
582
583
		return $this->buildItems( $map, $ref, 'customer', $addrItems, $propItems );
584
	}
585
586
587
	/**
588
	 * Returns a new manager for customer extensions
589
	 *
590
	 * @param string $manager Name of the sub manager type in lower case
591
	 * @param string|null $name Name of the implementation, will be from configuration (or Default) if null
592
	 * @return mixed Manager for different extensions, e.g stock, tags, locations, etc.
593
	 */
594
	public function getSubManager( string $manager, string $name = null ) : \Aimeos\MShop\Common\Manager\Iface
595
	{
596
		return $this->getSubManagerBase( 'customer', $manager, ( $name === null ? 'FosUser' : $name ) );
597
	}
598
599
600
	/**
601
	 * Creates a new customer item.
602
	 *
603
	 * @param array $values List of attributes for customer item
604
	 * @param \Aimeos\MShop\Common\Lists\Item\Iface[] $listItems List of list items
605
	 * @param \Aimeos\MShop\Common\Item\Iface[] $refItems List of referenced items
606
	 * @param \Aimeos\MShop\Common\Item\Address\Iface[] $addrItems List of referenced address items
607
	 * @param \Aimeos\MShop\Common\Item\Property\Iface[] $propItems List of property items
608
	 * @return \Aimeos\MShop\Customer\Item\Iface New customer item
609
	 */
610
	protected function createItemBase( array $values = [], array $listItems = [], array $refItems = [],
611
		array $addrItems = [], array $propItems = [] ) : \Aimeos\MShop\Common\Item\Iface
612
	{
613
		if( isset( $values['roles'] ) ) {
614
			$values['roles'] = unserialize( $values['roles'] );
615
		}
616
617
		$helper = $this->getPasswordHelper();
618
		$address = new \Aimeos\MShop\Common\Item\Address\Standard( 'customer.', $values );
619
620
		return new \Aimeos\MShop\Customer\Item\FosUser(
621
			$address, $values, $listItems, $refItems, $addrItems, $propItems, $helper
622
		);
623
	}
624
}
625