Passed
Push — master ( aa55d0...1b93d4 )
by Aimeos
16:27 queued 13:18
created

Typo3   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 770
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 358
dl 0
loc 770
rs 8.8
c 1
b 0
f 0
wmc 45

12 Methods

Rating   Name   Duplication   Size   Complexity  
A clear() 0 10 2
A delete() 0 4 1
A create() 0 4 1
A getSearchAttributes() 0 4 1
A aggregate() 0 54 1
B __construct() 0 71 10
A getSubManager() 0 3 2
B search() 0 32 7
A getPasswordHelper() 0 7 2
B saveItem() 0 176 9
B transform() 0 27 8
A createItemBase() 0 10 1

How to fix   Complexity   

Complex Class

Complex classes like Typo3 often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Typo3, and based on these observations, apply Extract Interface, too.

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

531
				$sql = $this->addSqlColumns( array_keys( $columns ), /** @scrutinizer ignore-type */ $this->getSqlConfig( $path ) );
Loading history...
532
			}
533
			else
534
			{
535
				/** mshop/customer/manager/typo3/update
536
				 * Updates an existing customer record in the database
537
				 *
538
				 * Items which already have an ID (i.e. the ID is not NULL) will
539
				 * be updated in the database.
540
				 *
541
				 * The SQL statement must be a string suitable for being used as
542
				 * prepared statement. It must include question marks for binding
543
				 * the values from the customer item to the statement before they are
544
				 * sent to the database server. The order of the columns must
545
				 * correspond to the order in the save() method, so the
546
				 * correct values are bound to the columns.
547
				 *
548
				 * The SQL statement should conform to the ANSI standard to be
549
				 * compatible with most relational database systems. This also
550
				 * includes using double quotes for table and column names.
551
				 *
552
				 * @param string SQL statement for updating records
553
				 * @since 2014.03
554
				 * @category Developer
555
				 * @see mshop/customer/manager/typo3/insert
556
				 * @see mshop/customer/manager/typo3/newid
557
				 * @see mshop/customer/manager/typo3/delete
558
				 * @see mshop/customer/manager/typo3/search
559
				 * @see mshop/customer/manager/typo3/count
560
				 */
561
				$path = 'mshop/customer/manager/typo3/update';
562
				$sql = $this->addSqlColumns( array_keys( $columns ), $this->getSqlConfig( $path ), false );
563
			}
564
565
			$address = $billingAddress->getAddress1();
566
567
			if( ( $part = $billingAddress->getAddress2() ) != '' ) {
568
				$address .= ' ' . $part;
569
			}
570
571
			if( ( $part = $billingAddress->getAddress3() ) != '' ) {
572
				$address .= ' ' . $part;
573
			}
574
575
			$idx = 1;
576
			$stmt = $this->getCachedStatement( $conn, $path, $sql );
577
578
			foreach( $columns as $name => $entry ) {
579
				$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

579
				$stmt->bind( $idx++, $item->get( $name ), /** @scrutinizer ignore-type */ $entry->getInternalType() );
Loading history...
580
			}
581
582
			// TYPO3 fe_users.static_info_country is a three letter ISO code instead a two letter one
583
			$stmt->bind( $idx++, $item->getLabel() );
584
			$stmt->bind( $idx++, $item->getCode() );
585
			$stmt->bind( $idx++, $this->plugins['customer.salutation']->translate( $billingAddress->getSalutation() ), \Aimeos\Base\DB\Statement\Base::PARAM_INT );
586
			$stmt->bind( $idx++, $billingAddress->getCompany() );
587
			$stmt->bind( $idx++, $billingAddress->getVatID() );
588
			$stmt->bind( $idx++, $billingAddress->getTitle() );
589
			$stmt->bind( $idx++, $billingAddress->getFirstname() );
590
			$stmt->bind( $idx++, $billingAddress->getLastname() );
591
			$stmt->bind( $idx++, $address );
592
			$stmt->bind( $idx++, $billingAddress->getPostal() );
593
			$stmt->bind( $idx++, $billingAddress->getCity() );
594
			$stmt->bind( $idx++, $billingAddress->getState() );
595
			$stmt->bind( $idx++, $billingAddress->getLanguageId() );
596
			$stmt->bind( $idx++, $billingAddress->getTelephone() );
597
			$stmt->bind( $idx++, $billingAddress->getEmail() );
598
			$stmt->bind( $idx++, $billingAddress->getTelefax() );
599
			$stmt->bind( $idx++, $billingAddress->getWebsite() );
600
			$stmt->bind( $idx++, $billingAddress->getLongitude(), \Aimeos\Base\DB\Statement\Base::PARAM_FLOAT );
601
			$stmt->bind( $idx++, $billingAddress->getLatitude(), \Aimeos\Base\DB\Statement\Base::PARAM_FLOAT );
602
			$stmt->bind( $idx++, $this->plugins['customer.birthday']->translate( $billingAddress->getBirthday() ), \Aimeos\Base\DB\Statement\Base::PARAM_INT );
603
			$stmt->bind( $idx++, $this->plugins['customer.status']->translate( $item->getStatus() ), \Aimeos\Base\DB\Statement\Base::PARAM_INT );
604
			$stmt->bind( $idx++, $item->getPassword() );
605
			$stmt->bind( $idx++, time(), \Aimeos\Base\DB\Statement\Base::PARAM_INT ); // Modification time
606
			$stmt->bind( $idx++, $billingAddress->getCountryId() );
607
			$stmt->bind( $idx++, implode( ',', $item->getGroups() ) );
608
			$stmt->bind( $idx++, $this->pid, \Aimeos\Base\DB\Statement\Base::PARAM_INT ); // TYPO3 PID value
609
			$stmt->bind( $idx++, $context->locale()->getSiteId() );
610
611
			if( $id !== null ) {
612
				$stmt->bind( $idx, $id, \Aimeos\Base\DB\Statement\Base::PARAM_INT );
613
				$item->setId( $id );
614
			} else {
615
				$stmt->bind( $idx, time(), \Aimeos\Base\DB\Statement\Base::PARAM_INT ); // Creation time
616
			}
617
618
			$stmt->execute()->finish();
619
620
			if( $id === null && $fetch === true )
621
			{
622
				/** mshop/customer/manager/typo3/newid
623
				 * Retrieves the ID generated by the database when inserting a new record
624
				 *
625
				 * As soon as a new record is inserted into the database table,
626
				 * the database server generates a new and unique identifier for
627
				 * that record. This ID can be used for retrieving, updating and
628
				 * deleting that specific record from the table again.
629
				 *
630
				 * For MySQL:
631
				 *  SELECT LAST_INSERT_ID()
632
				 * For PostgreSQL:
633
				 *  SELECT currval('seq_mcus_id')
634
				 * For SQL Server:
635
				 *  SELECT SCOPE_IDENTITY()
636
				 * For Oracle:
637
				 *  SELECT "seq_mcus_id".CURRVAL FROM DUAL
638
				 *
639
				 * There's no way to retrive the new ID by a SQL statements that
640
				 * fits for most database servers as they implement their own
641
				 * specific way.
642
				 *
643
				 * @param string SQL statement for retrieving the last inserted record ID
644
				 * @since 2014.03
645
				 * @category Developer
646
				 * @see mshop/customer/manager/typo3/insert
647
				 * @see mshop/customer/manager/typo3/update
648
				 * @see mshop/customer/manager/typo3/delete
649
				 * @see mshop/customer/manager/typo3/search
650
				 * @see mshop/customer/manager/typo3/count
651
				 */
652
				$path = 'mshop/customer/manager/typo3/newid';
653
				$item->setId( $this->newId( $conn, $path ) );
654
			}
655
656
		$item = $this->savePropertyItems( $item, 'customer' );
657
		$item = $this->saveAddressItems( $item, 'customer' );
658
		return $this->saveListItems( $item, 'customer' );
659
	}
660
661
662
	/**
663
	 * Returns the item objects matched by the given search criteria.
664
	 *
665
	 * @param \Aimeos\Base\Criteria\Iface $search Search criteria object
666
	 * @param int|null &$total Number of items that are available in total
667
	 * @return \Aimeos\Map List of items implementing \Aimeos\MShop\Customer\Item\Iface
668
	 * @throws \Aimeos\MShop\Customer\Exception If creating items failed
669
	 */
670
	public function search( \Aimeos\Base\Criteria\Iface $search, array $ref = [], int &$total = null ) : \Aimeos\Map
671
	{
672
		$conn = $this->context()->db( $this->getResourceName() );
673
		$map = [];
674
675
		$level = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
676
		$level = $this->context()->config()->get( 'mshop/customer/manager/sitemode', $level );
677
678
		$cfgPathSearch = 'mshop/customer/manager/typo3/search';
679
		$cfgPathCount = 'mshop/customer/manager/typo3/count';
680
		$required = array( 'customer' );
681
682
		$results = $this->searchItemsBase( $conn, $search, $cfgPathSearch, $cfgPathCount, $required, $total, $level, $this->plugins );
683
684
		while( ( $row = $results->fetch() ) !== null ) {
685
			$map[(string) $row['customer.id']] = $row;
686
		}
687
688
689
		$addrItems = [];
690
		if( in_array( 'customer/address', $ref, true ) ) {
691
			$addrItems = $this->getAddressItems( array_keys( $map ), 'customer' );
692
		}
693
694
		$propItems = []; $name = 'customer/property';
695
		if( isset( $ref[$name] ) || in_array( $name, $ref, true ) )
696
		{
697
			$propTypes = isset( $ref[$name] ) && is_array( $ref[$name] ) ? $ref[$name] : null;
698
			$propItems = $this->getPropertyItems( array_keys( $map ), 'customer', $propTypes );
699
		}
700
701
		return $this->buildItems( $map, $ref, 'customer', $addrItems, $propItems );
702
	}
703
704
705
	/**
706
	 * Returns a new manager for customer extensions
707
	 *
708
	 * @param string $manager Name of the sub manager type in lower case
709
	 * @param string|null $name Name of the implementation, will be from configuration (or Default) if null
710
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager for different extensions, e.g stock, tags, locations, etc.
711
	 */
712
	public function getSubManager( string $manager, string $name = null ) : \Aimeos\MShop\Common\Manager\Iface
713
	{
714
		return $this->getSubManagerBase( 'customer', $manager, ( $name === null ? 'Typo3' : $name ) );
715
	}
716
717
718
	/**
719
	 * Creates a new customer item.
720
	 *
721
	 * @param array $values List of attributes for customer item
722
	 * @param \Aimeos\MShop\Common\Lists\Item\Iface[] $listItems List of list items
723
	 * @param \Aimeos\MShop\Common\Item\Iface[] $refItems List of referenced items
724
	 * @param \Aimeos\MShop\Common\Item\Address\Iface[] $addrItems List of referenced address items
725
	 * @param \Aimeos\MShop\Common\Item\Property\Iface[] $propItems List of property items
726
	 * @return \Aimeos\MShop\Customer\Item\Iface New customer item
727
	 */
728
	protected function createItemBase( array $values = [], array $listItems = [], array $refItems = [],
729
		array $addrItems = [], array $propItems = [] ) : \Aimeos\MShop\Common\Item\Iface
730
	{
731
		$helper = $this->getPasswordHelper();
732
		$values = $this->transform( $values );
733
734
		$address = new \Aimeos\MShop\Common\Item\Address\Simple( 'customer.', $values );
735
736
		return new \Aimeos\MShop\Customer\Item\Standard(
737
			$address, $values, $listItems, $refItems, $addrItems, $propItems, $helper
738
		);
739
	}
740
741
742
	/**
743
	 * Returns a password helper object based on the configuration.
744
	 *
745
	 * @return \Aimeos\MShop\Common\Helper\Password\Iface Password helper object
746
	 * @throws \Aimeos\MShop\Exception If the name is invalid or the class isn't found
747
	 */
748
	protected function getPasswordHelper() : \Aimeos\MShop\Common\Helper\Password\Iface
749
	{
750
		if( $this->helper === null ) {
751
			$this->helper = new \Aimeos\MShop\Common\Helper\Password\Typo3( ['object' => $this->context()->password()] );
752
		}
753
754
		return $this->helper;
755
	}
756
757
758
	/**
759
	 * Transforms the application specific values to Aimeos standard values.
760
	 *
761
	 * @param array $values Associative list of key/value pairs from the storage
762
	 * @return array Associative list of key/value pairs with standard Aimeos values
763
	 */
764
	protected function transform( array $values ) : array
765
	{
766
		if( array_key_exists( 'customer.birthday', $values ) ) {
767
			$values['customer.birthday'] = $this->plugins['customer.birthday']->reverse( $values['customer.birthday'] );
768
		}
769
770
		if( array_key_exists( 'customer.salutation', $values ) ) {
771
			$values['customer.salutation'] = $this->plugins['customer.salutation']->reverse( $values['customer.salutation'] );
772
		}
773
774
		if( array_key_exists( 'customer.status', $values ) ) {
775
			$values['customer.status'] = $this->plugins['customer.status']->reverse( $values['customer.status'] );
776
		}
777
778
		if( array_key_exists( 'customer.mtime', $values ) ) {
779
			$values['customer.mtime'] = $this->plugins['customer.mtime']->reverse( $values['customer.mtime'] );
780
		}
781
782
		if( array_key_exists( 'customer.ctime', $values ) ) {
783
			$values['customer.ctime'] = $this->plugins['customer.ctime']->reverse( $values['customer.ctime'] );
784
		}
785
786
		if( array_key_exists( 'customer.groups', $values ) && $values['customer.groups'] !== '' ) {
787
			$values['customer.groups'] = explode( ',', $values['customer.groups'] );
788
		}
789
790
		return $values;
791
	}
792
}
793