Passed
Push — master ( 29d5f6...586595 )
by Aimeos
04:48
created

src/MShop/Locale/Manager/Site/Standard.php (1 issue)

Labels
Severity
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), 2015-2018
7
 * @package MShop
8
 * @subpackage Locale
9
 */
10
11
12
namespace Aimeos\MShop\Locale\Manager\Site;
13
14
15
/**
16
 * Default implementation for managing sites.
17
 *
18
 * @package MShop
19
 * @subpackage Locale
20
 */
21
class Standard
22
	extends \Aimeos\MShop\Common\Manager\Base
23
	implements \Aimeos\MShop\Locale\Manager\Site\Iface, \Aimeos\MShop\Common\Manager\Factory\Iface
24
{
25
	private $cache = [];
26
27
	private $searchConfig = array(
28
		'locale.site.id' => array(
29
			'code' => 'locale.site.id',
30
			'internalcode' => 'mlocsi."id"',
31
			'internaldeps' => array( 'LEFT JOIN "mshop_locale_site" AS mlocsi ON (mloc."siteid" = mlocsi."id")' ),
32
			'label' => 'Site ID',
33
			'type' => 'string',
34
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
35
			'public' => false,
36
		),
37
		'locale.site.siteid' => array(
38
			'code' => 'locale.site.siteid',
39
			'internalcode' => 'mlocsi."id"',
40
			'label' => 'Site ID',
41
			'type' => 'string',
42
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
43
			'public' => false,
44
		),
45
		'locale.site.label' => array(
46
			'code' => 'locale.site.label',
47
			'internalcode' => 'mlocsi."label"',
48
			'label' => 'Site label',
49
			'type' => 'string',
50
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
51
		),
52
		'locale.site.code' => array(
53
			'code' => 'locale.site.code',
54
			'internalcode' => 'mlocsi."code"',
55
			'label' => 'Site code',
56
			'type' => 'string',
57
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
58
		),
59
		'locale.site.status' => array(
60
			'code' => 'locale.site.status',
61
			'internalcode' => 'mlocsi."status"',
62
			'label' => 'Site status',
63
			'type' => 'integer',
64
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
65
		),
66
		'locale.site.level' => array(
67
			'code' => 'locale.site.level',
68
			'internalcode' => 'mlocsi."level"',
69
			'label' => 'Site tree level',
70
			'type' => 'integer',
71
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_INT,
72
			'public' => false,
73
		),
74
		'locale.site.config' => array(
75
			'code' => 'locale.site.config',
76
			'internalcode' => 'mlocsi."config"',
77
			'label' => 'Site config',
78
			'type' => 'string',
79
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
80
			'public' => false,
81
		),
82
		'locale.site.ctime' => array(
83
			'code' => 'locale.site.ctime',
84
			'internalcode' => 'mlocsi."ctime"',
85
			'label' => 'Site create date/time',
86
			'type' => 'datetime',
87
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
88
			'public' => false,
89
		),
90
		'locale.site.mtime' => array(
91
			'code' => 'locale.site.mtime',
92
			'internalcode' => 'mlocsi."mtime"',
93
			'label' => 'Site modify date/time',
94
			'type' => 'datetime',
95
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
96
			'public' => false,
97
		),
98
		'locale.site.editor' => array(
99
			'code' => 'locale.site.editor',
100
			'internalcode' => 'mlocsi."editor"',
101
			'label' => 'Site editor',
102
			'type' => 'string',
103
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
104
			'public' => false,
105
		),
106
	);
107
108
109
	/**
110
	 * Initializes the object.
111
	 *
112
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context object
113
	 */
114
	public function __construct( \Aimeos\MShop\Context\Item\Iface $context )
115
	{
116
		parent::__construct( $context );
117
		$this->setResourceName( 'db-locale' );
118
	}
119
120
121
	/**
122
	 * Removes old entries from the storage.
123
	 *
124
	 * @param integer[] $siteids List of IDs for sites whose entries should be deleted
125
	 */
126
	public function cleanup( array $siteids )
127
	{
128
		$context = $this->getContext();
129
		$config = $context->getConfig();
130
131
		/** mshop/locale/manager/site/cleanup/shop/domains
132
		 * List of madmin domains names whose items referring to the same site should be deleted as well
133
		 *
134
		 * As items for each domain can be stored in a separate database, the
135
		 * site manager needs a list of domain names used to connect to the
136
		 * correct database and to remove all items that belong the the deleted
137
		 * site.
138
		 *
139
		 * For each domain the cleanup will be done by the corresponding MShop
140
		 * manager. To keep records for old sites in the database even if the
141
		 * site was already deleted, you can configure a new list with the
142
		 * domains removed you would like to keep, e.g. the "order" domain to
143
		 * keep all orders ever placed.
144
		 *
145
		 * @param array List of domain names in lower case
146
		 * @since 2014.03
147
		 * @category Developer
148
		 * @see mshop/locale/manager/site/cleanup/admin/domains
149
		 */
150
		$path = 'mshop/locale/manager/site/cleanup/shop/domains';
151
		$default = array(
152
			'attribute', 'catalog', 'coupon', 'customer', 'index',
153
			'media', 'order', 'plugin', 'price', 'product', 'tag',
154
			'service', 'stock', 'subscription', 'supplier', 'text'
155
		);
156
157
		foreach( $config->get( $path, $default ) as $domain ) {
158
			\Aimeos\MShop\Factory::createManager( $context, $domain )->cleanup( $siteids );
159
		}
160
161
		/** mshop/locale/manager/site/cleanup/admin/domains
162
		 * List of mshop domains names whose items referring to the same site should be deleted as well
163
		 *
164
		 * As items for each domain can be stored in a separate database, the
165
		 * site manager needs a list of domain names used to connect to the
166
		 * correct database and to remove all items that belong the the deleted
167
		 * site.
168
		 *
169
		 * For each domain the cleanup will be done by the corresponding MAdmin
170
		 * manager. To keep records for old sites in the database even if the
171
		 * site was already deleted, you can configure a new list with the
172
		 * domains removed you would like to keep, e.g. the "log" domain to
173
		 * keep all log entries ever written.
174
		 *
175
		 * @param array List of domain names in lower case
176
		 * @since 2014.03
177
		 * @category Developer
178
		 * @see mshop/locale/manager/site/cleanup/shop/domains
179
		 */
180
		$path = 'mshop/locale/manager/site/cleanup/admin/domains';
181
		$default = array( 'job', 'log', 'cache' );
182
183
		foreach( $config->get( $path, $default ) as $domain ) {
184
			\Aimeos\MAdmin\Factory::createManager( $context, $domain )->cleanup( $siteids );
185
		}
186
	}
187
188
189
	/**
190
	 * Creates a new empty item instance
191
	 *
192
	 * @param string|null Type the item should be created with
193
	 * @param string|null Domain of the type the item should be created with
194
	 * @param array $values Values the item should be initialized with
195
	 * @return \Aimeos\MShop\Locale\Item\Site\Iface New locale site item object
196
	 */
197
	public function createItem( $type = null, $domain = null, array $values = [] )
198
	{
199
		return $this->createItemBase( $values );
200
	}
201
202
203
	/**
204
	 * Adds a new site to the storage or updates an existing one.
205
	 *
206
	 * @param \Aimeos\MShop\Common\Item\Iface $item New site item for saving to the storage
207
	 * @param boolean $fetch True if the new ID should be returned in the item
208
	 * @return \Aimeos\MShop\Common\Item\Iface $item Updated item including the generated ID
209
	 * @throws \Aimeos\MShop\Locale\Exception
210
	 */
211
	public function saveItem( \Aimeos\MShop\Common\Item\Iface $item, $fetch = true )
212
	{
213
		self::checkClass( '\\Aimeos\\MShop\\Locale\\Item\\Site\\Iface', $item );
214
215
		if( $item->getId() === null ) {
216
			throw new \Aimeos\MShop\Locale\Exception( sprintf( 'Newly created site can not be saved using method "saveItem()". Try using method "insertItem()" instead.' ) );
217
		}
218
219
		if( !$item->isModified() ) {
220
			return $item;
221
		}
222
223
		$context = $this->getContext();
224
225
		$dbm = $context->getDatabaseManager();
226
		$dbname = $this->getResourceName();
227
		$conn = $dbm->acquire( $dbname );
228
229
		try
230
		{
231
			$id = $item->getId();
232
233
			/** mshop/locale/manager/site/standard/update/mysql
234
			 * Updates an existing site record in the database
235
			 *
236
			 * @see mshop/locale/manager/site/standard/update/ansi
237
			 */
238
239
			/** mshop/locale/manager/site/standard/update/ansi
240
			 * Updates an existing site record in the database
241
			 *
242
			 * The SQL statement must be a string suitable for being used as
243
			 * prepared statement. It must include question marks for binding
244
			 * the values from the site item to the statement before they are
245
			 * sent to the database server. The order of the columns must
246
			 * correspond to the order in the saveItems() method, so the
247
			 * correct values are bound to the columns.
248
			 *
249
			 * The SQL statement should conform to the ANSI standard to be
250
			 * compatible with most relational database systems. This also
251
			 * includes using double quotes for table and column names.
252
			 *
253
			 * @param string SQL statement for updating records
254
			 * @since 2014.03
255
			 * @category Developer
256
			 * @see mshop/locale/manager/site/standard/insert/ansi
257
			 * @see mshop/locale/manager/site/standard/delete/ansi
258
			 * @see mshop/locale/manager/site/standard/search/ansi
259
			 * @see mshop/locale/manager/site/standard/count/ansi
260
			 * @see mshop/locale/manager/site/standard/newid/ansi
261
			 */
262
			$path = 'mshop/locale/manager/site/standard/update';
263
264
			$stmt = $this->getCachedStatement( $conn, $path );
265
266
			$stmt->bind( 1, $item->getCode() );
267
			$stmt->bind( 2, $item->getLabel() );
1 ignored issue
show
The method getLabel() does not exist on Aimeos\MShop\Common\Item\Iface. It seems like you code against a sub-type of Aimeos\MShop\Common\Item\Iface such as Aimeos\MShop\Product\Item\Iface or Aimeos\MShop\Service\Item\Iface or Aimeos\MShop\Locale\Item\Site\Iface or Aimeos\MShop\Customer\Item\Iface or Aimeos\MShop\Text\Item\Iface or Aimeos\MShop\Customer\Item\Group\Iface or Aimeos\MShop\Media\Item\Iface or Aimeos\MShop\Coupon\Item\Iface or Aimeos\MAdmin\Job\Item\Iface or Aimeos\MShop\Tag\Item\Iface or Aimeos\MShop\Common\Item\Type\Iface or Aimeos\MShop\Attribute\Item\Iface or Aimeos\MShop\Locale\Item\Language\Iface or Aimeos\MShop\Catalog\Item\Iface or Aimeos\MShop\Plugin\Item\Iface or Aimeos\MShop\Supplier\Item\Iface or Aimeos\MShop\Locale\Item\Currency\Iface or Aimeos\MShop\Attribute\Item\Standard or Aimeos\MShop\Catalog\Item\Standard or Aimeos\MShop\Customer\Item\Base or Aimeos\MShop\Plugin\Item\Standard or Aimeos\MShop\Tag\Item\Standard or Aimeos\MShop\Customer\Item\Group\Standard or Aimeos\MShop\Media\Item\Standard or Aimeos\MAdmin\Job\Item\Standard or Aimeos\MShop\Common\Item\Type\Standard or Aimeos\MShop\Locale\Item\Site\Standard or Aimeos\MShop\Locale\Item\Currency\Standard or Aimeos\MShop\Text\Item\Standard or Aimeos\MShop\Locale\Item\Language\Standard or Aimeos\MShop\Service\Item\Standard or Aimeos\MShop\Common\Item\ListRef\Base or Aimeos\MShop\Price\Item\Base or Aimeos\MShop\Supplier\Item\Standard or Aimeos\MShop\Coupon\Item\Standard or Aimeos\MShop\Product\Item\Standard or Aimeos\MShop\Product\Item\Iface or Aimeos\MShop\Service\Item\Iface or Aimeos\MShop\Customer\Item\Iface or Aimeos\MShop\Text\Item\Iface or Aimeos\MShop\Media\Item\Iface or Aimeos\MShop\Coupon\Item\Iface or Aimeos\MAdmin\Job\Item\Iface or Aimeos\MShop\Common\Item\Type\Iface or Aimeos\MShop\Attribute\Item\Iface or Aimeos\MShop\Locale\Item\Language\Iface or Aimeos\MShop\Common\Item\Tree\Iface or Aimeos\MShop\Plugin\Item\Iface or Aimeos\MShop\Supplier\Item\Iface or Aimeos\MShop\Locale\Item\Currency\Iface or Aimeos\MShop\Price\Item\Base or Aimeos\MShop\Price\Item\Base. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

267
			$stmt->bind( 2, $item->/** @scrutinizer ignore-call */ getLabel() );
Loading history...
268
			$stmt->bind( 3, json_encode( $item->getConfig() ) );
269
			$stmt->bind( 4, $item->getStatus(), \Aimeos\MW\DB\Statement\Base::PARAM_INT );
270
			$stmt->bind( 5, $context->getEditor() );
271
			$stmt->bind( 6, date( 'Y-m-d H:i:s' ) ); // mtime
272
			$stmt->bind( 7, $id, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
273
274
			$stmt->execute()->finish();
275
			$item->setId( $id ); // set Modified false
276
277
			$dbm->release( $conn, $dbname );
278
		}
279
		catch( \Exception $e )
280
		{
281
			$dbm->release( $conn, $dbname );
282
			throw $e;
283
		}
284
285
		return $item;
286
	}
287
288
289
	/**
290
	 * Removes multiple items specified by ids in the array.
291
	 *
292
	 * @param array $ids List of IDs
293
	 */
294
	public function deleteItems( array $ids )
295
	{
296
		$context = $this->getContext();
297
		$config = $context->getConfig();
298
299
		$this->getObject()->cleanup( $ids );
300
301
		/** mshop/locale/manager/site/standard/delete/mysql
302
		 * Deletes the items matched by the given IDs from the database
303
		 *
304
		 * @see mshop/locale/manager/site/standard/delete/ansi
305
		 */
306
307
		/** mshop/locale/manager/site/standard/delete/ansi
308
		 * Deletes the items matched by the given IDs from the database
309
		 *
310
		 * Removes the site records specified by the given IDs from the
311
		 * locale database. The records must be from the site that is configured
312
		 * via the context item.
313
		 *
314
		 * The ":cond" placeholder is replaced by the name of the ID column and
315
		 * the given ID or list of IDs while the site ID is bound to the question
316
		 * mark.
317
		 *
318
		 * The SQL statement should conform to the ANSI standard to be
319
		 * compatible with most relational database systems. This also
320
		 * includes using double quotes for table and column names.
321
		 *
322
		 * @param string SQL statement for deleting items
323
		 * @since 2014.03
324
		 * @category Developer
325
		 * @see mshop/locale/manager/site/standard/insert/ansi
326
		 * @see mshop/locale/manager/site/standard/update/ansi
327
		 * @see mshop/locale/manager/site/standard/search/ansi
328
		 * @see mshop/locale/manager/site/standard/count/ansi
329
		 * @see mshop/locale/manager/site/standard/newid/ansi
330
		 */
331
		$path = 'mshop/locale/manager/site/standard/delete';
332
		$this->deleteItemsBase( $ids, $path, false );
333
	}
334
335
336
	/**
337
	 * Returns the item specified by its code and domain/type if necessary
338
	 *
339
	 * @param string $code Code of the item
340
	 * @param string[] $ref List of domains to fetch list items and referenced items for
341
	 * @param string|null $domain Domain of the item if necessary to identify the item uniquely
342
	 * @param string|null $type Type code of the item if necessary to identify the item uniquely
343
	 * @param boolean $default True to add default criteria
344
	 * @return \Aimeos\MShop\Common\Item\Iface Item object
345
	 */
346
	public function findItem( $code, array $ref = [], $domain = null, $type = null, $default = false )
347
	{
348
		return $this->findItemBase( array( 'locale.site.code' => $code ), $ref, $default );
349
	}
350
351
352
	/**
353
	 * Returns the site item specified by its ID.
354
	 *
355
	 * @param string $id Unique ID of the site data in the storage
356
	 * @param string[] $ref List of domains to fetch list items and referenced items for
357
	 * @param boolean $default Add default criteria
358
	 * @return \Aimeos\MShop\Locale\Item\Site\Iface Returns the site item of the given id
359
	 * @throws \Aimeos\MShop\Exception If the item couldn't be found
360
	 */
361
	public function getItem( $id, array $ref = [], $default = false )
362
	{
363
		return $this->getItemBase( 'locale.site.id', $id, $ref, $default );
364
	}
365
366
367
	/**
368
	 * Returns the available manager types
369
	 *
370
	 * @param boolean $withsub Return also the resource type of sub-managers if true
371
	 * @return array Type of the manager and submanagers, subtypes are separated by slashes
372
	 */
373
	public function getResourceType( $withsub = true )
374
	{
375
		$path = 'mshop/locale/manager/site/submanagers';
376
377
		return $this->getResourceTypeBase( 'locale/site', $path, [], $withsub );
378
	}
379
380
381
	/**
382
	 * Returns the attributes that can be used for searching.
383
	 *
384
	 * @param boolean $withsub Return also attributes of sub-managers if true
385
	 * @return array List of attribute items implementing \Aimeos\MW\Criteria\Attribute\Iface
386
	 */
387
	public function getSearchAttributes( $withsub = true )
388
	{
389
		/** mshop/locale/manager/site/submanagers
390
		 * List of manager names that can be instantiated by the locale site manager
391
		 *
392
		 * Managers provide a generic interface to the underlying storage.
393
		 * Each manager has or can have sub-managers caring about particular
394
		 * aspects. Each of these sub-managers can be instantiated by its
395
		 * parent manager using the getSubManager() method.
396
		 *
397
		 * The search keys from sub-managers can be normally used in the
398
		 * manager as well. It allows you to search for items of the manager
399
		 * using the search keys of the sub-managers to further limit the
400
		 * retrieved list of items.
401
		 *
402
		 * @param array List of sub-manager names
403
		 * @since 2014.03
404
		 * @category Developer
405
		 */
406
		$path = 'mshop/locale/manager/site/submanagers';
407
408
		return $this->getSearchAttributesBase( $this->searchConfig, $path, [], $withsub );
409
	}
410
411
412
	/**
413
	 * Returns a new sub manager of the given type and name.
414
	 *
415
	 * @param string $manager Name of the sub manager type in lower case
416
	 * @param string|null $name Name of the implementation, will be from configuration (or Default) if null
417
	 * @return \Aimeos\MShop\Locale\Manager\Iface manager
418
	 */
419
	public function getSubManager( $manager, $name = null )
420
	{
421
		/** mshop/locale/manager/site/name
422
		 * Class name of the used locale site manager implementation
423
		 *
424
		 * Each default locale site manager can be replaced by an alternative imlementation.
425
		 * To use this implementation, you have to set the last part of the class
426
		 * name as configuration value so the manager factory knows which class it
427
		 * has to instantiate.
428
		 *
429
		 * For example, if the name of the default class is
430
		 *
431
		 *  \Aimeos\MShop\Locale\Manager\Site\Standard
432
		 *
433
		 * and you want to replace it with your own version named
434
		 *
435
		 *  \Aimeos\MShop\Locale\Manager\Site\Mysite
436
		 *
437
		 * then you have to set the this configuration option:
438
		 *
439
		 *  mshop/locale/manager/site/name = Mysite
440
		 *
441
		 * The value is the last part of your own class name and it's case sensitive,
442
		 * so take care that the configuration value is exactly named like the last
443
		 * part of the class name.
444
		 *
445
		 * The allowed characters of the class name are A-Z, a-z and 0-9. No other
446
		 * characters are possible! You should always start the last part of the class
447
		 * name with an upper case character and continue only with lower case characters
448
		 * or numbers. Avoid chamel case names like "MySite"!
449
		 *
450
		 * @param string Last part of the class name
451
		 * @since 2014.03
452
		 * @category Developer
453
		 */
454
455
		/** mshop/locale/manager/site/decorators/excludes
456
		 * Excludes decorators added by the "common" option from the locale site manager
457
		 *
458
		 * Decorators extend the functionality of a class by adding new aspects
459
		 * (e.g. log what is currently done), executing the methods of the underlying
460
		 * class only in certain conditions (e.g. only for logged in users) or
461
		 * modify what is returned to the caller.
462
		 *
463
		 * This option allows you to remove a decorator added via
464
		 * "mshop/common/manager/decorators/default" before they are wrapped
465
		 * around the locale site manager.
466
		 *
467
		 *  mshop/locale/manager/site/decorators/excludes = array( 'decorator1' )
468
		 *
469
		 * This would remove the decorator named "decorator1" from the list of
470
		 * common decorators ("\Aimeos\MShop\Common\Manager\Decorator\*") added via
471
		 * "mshop/common/manager/decorators/default" for the locale site manager.
472
		 *
473
		 * @param array List of decorator names
474
		 * @since 2014.03
475
		 * @category Developer
476
		 * @see mshop/common/manager/decorators/default
477
		 * @see mshop/locale/manager/site/decorators/global
478
		 * @see mshop/locale/manager/site/decorators/local
479
		 */
480
481
		/** mshop/locale/manager/site/decorators/global
482
		 * Adds a list of globally available decorators only to the locale site manager
483
		 *
484
		 * Decorators extend the functionality of a class by adding new aspects
485
		 * (e.g. log what is currently done), executing the methods of the underlying
486
		 * class only in certain conditions (e.g. only for logged in users) or
487
		 * modify what is returned to the caller.
488
		 *
489
		 * This option allows you to wrap global decorators
490
		 * ("\Aimeos\MShop\Common\Manager\Decorator\*") around the locale site
491
		 * manager.
492
		 *
493
		 *  mshop/locale/manager/site/decorators/global = array( 'decorator1' )
494
		 *
495
		 * This would add the decorator named "decorator1" defined by
496
		 * "\Aimeos\MShop\Common\Manager\Decorator\Decorator1" only to the locale
497
		 * site manager.
498
		 *
499
		 * @param array List of decorator names
500
		 * @since 2014.03
501
		 * @category Developer
502
		 * @see mshop/common/manager/decorators/default
503
		 * @see mshop/locale/manager/site/decorators/excludes
504
		 * @see mshop/locale/manager/site/decorators/local
505
		 */
506
507
		/** mshop/locale/manager/site/decorators/local
508
		 * Adds a list of local decorators only to the locale site manager
509
		 *
510
		 * Decorators extend the functionality of a class by adding new aspects
511
		 * (e.g. log what is currently done), executing the methods of the underlying
512
		 * class only in certain conditions (e.g. only for logged in users) or
513
		 * modify what is returned to the caller.
514
		 *
515
		 * This option allows you to wrap local decorators
516
		 * ("\Aimeos\MShop\Locale\Manager\Site\Decorator\*") around the locale site
517
		 * manager.
518
		 *
519
		 *  mshop/locale/manager/site/decorators/local = array( 'decorator2' )
520
		 *
521
		 * This would add the decorator named "decorator2" defined by
522
		 * "\Aimeos\MShop\Locale\Manager\Site\Decorator\Decorator2" only to the
523
		 * locale site manager.
524
		 *
525
		 * @param array List of decorator names
526
		 * @since 2014.03
527
		 * @category Developer
528
		 * @see mshop/common/manager/decorators/default
529
		 * @see mshop/locale/manager/site/decorators/excludes
530
		 * @see mshop/locale/manager/site/decorators/global
531
		 */
532
533
		return $this->getSubManagerBase( 'locale', 'site/' . $manager, $name );
534
	}
535
536
537
	/**
538
	 * Searches for site items matching the given criteria.
539
	 *
540
	 * @param \Aimeos\MW\Criteria\Iface $search Search criteria object
541
	 * @param string[] $ref List of domains to fetch list items and referenced items for
542
	 * @param integer|null &$total Number of items that are available in total
543
	 * @return array List of site items implementing \Aimeos\MShop\Locale\Item\Site\Iface
544
	 */
545
	public function searchItems( \Aimeos\MW\Criteria\Iface $search, array $ref = [], &$total = null )
546
	{
547
		$items = [];
548
		$context = $this->getContext();
549
550
		$dbm = $context->getDatabaseManager();
551
		$dbname = $this->getResourceName();
552
		$conn = $dbm->acquire( $dbname );
553
554
		try
555
		{
556
			$attributes = $this->getObject()->getSearchAttributes();
557
			$types = $this->getSearchTypes( $attributes );
558
			$translations = $this->getSearchTranslations( $attributes );
559
			$columns = $search->translate( $search->getSortations(), $translations );
560
561
			$find = array( ':cond', ':order', ':columns', ':start', ':size' );
562
			$replace = array(
563
				$search->getConditionSource( $types, $translations ),
564
				$search->getSortationSource( $types, $translations ),
565
				( $columns ? ', ' . implode( ',', $columns ) : '' ),
566
				$search->getSliceStart(),
567
				$search->getSliceSize(),
568
			);
569
570
			/** mshop/locale/manager/site/standard/search/mysql
571
			 * Retrieves the records matched by the given criteria in the database
572
			 *
573
			 * @see mshop/locale/manager/site/standard/search/ansi
574
			 */
575
576
			/** mshop/locale/manager/site/standard/search/ansi
577
			 * Retrieves the records matched by the given criteria in the database
578
			 *
579
			 * Fetches the records matched by the given criteria from the attribute
580
			 * database. The records must be from one of the sites that are
581
			 * configured via the context item. If the current site is part of
582
			 * a tree of sites, the SELECT statement can retrieve all records
583
			 * from the current site and the complete sub-tree of sites.
584
			 *
585
			 * As the records can normally be limited by criteria from sub-managers,
586
			 * their tables must be joined in the SQL context. This is done by
587
			 * using the "internaldeps" property from the definition of the ID
588
			 * column of the sub-managers. These internal dependencies specify
589
			 * the JOIN between the tables and the used columns for joining. The
590
			 * ":joins" placeholder is then replaced by the JOIN strings from
591
			 * the sub-managers.
592
			 *
593
			 * To limit the records matched, conditions can be added to the given
594
			 * criteria object. It can contain comparisons like column names that
595
			 * must match specific values which can be combined by AND, OR or NOT
596
			 * operators. The resulting string of SQL conditions replaces the
597
			 * ":cond" placeholder before the statement is sent to the database
598
			 * server.
599
			 *
600
			 * If the records that are retrieved should be ordered by one or more
601
			 * columns, the generated string of column / sort direction pairs
602
			 * replaces the ":order" placeholder. In case no ordering is required,
603
			 * the complete ORDER BY part including the "\/*-orderby*\/...\/*orderby-*\/"
604
			 * markers is removed to speed up retrieving the records. Columns of
605
			 * sub-managers can also be used for ordering the result set but then
606
			 * no index can be used.
607
			 *
608
			 * The number of returned records can be limited and can start at any
609
			 * number between the begining and the end of the result set. For that
610
			 * the ":size" and ":start" placeholders are replaced by the
611
			 * corresponding values from the criteria object. The default values
612
			 * are 0 for the start and 100 for the size value.
613
			 *
614
			 * The SQL statement should conform to the ANSI standard to be
615
			 * compatible with most relational database systems. This also
616
			 * includes using double quotes for table and column names.
617
			 *
618
			 * @param string SQL statement for searching items
619
			 * @since 2014.03
620
			 * @category Developer
621
			 * @see mshop/locale/manager/site/standard/insert/ansi
622
			 * @see mshop/locale/manager/site/standard/update/ansi
623
			 * @see mshop/locale/manager/site/standard/delete/ansi
624
			 * @see mshop/locale/manager/site/standard/count/ansi
625
			 * @see mshop/locale/manager/site/standard/newid/ansi
626
			 */
627
			$path = 'mshop/locale/manager/site/standard/search';
628
629
			$sql = $this->getSqlConfig( $path );
630
			$results = $this->getSearchResults( $conn, str_replace( $find, $replace, $sql ) );
631
632
			try
633
			{
634
				while( ( $row = $results->fetch() ) !== false )
635
				{
636
					$config = $row['locale.site.config'];
637
638
					if( ( $row['locale.site.config'] = json_decode( $row['locale.site.config'], true ) ) === null )
639
					{
640
						$msg = sprintf( 'Invalid JSON as result of search for ID "%2$s" in "%1$s": %3$s', 'mshop_locale.config', $row['locale.site.id'], $config );
641
						$this->getContext()->getLogger()->log( $msg, \Aimeos\MW\Logger\Base::WARN );
642
					}
643
644
					$items[$row['locale.site.id']] = $this->createItemBase( $row );
645
				}
646
			}
647
			catch( \Exception $e )
648
			{
649
				$results->finish();
650
				throw $e;
651
			}
652
653
			if( $total !== null ) {
654
				$total = $this->getTotal( $conn, $find, $replace );
655
			}
656
657
			$dbm->release( $conn, $dbname );
658
		}
659
		catch( \Exception $e )
660
		{
661
			$dbm->release( $conn, $dbname );
662
			throw $e;
663
		}
664
665
		return $items;
666
	}
667
668
669
	/**
670
	 * Creates a search object and sets base criteria.
671
	 *
672
	 * @param boolean $default
673
	 * @return \Aimeos\MW\Criteria\Iface
674
	 */
675
	public function createSearch( $default = false )
676
	{
677
		if( $default === true ) {
678
			$search = $this->createSearchBase( 'locale.site' );
679
		} else {
680
			$search = parent::createSearch( $default );
681
		}
682
683
		$expr = array(
684
			$search->compare( '==', 'locale.site.level', 0 ),
685
			$search->getConditions(),
686
		);
687
688
		$search->setConditions( $search->combine( '&&', $expr ) );
689
690
		return $search;
691
	}
692
693
694
	/**
695
	 * Returns a list of item IDs, that are in the path of given item ID.
696
	 *
697
	 * @param integer $id ID of item to get the path for
698
	 * @param array $ref List of domains to fetch list items and referenced items for
699
	 * @return \Aimeos\MShop\Locale\Item\Site\Iface[] Associative list of items implementing \Aimeos\MShop\Locale\Item\Site\Iface with IDs as keys
700
	 */
701
	public function getPath( $id, array $ref = [] )
702
	{
703
		$item = $this->getTree( $id, $ref, \Aimeos\MW\Tree\Manager\Base::LEVEL_ONE );
704
		return array( $item->getId() => $item );
705
	}
706
707
708
	/**
709
	 * Returns a node and its descendants depending on the given resource.
710
	 *
711
	 * @param integer|null $id Retrieve nodes starting from the given ID
712
	 * @param array List of domains (e.g. text, media, etc.) whose referenced items should be attached to the objects
713
	 * @param integer $level One of the level constants from \Aimeos\MW\Tree\Manager\Base
714
	 * @return \Aimeos\MShop\Locale\Item\Site\Iface Site item
715
	 */
716
	public function getTree( $id = null, array $ref = [], $level = \Aimeos\MW\Tree\Manager\Base::LEVEL_TREE )
717
	{
718
		if( $id !== null )
719
		{
720
			if( count( $ref ) > 0 ) {
721
				return $this->getObject()->getItem( $id, $ref );
722
			}
723
724
			if( !isset( $this->cache[$id] ) ) {
725
				$this->cache[$id] = $this->getObject()->getItem( $id, $ref );
726
			}
727
728
			return $this->cache[$id];
729
		}
730
731
		$criteria = $this->getObject()->createSearch();
732
		$criteria->setConditions( $criteria->compare( '==', 'locale.site.code', 'default' ) );
733
		$criteria->setSlice( 0, 1 );
734
735
		$items = $this->getObject()->searchItems( $criteria, $ref );
736
737
		if( ( $item = reset( $items ) ) === false ) {
738
			throw new \Aimeos\MShop\Locale\Exception( sprintf( 'Tree root with code "%1$s" in "%2$s" not found', 'default', 'locale.site.code' ) );
739
		}
740
741
		$this->cache[$item->getId()] = $item;
742
743
		return $item;
744
	}
745
746
747
	/**
748
	 * Adds a new item object.
749
	 *
750
	 * @param \Aimeos\MShop\Locale\Item\Site\Iface $item Item which should be inserted
751
	 * @param integer|null $parentId ID of the parent item where the item should be inserted into
752
	 * @param integer|null $refId ID of the item where the item should be inserted before (null to append)
753
	 * @return \Aimeos\MShop\Common\Item\Iface $item Updated item including the generated ID
754
	 */
755
	public function insertItem( \Aimeos\MShop\Locale\Item\Site\Iface $item, $parentId = null, $refId = null )
756
	{
757
		$context = $this->getContext();
758
759
		$dbm = $context->getDatabaseManager();
760
		$dbname = $this->getResourceName();
761
		$conn = $dbm->acquire( $dbname );
762
763
		try
764
		{
765
			$date = date( 'Y-m-d H:i:s' );
766
767
			/** mshop/locale/manager/site/standard/insert/mysql
768
			 * Inserts a new currency record into the database table
769
			 *
770
			 * @see mshop/locale/manager/site/standard/insert/ansi
771
			 */
772
773
			/** mshop/locale/manager/site/standard/insert/ansi
774
			 * Inserts a new currency record into the database table
775
			 *
776
			 * The SQL statement must be a string suitable for being used as
777
			 * prepared statement. It must include question marks for binding
778
			 * the values from the site item to the statement before they are
779
			 * sent to the database server. The number of question marks must
780
			 * be the same as the number of columns listed in the INSERT
781
			 * statement. The order of the columns must correspond to the
782
			 * order in the saveItems() method, so the correct values are
783
			 * bound to the columns.
784
			 *
785
			 * The SQL statement should conform to the ANSI standard to be
786
			 * compatible with most relational database systems. This also
787
			 * includes using double quotes for table and column names.
788
			 *
789
			 * @param string SQL statement for inserting records
790
			 * @since 2014.03
791
			 * @category Developer
792
			 * @see mshop/locale/manager/site/standard/update/ansi
793
			 * @see mshop/locale/manager/site/standard/delete/ansi
794
			 * @see mshop/locale/manager/site/standard/search/ansi
795
			 * @see mshop/locale/manager/site/standard/count/ansi
796
			 * @see mshop/locale/manager/site/standard/newid/ansi
797
			 */
798
			$path = 'mshop/locale/manager/site/standard/insert';
799
800
			$stmt = $this->getCachedStatement( $conn, $path );
801
802
			$stmt->bind( 1, $item->getCode() );
803
			$stmt->bind( 2, $item->getLabel() );
804
			$stmt->bind( 3, json_encode( $item->getConfig() ) );
805
			$stmt->bind( 4, $item->getStatus(), \Aimeos\MW\DB\Statement\Base::PARAM_INT );
806
			$stmt->bind( 5, $context->getEditor() );
807
			$stmt->bind( 6, $date ); // mtime
808
			$stmt->bind( 7, $date ); // ctime
809
810
			$stmt->execute()->finish();
811
812
			/** mshop/locale/manager/site/standard/newid/mysql
813
			 * Retrieves the ID generated by the database when inserting a new record
814
			 *
815
			 * @see mshop/locale/manager/site/standard/newid/ansi
816
			 */
817
818
			/** mshop/locale/manager/site/standard/newid/ansi
819
			 * Retrieves the ID generated by the database when inserting a new record
820
			 *
821
			 * As soon as a new record is inserted into the database table,
822
			 * the database server generates a new and unique identifier for
823
			 * that record. This ID can be used for retrieving, updating and
824
			 * deleting that specific record from the table again.
825
			 *
826
			 * For MySQL:
827
			 *  SELECT LAST_INSERT_ID()
828
			 * For PostgreSQL:
829
			 *  SELECT currval('seq_matt_id')
830
			 * For SQL Server:
831
			 *  SELECT SCOPE_IDENTITY()
832
			 * For Oracle:
833
			 *  SELECT "seq_matt_id".CURRVAL FROM DUAL
834
			 *
835
			 * There's no way to retrive the new ID by a SQL statements that
836
			 * fits for most database servers as they implement their own
837
			 * specific way.
838
			 *
839
			 * @param string SQL statement for retrieving the last inserted record ID
840
			 * @since 2014.03
841
			 * @category Developer
842
			 * @see mshop/locale/manager/site/standard/insert/ansi
843
			 * @see mshop/locale/manager/site/standard/update/ansi
844
			 * @see mshop/locale/manager/site/standard/delete/ansi
845
			 * @see mshop/locale/manager/site/standard/search/ansi
846
			 * @see mshop/locale/manager/site/standard/count/ansi
847
			 */
848
			$path = 'mshop/locale/manager/standard/newid';
849
			$item->setId( $this->newId( $conn, $this->getSqlConfig( $path ) ) );
850
851
			$dbm->release( $conn, $dbname );
852
		}
853
		catch( \Exception $e )
854
		{
855
			$dbm->release( $conn, $dbname );
856
			throw $e;
857
		}
858
859
		return $item;
860
	}
861
862
863
	/**
864
	 * Moves an existing item to the new parent in the storage.
865
	 *
866
	 * @param integer $id ID of the item that should be moved
867
	 * @param integer $oldParentId ID of the old parent item which currently contains the item that should be removed
868
	 * @param integer $newParentId ID of the new parent item where the item should be moved to
869
	 * @param integer|null $refId ID of the item where the item should be inserted before (null to append)
870
	 */
871
	public function moveItem( $id, $oldParentId, $newParentId, $refId = null )
872
	{
873
		throw new \Aimeos\MShop\Locale\Exception( sprintf( 'Method "%1$s" for locale site manager not available', 'moveItem()' ) );
874
	}
875
876
877
	/**
878
	 * Registers a new item filter for the given name
879
	 *
880
	 * Not used for site items but required for trees
881
	 *
882
	 * @param string $name Filter name
883
	 * @param \Closure $fcn Callback function
884
	 */
885
	public function registerItemFilter( $name, \Closure $fcn )
886
	{
887
	}
888
889
890
	/**
891
	 * Returns the search results for the given SQL statement.
892
	 *
893
	 * @param \Aimeos\MW\DB\Connection\Iface $conn Database connection
894
	 * @param string $sql SQL statement
895
	 * @return \Aimeos\MW\DB\Result\Iface Search result object
896
	 */
897
	protected function getSearchResults( \Aimeos\MW\DB\Connection\Iface $conn, $sql )
898
	{
899
		$time = microtime( true );
900
901
		$stmt = $conn->create( $sql );
902
		$result = $stmt->execute();
903
904
		$msg = [
905
			'time' => ( microtime( true ) - $time ) * 1000,
906
			'class' => get_class( $this ),
907
			'stmt' => (string) $stmt,
908
		];
909
		$this->getContext()->getLogger()->log( $msg, \Aimeos\MW\Logger\Base::DEBUG, 'core/sql' );
910
911
		return $result;
912
	}
913
914
915
	/**
916
	 * Create new item object initialized with given parameters.
917
	 *
918
	 * @param array $data Associative list of item key/value pairs
919
	 * @return \Aimeos\MShop\Locale\Item\Site\Iface Site item object
920
	 */
921
	protected function createItemBase( array $data = [] )
922
	{
923
		return new \Aimeos\MShop\Locale\Item\Site\Standard( $data );
924
	}
925
926
927
	/**
928
	 * Returns the raw search config array.
929
	 *
930
	 * @return array List of search config arrays
931
	 */
932
	protected function getSearchConfig()
933
	{
934
		return $this->searchConfig;
935
	}
936
937
938
	/**
939
	 * Returns the total number of items found for the conditions
940
	 *
941
	 * @param \Aimeos\MW\DB\Connection\Iface $conn Database connection
942
	 * @param array $find List of markers that should be replaced in the SQL statement
943
	 * @param array $replace List of replacements for the markers in the SQL statement
944
	 * @throws \Aimeos\MShop\Locale\Exception If no total value was found
945
	 * @return integer Total number of found items
946
	 */
947
	protected function getTotal( \Aimeos\MW\DB\Connection\Iface $conn, array $find, array $replace )
948
	{
949
		/** mshop/locale/manager/site/standard/count/mysql
950
		 * Counts the number of records matched by the given criteria in the database
951
		 *
952
		 * @see mshop/locale/manager/site/standard/count/ansi
953
		 */
954
955
		/** mshop/locale/manager/site/standard/count/ansi
956
		 * Counts the number of records matched by the given criteria in the database
957
		 *
958
		 * Counts all records matched by the given criteria from the attribute
959
		 * database. The records must be from one of the sites that are
960
		 * configured via the context item. If the current site is part of
961
		 * a tree of sites, the statement can count all records from the
962
		 * current site and the complete sub-tree of sites.
963
		 *
964
		 * As the records can normally be limited by criteria from sub-managers,
965
		 * their tables must be joined in the SQL context. This is done by
966
		 * using the "internaldeps" property from the definition of the ID
967
		 * column of the sub-managers. These internal dependencies specify
968
		 * the JOIN between the tables and the used columns for joining. The
969
		 * ":joins" placeholder is then replaced by the JOIN strings from
970
		 * the sub-managers.
971
		 *
972
		 * To limit the records matched, conditions can be added to the given
973
		 * criteria object. It can contain comparisons like column names that
974
		 * must match specific values which can be combined by AND, OR or NOT
975
		 * operators. The resulting string of SQL conditions replaces the
976
		 * ":cond" placeholder before the statement is sent to the database
977
		 * server.
978
		 *
979
		 * Both, the strings for ":joins" and for ":cond" are the same as for
980
		 * the "search" SQL statement.
981
		 *
982
		 * Contrary to the "search" statement, it doesn't return any records
983
		 * but instead the number of records that have been found. As counting
984
		 * thousands of records can be a long running task, the maximum number
985
		 * of counted records is limited for performance reasons.
986
		 *
987
		 * The SQL statement should conform to the ANSI standard to be
988
		 * compatible with most relational database systems. This also
989
		 * includes using double quotes for table and column names.
990
		 *
991
		 * @param string SQL statement for counting items
992
		 * @since 2014.03
993
		 * @category Developer
994
		 * @see mshop/locale/manager/site/standard/insert/ansi
995
		 * @see mshop/locale/manager/site/standard/update/ansi
996
		 * @see mshop/locale/manager/site/standard/delete/ansi
997
		 * @see mshop/locale/manager/site/standard/search/ansi
998
		 * @see mshop/locale/manager/site/standard/newid/ansi
999
		 */
1000
		$path = 'mshop/locale/manager/site/standard/count';
1001
1002
		$sql = $this->getSqlConfig( $path );
1003
		$results = $this->getSearchResults( $conn, str_replace( $find, $replace, $sql ) );
1004
1005
		$row = $results->fetch();
1006
		$results->finish();
1007
1008
		if( $row === false ) {
1009
			throw new \Aimeos\MShop\Locale\Exception( 'No total results value found' );
1010
		}
1011
1012
		return $row['count'];
1013
	}
1014
}
1015