Passed
Push — master ( 49f6b0...199364 )
by Aimeos
09:07
created

Typo3   B

Complexity

Total Complexity 51

Size/Duplication

Total Lines 802
Duplicated Lines 0 %

Importance

Changes 32
Bugs 0 Features 0
Metric Value
wmc 51
eloc 377
c 32
b 0
f 0
dl 0
loc 802
rs 7.92

12 Methods

Rating   Name   Duplication   Size   Complexity  
A getSubManager() 0 3 2
B search() 0 41 8
A getPasswordHelper() 0 18 4
C __construct() 0 71 12
F saveItem() 0 188 10
A clear() 0 10 2
B transform() 0 27 8
A delete() 0 4 1
A createItemBase() 0 10 1
A create() 0 4 1
A getSearchAttributes() 0 4 1
A aggregate() 0 54 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-2021
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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\MW\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\Context\Item\Iface $context Context object with required objects
276
	 */
277
	public function __construct( \Aimeos\MShop\Context\Item\Iface $context )
278
	{
279
		parent::__construct( $context );
280
281
		$plugin = new \Aimeos\MW\Criteria\Plugin\T3Salutation();
282
		$this->plugins['customer.salutation'] = $plugin;
283
284
		$plugin = new \Aimeos\MW\Criteria\Plugin\T3Status();
285
		$this->plugins['customer.status'] = $plugin;
286
287
		$plugin = new \Aimeos\MW\Criteria\Plugin\T3Date();
288
		$this->plugins['customer.birthday'] = $plugin;
289
290
		$plugin = new \Aimeos\MW\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->getConfig()->get( 'mshop/customer/manager/typo3/pid-default', 0 );
308
309
310
		$level = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
311
		$level = $context->getConfig()->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->getSiteString( '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 $id ) {
339
					$keys[] = $params[0] . '|' . ( $lang === null ? 'null|' : ( $lang ? $lang . '|' : '' ) ) . ( $id != '' ? md5( $id ) : '' );
340
				}
341
			}
342
343
			$sitestr = $this->getSiteString( 'mcuspr."siteid"', $level );
344
			$keystr = $this->toExpression( 'mcuspr."key"', $keys, ( $params[2] ?? null ) ? '==' : '=~' );
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\MW\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\MW\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->getContext()->getConfig()->get( $path, $default ) as $domain ) {
430
			$this->getObject()->getSubManager( $domain )->clear( $siteids );
431
		}
432
433
		return $this->clearBase( $siteids, 'mshop/customer/manager/typo3/delete' );
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->getContext()->getLocale()->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\MW\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->getContext();
493
		$dbm = $context->getDatabaseManager();
494
		$dbname = $this->getResourceName();
495
		$conn = $dbm->acquire( $dbname );
496
497
		try
498
		{
499
			$id = $item->getId();
500
			$billingAddress = $item->getPaymentAddress();
501
			$columns = $this->getObject()->getSaveAttributes();
502
503
			if( $id === null )
504
			{
505
				/** mshop/customer/manager/typo3/insert
506
				 * Inserts a new customer record into the database table
507
				 *
508
				 * Items with no ID yet (i.e. the ID is NULL) will be created in
509
				 * the database and the newly created ID retrieved afterwards
510
				 * using the "newid" SQL statement.
511
				 *
512
				 * The SQL statement must be a string suitable for being used as
513
				 * prepared statement. It must include question marks for binding
514
				 * the values from the customer item to the statement before they are
515
				 * sent to the database server. The number of question marks must
516
				 * be the same as the number of columns listed in the INSERT
517
				 * statement. The order of the columns must correspond to the
518
				 * order in the save() method, so the correct values are
519
				 * bound to the columns.
520
				 *
521
				 * The SQL statement should conform to the ANSI standard to be
522
				 * compatible with most relational database systems. This also
523
				 * includes using double quotes for table and column names.
524
				 *
525
				 * @param string SQL statement for inserting records
526
				 * @since 2014.03
527
				 * @category Developer
528
				 * @see mshop/customer/manager/typo3/update
529
				 * @see mshop/customer/manager/typo3/newid
530
				 * @see mshop/customer/manager/typo3/delete
531
				 * @see mshop/customer/manager/typo3/search
532
				 * @see mshop/customer/manager/typo3/count
533
				 */
534
				$path = 'mshop/customer/manager/typo3/insert';
535
				$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

535
				$sql = $this->addSqlColumns( array_keys( $columns ), /** @scrutinizer ignore-type */ $this->getSqlConfig( $path ) );
Loading history...
536
			}
537
			else
538
			{
539
				/** mshop/customer/manager/typo3/update
540
				 * Updates an existing customer record in the database
541
				 *
542
				 * Items which already have an ID (i.e. the ID is not NULL) will
543
				 * be updated in the database.
544
				 *
545
				 * The SQL statement must be a string suitable for being used as
546
				 * prepared statement. It must include question marks for binding
547
				 * the values from the customer item to the statement before they are
548
				 * sent to the database server. The order of the columns must
549
				 * correspond to the order in the save() method, so the
550
				 * correct values are bound to the columns.
551
				 *
552
				 * The SQL statement should conform to the ANSI standard to be
553
				 * compatible with most relational database systems. This also
554
				 * includes using double quotes for table and column names.
555
				 *
556
				 * @param string SQL statement for updating records
557
				 * @since 2014.03
558
				 * @category Developer
559
				 * @see mshop/customer/manager/typo3/insert
560
				 * @see mshop/customer/manager/typo3/newid
561
				 * @see mshop/customer/manager/typo3/delete
562
				 * @see mshop/customer/manager/typo3/search
563
				 * @see mshop/customer/manager/typo3/count
564
				 */
565
				$path = 'mshop/customer/manager/typo3/update';
566
				$sql = $this->addSqlColumns( array_keys( $columns ), $this->getSqlConfig( $path ), false );
567
			}
568
569
			$address = $billingAddress->getAddress1();
570
571
			if( ( $part = $billingAddress->getAddress2() ) != '' ) {
572
				$address .= ' ' . $part;
573
			}
574
575
			if( ( $part = $billingAddress->getAddress3() ) != '' ) {
576
				$address .= ' ' . $part;
577
			}
578
579
			$idx = 1;
580
			$stmt = $this->getCachedStatement( $conn, $path, $sql );
581
582
			foreach( $columns as $name => $entry ) {
583
				$stmt->bind( $idx++, $item->get( $name ), $entry->getInternalType() );
584
			}
585
586
			// TYPO3 fe_users.static_info_country is a three letter ISO code instead a two letter one
587
			$stmt->bind( $idx++, $item->getLabel() );
588
			$stmt->bind( $idx++, $item->getCode() );
589
			$stmt->bind( $idx++, $this->plugins['customer.salutation']->translate( $billingAddress->getSalutation() ), \Aimeos\MW\DB\Statement\Base::PARAM_INT );
590
			$stmt->bind( $idx++, $billingAddress->getCompany() );
591
			$stmt->bind( $idx++, $billingAddress->getVatID() );
592
			$stmt->bind( $idx++, $billingAddress->getTitle() );
593
			$stmt->bind( $idx++, $billingAddress->getFirstname() );
594
			$stmt->bind( $idx++, $billingAddress->getLastname() );
595
			$stmt->bind( $idx++, $address );
596
			$stmt->bind( $idx++, $billingAddress->getPostal() );
597
			$stmt->bind( $idx++, $billingAddress->getCity() );
598
			$stmt->bind( $idx++, $billingAddress->getState() );
599
			$stmt->bind( $idx++, $billingAddress->getLanguageId() );
600
			$stmt->bind( $idx++, $billingAddress->getTelephone() );
601
			$stmt->bind( $idx++, $billingAddress->getEmail() );
602
			$stmt->bind( $idx++, $billingAddress->getTelefax() );
603
			$stmt->bind( $idx++, $billingAddress->getWebsite() );
604
			$stmt->bind( $idx++, $billingAddress->getLongitude(), \Aimeos\MW\DB\Statement\Base::PARAM_FLOAT );
605
			$stmt->bind( $idx++, $billingAddress->getLatitude(), \Aimeos\MW\DB\Statement\Base::PARAM_FLOAT );
606
			$stmt->bind( $idx++, $this->plugins['customer.birthday']->translate( $billingAddress->getBirthday() ), \Aimeos\MW\DB\Statement\Base::PARAM_INT );
607
			$stmt->bind( $idx++, $this->plugins['customer.status']->translate( $item->getStatus() ), \Aimeos\MW\DB\Statement\Base::PARAM_INT );
608
			$stmt->bind( $idx++, $item->getPassword() );
609
			$stmt->bind( $idx++, time(), \Aimeos\MW\DB\Statement\Base::PARAM_INT ); // Modification time
610
			$stmt->bind( $idx++, $billingAddress->getCountryId() );
611
			$stmt->bind( $idx++, implode( ',', $item->getGroups() ) );
612
			$stmt->bind( $idx++, $this->pid, \Aimeos\MW\DB\Statement\Base::PARAM_INT ); // TYPO3 PID value
613
			$stmt->bind( $idx++, $context->getLocale()->getSiteId() );
614
615
			if( $id !== null ) {
616
				$stmt->bind( $idx, $id, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
617
				$item->setId( $id );
618
			} else {
619
				$stmt->bind( $idx, time(), \Aimeos\MW\DB\Statement\Base::PARAM_INT ); // Creation time
620
			}
621
622
			$stmt->execute()->finish();
623
624
			if( $id === null && $fetch === true )
625
			{
626
				/** mshop/customer/manager/typo3/newid
627
				 * Retrieves the ID generated by the database when inserting a new record
628
				 *
629
				 * As soon as a new record is inserted into the database table,
630
				 * the database server generates a new and unique identifier for
631
				 * that record. This ID can be used for retrieving, updating and
632
				 * deleting that specific record from the table again.
633
				 *
634
				 * For MySQL:
635
				 *  SELECT LAST_INSERT_ID()
636
				 * For PostgreSQL:
637
				 *  SELECT currval('seq_mcus_id')
638
				 * For SQL Server:
639
				 *  SELECT SCOPE_IDENTITY()
640
				 * For Oracle:
641
				 *  SELECT "seq_mcus_id".CURRVAL FROM DUAL
642
				 *
643
				 * There's no way to retrive the new ID by a SQL statements that
644
				 * fits for most database servers as they implement their own
645
				 * specific way.
646
				 *
647
				 * @param string SQL statement for retrieving the last inserted record ID
648
				 * @since 2014.03
649
				 * @category Developer
650
				 * @see mshop/customer/manager/typo3/insert
651
				 * @see mshop/customer/manager/typo3/update
652
				 * @see mshop/customer/manager/typo3/delete
653
				 * @see mshop/customer/manager/typo3/search
654
				 * @see mshop/customer/manager/typo3/count
655
				 */
656
				$path = 'mshop/customer/manager/typo3/newid';
657
				$item->setId( $this->newId( $conn, $path ) );
658
			}
659
660
			$dbm->release( $conn, $dbname );
661
		}
662
		catch( \Exception $e )
663
		{
664
			$dbm->release( $conn, $dbname );
665
			throw $e;
666
		}
667
668
		$item = $this->savePropertyItems( $item, 'customer' );
669
		$item = $this->saveAddressItems( $item, 'customer' );
670
		return $this->saveListItems( $item, 'customer' );
671
	}
672
673
674
	/**
675
	 * Returns the item objects matched by the given search criteria.
676
	 *
677
	 * @param \Aimeos\MW\Criteria\Iface $search Search criteria object
678
	 * @param int|null &$total Number of items that are available in total
679
	 * @return \Aimeos\Map List of items implementing \Aimeos\MShop\Customer\Item\Iface
680
	 * @throws \Aimeos\MShop\Customer\Exception If creating items failed
681
	 */
682
	public function search( \Aimeos\MW\Criteria\Iface $search, array $ref = [], int &$total = null ) : \Aimeos\Map
683
	{
684
		$dbm = $this->getContext()->getDatabaseManager();
685
		$dbname = $this->getResourceName();
686
		$conn = $dbm->acquire( $dbname );
687
		$map = [];
688
689
		try
690
		{
691
			$level = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
692
			$cfgPathSearch = 'mshop/customer/manager/typo3/search';
693
			$cfgPathCount = 'mshop/customer/manager/typo3/count';
694
			$required = array( 'customer' );
695
696
			$results = $this->searchItemsBase( $conn, $search, $cfgPathSearch, $cfgPathCount, $required, $total, $level, $this->plugins );
697
698
			while( ( $row = $results->fetch() ) !== null ) {
699
				$map[(string) $row['customer.id']] = $row;
700
			}
701
702
			$dbm->release( $conn, $dbname );
703
		}
704
		catch( \Exception $e )
705
		{
706
			$dbm->release( $conn, $dbname );
707
			throw $e;
708
		}
709
710
		$addrItems = [];
711
		if( in_array( 'customer/address', $ref, true ) ) {
712
			$addrItems = $this->getAddressItems( array_keys( $map ), 'customer' );
713
		}
714
715
		$propItems = []; $name = 'customer/property';
716
		if( isset( $ref[$name] ) || in_array( $name, $ref, true ) )
717
		{
718
			$propTypes = isset( $ref[$name] ) && is_array( $ref[$name] ) ? $ref[$name] : null;
719
			$propItems = $this->getPropertyItems( array_keys( $map ), 'customer', $propTypes );
720
		}
721
722
		return $this->buildItems( $map, $ref, 'customer', $addrItems, $propItems );
723
	}
724
725
726
	/**
727
	 * Returns a new manager for customer extensions
728
	 *
729
	 * @param string $manager Name of the sub manager type in lower case
730
	 * @param string|null $name Name of the implementation, will be from configuration (or Default) if null
731
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager for different extensions, e.g stock, tags, locations, etc.
732
	 */
733
	public function getSubManager( string $manager, string $name = null ) : \Aimeos\MShop\Common\Manager\Iface
734
	{
735
		return $this->getSubManagerBase( 'customer', $manager, ( $name === null ? 'Typo3' : $name ) );
736
	}
737
738
739
	/**
740
	 * Creates a new customer item.
741
	 *
742
	 * @param array $values List of attributes for customer item
743
	 * @param \Aimeos\MShop\Common\Lists\Item\Iface[] $listItems List of list items
744
	 * @param \Aimeos\MShop\Common\Item\Iface[] $refItems List of referenced items
745
	 * @param \Aimeos\MShop\Common\Item\Address\Iface[] $addrItems List of referenced address items
746
	 * @param \Aimeos\MShop\Common\Item\Property\Iface[] $propItems List of property items
747
	 * @return \Aimeos\MShop\Customer\Item\Iface New customer item
748
	 */
749
	protected function createItemBase( array $values = [], array $listItems = [], array $refItems = [],
750
		array $addrItems = [], array $propItems = [] ) : \Aimeos\MShop\Common\Item\Iface
751
	{
752
		$helper = $this->getPasswordHelper();
753
		$values = $this->transform( $values );
754
755
		$address = new \Aimeos\MShop\Common\Item\Address\Simple( 'customer.', $values );
756
757
		return new \Aimeos\MShop\Customer\Item\Standard(
758
			$address, $values, $listItems, $refItems, $addrItems, $propItems, $helper
759
		);
760
	}
761
762
763
	/**
764
	 * Returns a password helper object based on the configuration.
765
	 *
766
	 * @return \Aimeos\MShop\Common\Helper\Password\Iface Password helper object
767
	 * @throws \Aimeos\MShop\Exception If the name is invalid or the class isn't found
768
	 */
769
	protected function getPasswordHelper() : \Aimeos\MShop\Common\Helper\Password\Iface
770
	{
771
		if( $this->helper ) {
772
			return $this->helper;
773
		}
774
775
		$classname = \Aimeos\MShop\Common\Helper\Password\Typo3::class;
776
777
		if( class_exists( $classname ) === false ) {
778
			throw new \Aimeos\MShop\Exception( sprintf( 'Class "%1$s" not available', $classname ) );
779
		}
780
781
		$context = $this->getContext();
782
		$object = ( method_exists( $context, 'getHasherTypo3' ) ? $context->getHasherTypo3() : null );
783
784
		$helper = new $classname( array( 'object' => $object ) );
785
786
		return $this->helper = self::checkClass( \Aimeos\MShop\Common\Helper\Password\Iface::class, $helper );
787
	}
788
789
790
	/**
791
	 * Transforms the application specific values to Aimeos standard values.
792
	 *
793
	 * @param array $values Associative list of key/value pairs from the storage
794
	 * @return array Associative list of key/value pairs with standard Aimeos values
795
	 */
796
	protected function transform( array $values ) : array
797
	{
798
		if( array_key_exists( 'customer.birthday', $values ) ) {
799
			$values['customer.birthday'] = $this->plugins['customer.birthday']->reverse( $values['customer.birthday'] );
800
		}
801
802
		if( array_key_exists( 'customer.salutation', $values ) ) {
803
			$values['customer.salutation'] = $this->plugins['customer.salutation']->reverse( $values['customer.salutation'] );
804
		}
805
806
		if( array_key_exists( 'customer.status', $values ) ) {
807
			$values['customer.status'] = $this->plugins['customer.status']->reverse( $values['customer.status'] );
808
		}
809
810
		if( array_key_exists( 'customer.mtime', $values ) ) {
811
			$values['customer.mtime'] = $this->plugins['customer.mtime']->reverse( $values['customer.mtime'] );
812
		}
813
814
		if( array_key_exists( 'customer.ctime', $values ) ) {
815
			$values['customer.ctime'] = $this->plugins['customer.ctime']->reverse( $values['customer.ctime'] );
816
		}
817
818
		if( array_key_exists( 'customer.groups', $values ) && $values['customer.groups'] !== '' ) {
819
			$values['customer.groups'] = explode( ',', $values['customer.groups'] );
820
		}
821
822
		return $values;
823
	}
824
}
825