Passed
Push — master ( 1c1daf...484f92 )
by Aimeos
04:34
created

Standard::saveText()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nc 2
nop 8
dl 0
loc 14
rs 9.9332
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
4
 * @copyright Metaways Infosystems GmbH, 2012
5
 * @copyright Aimeos (aimeos.org), 2015-2018
6
 * @package MShop
7
 * @subpackage Index
8
 */
9
10
11
namespace Aimeos\MShop\Index\Manager\Text;
12
13
14
/**
15
 * Submanager for text.
16
 *
17
 * @package MShop
18
 * @subpackage Index
19
 */
20
class Standard
21
	extends \Aimeos\MShop\Index\Manager\DBBase
22
	implements \Aimeos\MShop\Index\Manager\Text\Iface, \Aimeos\MShop\Common\Manager\Factory\Iface
23
{
24
	private $searchConfig = array(
25
		// @deprecated Removed 2019.01
26
		'index.text.id' => array(
27
			'code' => 'index.text.id',
28
			'internalcode' => 'mindte."prodid"',
29
			'internaldeps'=>array( 'LEFT JOIN "mshop_index_text" AS mindte ON mindte."prodid" = mpro."id"' ),
30
			'label' => 'Product index text ID',
31
			'type' => 'string',
32
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
33
			'public' => false,
34
		),
35
		'index.text:url' => array(
36
			'code' => 'index.text:url()',
37
			'internalcode' => ':site AND mindte."langid" = $1 AND mindte."url"',
38
			'label' => 'Product URL by language, parameter(<language ID>)',
39
			'type' => 'string',
40
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
41
			'public' => false,
42
		),
43
		'index.text:name' => array(
44
			'code' => 'index.text:name()',
45
			'internalcode' => ':site AND mindte."langid" = $1 AND mindte."name"',
46
			'label' => 'Product name, parameter(<language ID>)',
47
			'type' => 'string',
48
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
49
			'public' => false,
50
		),
51
		'sort:index.text:name' => array(
52
			'code' => 'sort:index.text:name()',
53
			'internalcode' => 'mindte."name"',
54
			'label' => 'Sort by product name, parameter(<language ID>)',
55
			'type' => 'string',
56
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_STR,
57
			'public' => false,
58
		),
59
		'index.text:relevance' => array(
60
			'code' => 'index.text:relevance()',
61
			'internalcode' => ':site AND mindte."langid" = $1 AND POSITION( $2 IN mindte."content" )',
62
			'label' => 'Product texts, parameter(<language ID>,<search term>)',
63
			'type' => 'float',
64
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_FLOAT,
65
			'public' => false,
66
		),
67
		'sort:index.text:relevance' => array(
68
			'code' => 'sort:index.text:relevance()',
69
			'internalcode' => 'POSITION( $2 IN mindte."content" )',
70
			'label' => 'Product texts, parameter(<language ID>,<search term>)',
71
			'type' => 'float',
72
			'internaltype' => \Aimeos\MW\DB\Statement\Base::PARAM_FLOAT,
73
			'public' => false,
74
		),
75
	);
76
77
	private $languageIds;
78
	private $subManagers;
79
80
81
	/**
82
	 * Initializes the manager instance.
83
	 *
84
	 * @param \Aimeos\MShop\Context\Item\Iface $context Context object
85
	 */
86
	public function __construct( \Aimeos\MShop\Context\Item\Iface $context )
87
	{
88
		parent::__construct( $context );
89
90
		$locale = $context->getLocale();
91
		$level = \Aimeos\MShop\Locale\Manager\Base::SITE_ALL;
92
		$level = $context->getConfig()->get( 'mshop/index/manager/sitemode', $level );
93
94
		$siteIds = [$locale->getSiteId()];
95
96
		if( $level & \Aimeos\MShop\Locale\Manager\Base::SITE_PATH ) {
97
			$siteIds = array_merge( $siteIds, $locale->getSitePath() );
98
		}
99
100
		if( $level & \Aimeos\MShop\Locale\Manager\Base::SITE_SUBTREE ) {
101
			$siteIds = array_merge( $siteIds, $locale->getSiteSubTree() );
102
		}
103
104
105
		$this->searchConfig['index.text:relevance']['function'] = function( $source, array $params ) {
106
107
			if( isset( $params[1] ) ) {
108
				$params[1] = strtolower( $params[1] );
109
			}
110
111
			return $params;
112
		};
113
114
		$this->replaceSiteMarker( $this->searchConfig['index.text:url'], 'mindte."siteid"', $siteIds );
115
		$this->replaceSiteMarker( $this->searchConfig['index.text:name'], 'mindte."siteid"', $siteIds );
116
		$this->replaceSiteMarker( $this->searchConfig['index.text:relevance'], 'mindte."siteid"', $siteIds );
117
	}
118
119
120
	/**
121
	 * Counts the number products that are available for the values of the given key.
122
	 *
123
	 * @param \Aimeos\MW\Criteria\Iface $search Search criteria
124
	 * @param string $key Search key (usually the ID) to aggregate products for
125
	 * @return integer[] List of ID values as key and the number of counted products as value
126
	 */
127
	public function aggregate( \Aimeos\MW\Criteria\Iface $search, $key )
128
	{
129
		return [];
130
	}
131
132
133
	/**
134
	 * Removes old entries from the storage.
135
	 *
136
	 * @param string[] $siteids List of IDs for sites whose entries should be deleted
137
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
138
	 */
139
	public function cleanup( array $siteids )
140
	{
141
		parent::cleanup( $siteids );
142
143
		return $this->cleanupBase( $siteids, 'mshop/index/manager/text/standard/delete' );
144
	}
145
146
147
	/**
148
	 * Removes all entries not touched after the given timestamp in the index.
149
	 * This can be a long lasting operation.
150
	 *
151
	 * @param string $timestamp Timestamp in ISO format (YYYY-MM-DD HH:mm:ss)
152
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
153
	 */
154
	public function cleanupIndex( $timestamp )
155
	{
156
		/** mshop/index/manager/text/standard/cleanup/mysql
157
		 * Deletes the index text records that haven't been touched
158
		 *
159
		 * @see mshop/index/manager/text/standard/cleanup/ansi
160
		 */
161
162
		/** mshop/index/manager/text/standard/cleanup/ansi
163
		 * Deletes the index text records that haven't been touched
164
		 *
165
		 * During the rebuild process of the product index, the entries of all
166
		 * active products will be removed and readded. Thus, no stale data for
167
		 * these products will remain in the database.
168
		 *
169
		 * All products that have been disabled since the last rebuild will be
170
		 * still part of the index. The cleanup statement removes all records
171
		 * that belong to products that haven't been touched during the index
172
		 * rebuild because these are the disabled ones.
173
		 *
174
		 * The SQL statement should conform to the ANSI standard to be
175
		 * compatible with most relational database systems. This also
176
		 * includes using double quotes for table and column names.
177
		 *
178
		 * @param string SQL statement for deleting the outdated text index records
179
		 * @since 2014.03
180
		 * @category Developer
181
		 * @see mshop/index/manager/text/standard/count/ansi
182
		 * @see mshop/index/manager/text/standard/delete/ansi
183
		 * @see mshop/index/manager/text/standard/insert/ansi
184
		 * @see mshop/index/manager/text/standard/search/ansi
185
		 * @see mshop/index/manager/text/standard/text/ansi
186
		 */
187
		return $this->cleanupIndexBase( $timestamp, 'mshop/index/manager/text/standard/cleanup' );
188
	}
189
190
191
	/**
192
	 * Removes multiple items from the index.
193
	 *
194
	 * @param string[] $ids List of Product IDs
195
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
196
	 */
197
	public function deleteItems( array $ids )
198
	{
199
		/** mshop/index/manager/text/standard/delete/mysql
200
		 * Deletes the items matched by the given IDs from the database
201
		 *
202
		 * @see mshop/index/manager/text/standard/delete/ansi
203
		 */
204
205
		/** mshop/index/manager/text/standard/delete/ansi
206
		 * Deletes the items matched by the given IDs from the database
207
		 *
208
		 * Removes the records specified by the given IDs from the index database.
209
		 * The records must be from the site that is configured via the
210
		 * context item.
211
		 *
212
		 * The ":cond" placeholder is replaced by the name of the ID column and
213
		 * the given ID or list of IDs while the site ID is bound to the question
214
		 * mark.
215
		 *
216
		 * The SQL statement should conform to the ANSI standard to be
217
		 * compatible with most relational database systems. This also
218
		 * includes using double quotes for table and column names.
219
		 *
220
		 * @param string SQL statement for deleting index text records
221
		 * @since 2014.03
222
		 * @category Developer
223
		 * @see mshop/index/manager/text/standard/count/ansi
224
		 * @see mshop/index/manager/text/standard/cleanup/ansi
225
		 * @see mshop/index/manager/text/standard/insert/ansi
226
		 * @see mshop/index/manager/text/standard/search/ansi
227
		 * @see mshop/index/manager/text/standard/text/ansi
228
		 */
229
		return $this->deleteItemsBase( $ids, 'mshop/index/manager/text/standard/delete', true, 'prodid' );
230
	}
231
232
233
	/**
234
	 * Returns the available manager types
235
	 *
236
	 * @param boolean $withsub Return also the resource type of sub-managers if true
237
	 * @return string[] Type of the manager and submanagers, subtypes are separated by slashes
238
	 */
239
	public function getResourceType( $withsub = true )
240
	{
241
		$path = 'mshop/index/manager/text/submanagers';
242
		return $this->getResourceTypeBase( 'index/text', $path, [], $withsub );
243
	}
244
245
246
	/**
247
	 * Returns a list of objects describing the available criterias for searching.
248
	 *
249
	 * @param boolean $withsub Return also attributes of sub-managers if true
250
	 * @return array List of items implementing \Aimeos\MW\Criteria\Attribute\Iface
251
	 */
252
	public function getSearchAttributes( $withsub = true )
253
	{
254
		$list = parent::getSearchAttributes( $withsub );
255
256
		/** mshop/index/manager/text/submanagers
257
		 * List of manager names that can be instantiated by the index text manager
258
		 *
259
		 * Managers provide a generic interface to the underlying storage.
260
		 * Each manager has or can have sub-managers caring about particular
261
		 * aspects. Each of these sub-managers can be instantiated by its
262
		 * parent manager using the getSubManager() method.
263
		 *
264
		 * The search keys from sub-managers can be normally used in the
265
		 * manager as well. It allows you to search for items of the manager
266
		 * using the search keys of the sub-managers to further limit the
267
		 * retrieved list of items.
268
		 *
269
		 * @param array List of sub-manager names
270
		 * @since 2014.03
271
		 * @category Developer
272
		 */
273
		$path = 'mshop/index/manager/text/submanagers';
274
275
		return $list + $this->getSearchAttributesBase( $this->searchConfig, $path, [], $withsub );
276
	}
277
278
279
	/**
280
	 * Returns a new manager for product extensions.
281
	 *
282
	 * @param string $manager Name of the sub manager type in lower case
283
	 * @param string|null $name Name of the implementation, will be from configuration (or Default) if null
284
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager for different extensions, e.g stock, tags, locations, etc.
285
	 */
286
	public function getSubManager( $manager, $name = null )
287
	{
288
		/** mshop/index/manager/text/name
289
		 * Class name of the used index text manager implementation
290
		 *
291
		 * Each default index text manager can be replaced by an alternative imlementation.
292
		 * To use this implementation, you have to set the last part of the class
293
		 * name as configuration value so the manager factory knows which class it
294
		 * has to instantiate.
295
		 *
296
		 * For example, if the name of the default class is
297
		 *
298
		 *  \Aimeos\MShop\Index\Manager\Text\Standard
299
		 *
300
		 * and you want to replace it with your own version named
301
		 *
302
		 *  \Aimeos\MShop\Index\Manager\Text\Mytext
303
		 *
304
		 * then you have to set the this configuration option:
305
		 *
306
		 *  mshop/index/manager/text/name = Mytext
307
		 *
308
		 * The value is the last part of your own class name and it's case sensitive,
309
		 * so take care that the configuration value is exactly named like the last
310
		 * part of the class name.
311
		 *
312
		 * The allowed characters of the class name are A-Z, a-z and 0-9. No other
313
		 * characters are possible! You should always start the last part of the class
314
		 * name with an upper case character and continue only with lower case characters
315
		 * or numbers. Avoid chamel case names like "MyText"!
316
		 *
317
		 * @param string Last part of the class name
318
		 * @since 2014.03
319
		 * @category Developer
320
		 */
321
322
		/** mshop/index/manager/text/decorators/excludes
323
		 * Excludes decorators added by the "common" option from the index text manager
324
		 *
325
		 * Decorators extend the functionality of a class by adding new aspects
326
		 * (e.g. log what is currently done), executing the methods of the underlying
327
		 * class only in certain conditions (e.g. only for logged in users) or
328
		 * modify what is returned to the caller.
329
		 *
330
		 * This option allows you to remove a decorator added via
331
		 * "mshop/common/manager/decorators/default" before they are wrapped
332
		 * around the index text manager.
333
		 *
334
		 *  mshop/index/manager/text/decorators/excludes = array( 'decorator1' )
335
		 *
336
		 * This would remove the decorator named "decorator1" from the list of
337
		 * common decorators ("\Aimeos\MShop\Common\Manager\Decorator\*") added via
338
		 * "mshop/common/manager/decorators/default" for the index text manager.
339
		 *
340
		 * @param array List of decorator names
341
		 * @since 2014.03
342
		 * @category Developer
343
		 * @see mshop/common/manager/decorators/default
344
		 * @see mshop/index/manager/text/decorators/global
345
		 * @see mshop/index/manager/text/decorators/local
346
		 */
347
348
		/** mshop/index/manager/text/decorators/global
349
		 * Adds a list of globally available decorators only to the index text manager
350
		 *
351
		 * Decorators extend the functionality of a class by adding new aspects
352
		 * (e.g. log what is currently done), executing the methods of the underlying
353
		 * class only in certain conditions (e.g. only for logged in users) or
354
		 * modify what is returned to the caller.
355
		 *
356
		 * This option allows you to wrap global decorators
357
		 * ("\Aimeos\MShop\Common\Manager\Decorator\*") around the index text
358
		 * manager.
359
		 *
360
		 *  mshop/index/manager/text/decorators/global = array( 'decorator1' )
361
		 *
362
		 * This would add the decorator named "decorator1" defined by
363
		 * "\Aimeos\MShop\Common\Manager\Decorator\Decorator1" only to the index
364
		 * text manager.
365
		 *
366
		 * @param array List of decorator names
367
		 * @since 2014.03
368
		 * @category Developer
369
		 * @see mshop/common/manager/decorators/default
370
		 * @see mshop/index/manager/text/decorators/excludes
371
		 * @see mshop/index/manager/text/decorators/local
372
		 */
373
374
		/** mshop/index/manager/text/decorators/local
375
		 * Adds a list of local decorators only to the index text manager
376
		 *
377
		 * Decorators extend the functionality of a class by adding new aspects
378
		 * (e.g. log what is currently done), executing the methods of the underlying
379
		 * class only in certain conditions (e.g. only for logged in users) or
380
		 * modify what is returned to the caller.
381
		 *
382
		 * This option allows you to wrap local decorators
383
		 * ("\Aimeos\MShop\Index\Manager\Text\Decorator\*") around the index text
384
		 * manager.
385
		 *
386
		 *  mshop/index/manager/text/decorators/local = array( 'decorator2' )
387
		 *
388
		 * This would add the decorator named "decorator2" defined by
389
		 * "\Aimeos\MShop\Index\Manager\Text\Decorator\Decorator2" only to the index
390
		 * text manager.
391
		 *
392
		 * @param array List of decorator names
393
		 * @since 2014.03
394
		 * @category Developer
395
		 * @see mshop/common/manager/decorators/default
396
		 * @see mshop/index/manager/text/decorators/excludes
397
		 * @see mshop/index/manager/text/decorators/global
398
		 */
399
400
		return $this->getSubManagerBase( 'index', 'text/' . $manager, $name );
401
	}
402
403
404
	/**
405
	 * Optimizes the index if necessary.
406
	 * Execution of this operation can take a very long time and shouldn't be
407
	 * called through a web server enviroment.
408
	 *
409
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
410
	 */
411
	public function optimize()
412
	{
413
		/** mshop/index/manager/text/standard/optimize/mysql
414
		 * Optimizes the stored text data for retrieving the records faster
415
		 *
416
		 * @see mshop/index/manager/text/standard/optimize/ansi
417
		 */
418
419
		/** mshop/index/manager/text/standard/optimize/ansi
420
		 * Optimizes the stored text data for retrieving the records faster
421
		 *
422
		 * The SQL statement should reorganize the data in the DBMS storage to
423
		 * optimize access to the records of the table or tables. Some DBMS
424
		 * offer specialized statements to optimize indexes and records. This
425
		 * statement doesn't return any records.
426
		 *
427
		 * The SQL statement should conform to the ANSI standard to be
428
		 * compatible with most relational database systems. This also
429
		 * includes using double quotes for table and column names.
430
		 *
431
		 * @param string SQL statement for optimizing the stored text data
432
		 * @since 2014.09
433
		 * @category Developer
434
		 * @see mshop/index/manager/text/standard/aggregate/ansi
435
		 * @see mshop/index/manager/text/standard/cleanup/ansi
436
		 * @see mshop/index/manager/text/standard/count/ansi
437
		 * @see mshop/index/manager/text/standard/insert/ansi
438
		 * @see mshop/index/manager/text/standard/search/ansi
439
		 * @see mshop/index/manager/text/standard/text/ansi
440
		 */
441
		return $this->optimizeBase( 'mshop/index/manager/text/standard/optimize' );
442
	}
443
444
445
	/**
446
	 * Rebuilds the index text for searching products or specified list of products.
447
	 * This can be a long lasting operation.
448
	 *
449
	 * @param \Aimeos\MShop\Product\Item\Iface[] $items Associative list of product IDs as keys and items as values
450
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
451
	 */
452
	public function rebuildIndex( array $items = [] )
453
	{
454
		if( empty( $items ) ) { return $this; }
455
456
		\Aimeos\MW\Common\Base::checkClassList( \Aimeos\MShop\Product\Item\Iface::class, $items );
457
458
		$context = $this->getContext();
459
		$dbm = $context->getDatabaseManager();
460
		$dbname = $this->getResourceName();
461
		$conn = $dbm->acquire( $dbname );
462
463
		try
464
		{
465
			/** mshop/index/manager/text/standard/insert/mysql
466
			 * Inserts a new text record into the product index database
467
			 *
468
			 * @see mshop/index/manager/text/standard/insert/ansi
469
			 */
470
471
			/** mshop/index/manager/text/standard/insert/ansi
472
			 * Inserts a new text record into the product index database
473
			 *
474
			 * During the product index rebuild, texts related to a product
475
			 * will be stored in the index for this product. All records
476
			 * are deleted before the new ones are inserted.
477
			 *
478
			 * The SQL statement must be a string suitable for being used as
479
			 * prepared statement. It must include question marks for binding
480
			 * the values from the order item to the statement before they are
481
			 * sent to the database server. The number of question marks must
482
			 * be the same as the number of columns listed in the INSERT
483
			 * statement. The order of the columns must correspond to the
484
			 * order in the rebuildIndex() method, so the correct values are
485
			 * bound to the columns.
486
			 *
487
			 * The SQL statement should conform to the ANSI standard to be
488
			 * compatible with most relational database systems. This also
489
			 * includes using double quotes for table and column names.
490
			 *
491
			 * @param string SQL statement for inserting records
492
			 * @since 2014.03
493
			 * @category Developer
494
			 * @see mshop/index/manager/text/standard/cleanup/ansi
495
			 * @see mshop/index/manager/text/standard/count/ansi
496
			 * @see mshop/index/manager/text/standard/delete/ansi
497
			 * @see mshop/index/manager/text/standard/insert/ansi
498
			 * @see mshop/index/manager/text/standard/search/ansi
499
			 * @see mshop/index/manager/text/standard/text/ansi
500
			 */
501
			$stmt = $this->getCachedStatement( $conn, 'mshop/index/manager/text/standard/insert' );
502
503
			foreach( $items as $item ) {
504
				$this->saveTexts( $stmt, $item );
505
			}
506
507
			$dbm->release( $conn, $dbname );
508
		}
509
		catch( \Exception $e )
510
		{
511
			$dbm->release( $conn, $dbname );
512
			throw $e;
513
		}
514
515
		foreach( $this->getSubManagers() as $submanager ) {
516
			$submanager->rebuildIndex( $items );
517
		}
518
519
		return $this;
520
	}
521
522
523
	/**
524
	 * Searches for items matching the given criteria.
525
	 *
526
	 * @param \Aimeos\MW\Criteria\Iface $search Search criteria object
527
	 * @param string[] $ref List of domains to fetch list items and referenced items for
528
	 * @param integer|null &$total Number of items that are available in total
529
	 * @return array List of items implementing \Aimeos\MShop\Product\Item\Iface with ids as keys
530
	 */
531
	public function searchItems( \Aimeos\MW\Criteria\Iface $search, array $ref = [], &$total = null )
532
	{
533
		/** mshop/index/manager/text/standard/search/mysql
534
		 * Retrieves the records matched by the given criteria in the database
535
		 *
536
		 * @see mshop/index/manager/text/standard/search/ansi
537
		 */
538
539
		/** mshop/index/manager/text/standard/search/ansi
540
		 * Retrieves the records matched by the given criteria in the database
541
		 *
542
		 * Fetches the records matched by the given criteria from the product index
543
		 * database. The records must be from one of the sites that are
544
		 * configured via the context item. If the current site is part of
545
		 * a tree of sites, the SELECT statement can retrieve all records
546
		 * from the current site and the complete sub-tree of sites.
547
		 *
548
		 * As the records can normally be limited by criteria from sub-managers,
549
		 * their tables must be joined in the SQL context. This is done by
550
		 * using the "internaldeps" property from the definition of the ID
551
		 * column of the sub-managers. These internal dependencies specify
552
		 * the JOIN between the tables and the used columns for joining. The
553
		 * ":joins" placeholder is then replaced by the JOIN strings from
554
		 * the sub-managers.
555
		 *
556
		 * To limit the records matched, conditions can be added to the given
557
		 * criteria object. It can contain comparisons like column names that
558
		 * must match specific values which can be combined by AND, OR or NOT
559
		 * operators. The resulting string of SQL conditions replaces the
560
		 * ":cond" placeholder before the statement is sent to the database
561
		 * server.
562
		 *
563
		 * If the records that are retrieved should be ordered by one or more
564
		 * columns, the generated string of column / sort direction pairs
565
		 * replaces the ":order" placeholder. In case no ordering is required,
566
		 * the complete ORDER BY part including the "\/*-orderby*\/...\/*orderby-*\/"
567
		 * markers is removed to speed up retrieving the records. Columns of
568
		 * sub-managers can also be used for ordering the result set but then
569
		 * no index can be used.
570
		 *
571
		 * The number of returned records can be limited and can start at any
572
		 * number between the begining and the end of the result set. For that
573
		 * the ":size" and ":start" placeholders are replaced by the
574
		 * corresponding values from the criteria object. The default values
575
		 * are 0 for the start and 100 for the size value.
576
		 *
577
		 * The SQL statement should conform to the ANSI standard to be
578
		 * compatible with most relational database systems. This also
579
		 * includes using double quotes for table and column names.
580
		 *
581
		 * @param string SQL statement for searching items
582
		 * @since 2014.03
583
		 * @category Developer
584
		 * @see mshop/index/manager/text/standard/aggregate/ansi
585
		 * @see mshop/index/manager/text/standard/cleanup/ansi
586
		 * @see mshop/index/manager/text/standard/count/ansi
587
		 * @see mshop/index/manager/text/standard/insert/ansi
588
		 * @see mshop/index/manager/text/standard/optimize/ansi
589
		 * @see mshop/index/manager/text/standard/text/ansi
590
		 */
591
		$cfgPathSearch = 'mshop/index/manager/text/standard/search';
592
593
		/** mshop/index/manager/text/standard/count/mysql
594
		 * Counts the number of records matched by the given criteria in the database
595
		 *
596
		 * @see mshop/index/manager/text/standard/count/ansi
597
		 */
598
599
		/** mshop/index/manager/text/standard/count/ansi
600
		 * Counts the number of records matched by the given criteria in the database
601
		 *
602
		 * Counts all records matched by the given criteria from the product index
603
		 * database. The records must be from one of the sites that are
604
		 * configured via the context item. If the current site is part of
605
		 * a tree of sites, the statement can count all records from the
606
		 * current site and the complete sub-tree of sites.
607
		 *
608
		 * As the records can normally be limited by criteria from sub-managers,
609
		 * their tables must be joined in the SQL context. This is done by
610
		 * using the "internaldeps" property from the definition of the ID
611
		 * column of the sub-managers. These internal dependencies specify
612
		 * the JOIN between the tables and the used columns for joining. The
613
		 * ":joins" placeholder is then replaced by the JOIN strings from
614
		 * the sub-managers.
615
		 *
616
		 * To limit the records matched, conditions can be added to the given
617
		 * criteria object. It can contain comparisons like column names that
618
		 * must match specific values which can be combined by AND, OR or NOT
619
		 * operators. The resulting string of SQL conditions replaces the
620
		 * ":cond" placeholder before the statement is sent to the database
621
		 * server.
622
		 *
623
		 * Both, the strings for ":joins" and for ":cond" are the same as for
624
		 * the "search" SQL statement.
625
		 *
626
		 * Contrary to the "search" statement, it doesn't return any records
627
		 * but instead the number of records that have been found. As counting
628
		 * thousands of records can be a long running task, the maximum number
629
		 * of counted records is limited for performance reasons.
630
		 *
631
		 * The SQL statement should conform to the ANSI standard to be
632
		 * compatible with most relational database systems. This also
633
		 * includes using double quotes for table and column names.
634
		 *
635
		 * @param string SQL statement for counting items
636
		 * @since 2014.03
637
		 * @category Developer
638
		 * @see mshop/index/manager/text/standard/aggregate/ansi
639
		 * @see mshop/index/manager/text/standard/cleanup/ansi
640
		 * @see mshop/index/manager/text/standard/insert/ansi
641
		 * @see mshop/index/manager/text/standard/optimize/ansi
642
		 * @see mshop/index/manager/text/standard/search/ansi
643
		 * @see mshop/index/manager/text/standard/text/ansi
644
		 */
645
		$cfgPathCount = 'mshop/index/manager/text/standard/count';
646
647
		return $this->searchItemsIndexBase( $search, $ref, $total, $cfgPathSearch, $cfgPathCount );
648
	}
649
650
651
	/**
652
	 * Returns the language IDs available for the current site
653
	 *
654
	 * @return string[] List of ISO language codes
655
	 */
656
	protected function getLanguageIds()
657
	{
658
		if( !isset( $this->languageIds ) )
659
		{
660
			$list = [];
661
			$manager = \Aimeos\MShop::create( $this->getContext(), 'locale' );
662
			$items = $manager->searchItems( $manager->createSearch()->setSlice( 0, 10000 ) );
663
664
			foreach( $items as $item ) {
665
				$list[$item->getLanguageId()] = null;
666
			}
667
668
			$this->languageIds = array_keys( $list );
669
		}
670
671
		return $this->languageIds;
672
	}
673
674
675
	/**
676
	 * Saves the text items referenced indirectly by products
677
	 *
678
	 * @param \Aimeos\MW\DB\Statement\Iface $stmt Prepared SQL statement with place holders
679
	 * @param \Aimeos\MShop\Product\Item\Iface $item Product item containing associated text items
680
	 */
681
	protected function saveTexts( \Aimeos\MW\DB\Statement\Iface $stmt, \Aimeos\MShop\Product\Item\Iface $item )
682
	{
683
		$texts = [];
684
		$date = date( 'Y-m-d H:i:s' );
685
		$siteid = $this->getContext()->getLocale()->getSiteId();
686
687
		foreach( $item->getRefItems( 'text', 'url', 'default' ) as $text ) {
688
			$texts[$text->getLanguageId()]['url'] = \Aimeos\MW\Common\Base::sanitize( $text->getContent() );
689
		}
690
691
		foreach( $item->getRefItems( 'text', 'name', 'default' ) as $text ) {
692
			$texts[$text->getLanguageId()]['name'] = $text->getContent();
693
		}
694
695
		/** mshop/index/manager/text/types
696
		 * List of text types that should be added to the product index
697
		 *
698
		 * By default, all available texts of a product are indexed. This setting
699
		 * allows you to name only those text types that should be added. All
700
		 * others will be left out so products won't be found if users search
701
		 * for words that are part of those skipped texts. This is most useful
702
		 * for avoiding product matches due to texts that should be internal only.
703
		 *
704
		 * @param array|string|null Type name or list of type names, null for all
705
		 * @category Developer
706
		 * @since 2019.04
707
		 */
708
		$types = $this->getContext()->getConfig()->get( 'mshop/index/manager/text/types' );
709
		$products = ( $item->getType() === 'select' ? $item->getRefItems( 'product', null, 'default' ) : [] );
710
		$products[] = $item;
711
712
		foreach( $products as $product )
713
		{
714
			foreach( $this->getLanguageIds() as $langId ) {
715
				$texts[$langId]['content'][] = $product->getCode();
716
			}
717
718
			foreach( $product->getRefItems( 'text', $types ) as $text ) {
719
				$texts[$text->getLanguageId()]['content'][] = $text->getContent();
720
			}
721
		}
722
723
		foreach( $texts as $langId => $map )
724
		{
725
			if( !isset( $map['url'] ) ) {
726
				$map['url'] = \Aimeos\MW\Common\Base::sanitize( $item->getLabel() );
727
			}
728
729
			if( !isset( $map['name'] ) ) {
730
				$map['content'][] = $map['name'] = $item->getLabel();
731
			}
732
733
			$content = join( ' ', $map['content'] );
734
			$this->saveText( $stmt, $item->getId(), $siteid, $langId, $map['url'], $map['name'], $content, $date );
735
		}
736
	}
737
738
739
	/**
740
	 * Saves the text record with given set of parameters.
741
	 *
742
	 * @param \Aimeos\MW\DB\Statement\Iface $stmt Prepared SQL statement with place holders
743
	 * @param string $id ID of the product item
744
	 * @param string $siteid Site ID
745
	 * @param string $lang Two letter ISO language code
746
	 * @param string $url Product name in URL
747
	 * @param string $name Name of the product
748
	 * @param string $content Text content to store
749
	 * @param string $date Current timestamp in "YYYY-MM-DD HH:mm:ss" format
750
	 */
751
	protected function saveText( \Aimeos\MW\DB\Statement\Iface $stmt, $id, $siteid, $lang,
752
		$url, $name, $content, $date )
753
	{
754
		$stmt->bind( 1, $id, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
755
		$stmt->bind( 2, $lang );
756
		$stmt->bind( 3, $url );
757
		$stmt->bind( 4, $name );
758
		$stmt->bind( 5, strtolower( $content ) ); // for case insensitive searches
759
		$stmt->bind( 6, $date ); //mtime
760
		$stmt->bind( 7, $siteid, \Aimeos\MW\DB\Statement\Base::PARAM_INT );
761
762
		try {
763
			$stmt->execute()->finish();
764
		} catch( \Aimeos\MW\DB\Exception $e ) { ; } // Ignore duplicates
765
	}
766
767
768
	/**
769
	 * Returns the list of sub-managers available for the index attribute manager.
770
	 *
771
	 * @return \Aimeos\MShop\Index\Manager\Iface Associative list of the sub-domain as key and the manager object as value
772
	 */
773
	protected function getSubManagers()
774
	{
775
		if( $this->subManagers === null )
776
		{
777
			$this->subManagers = [];
778
779
			/** mshop/index/manager/text/submanagers
780
			 * A list of sub-manager names used for indexing associated items to texts
781
			 *
782
			 * All items referenced by a product (e.g. texts, prices, media,
783
			 * etc.) are added to the product index via specialized index
784
			 * managers. You can add the name of new sub-managers to add more
785
			 * data to the index or remove existing ones if you don't want to
786
			 * index that data at all.
787
			 *
788
			 * This option configures the sub-managers that cares about
789
			 * indexing data associated to product texts.
790
			 *
791
			 * @param string List of index sub-manager names
792
			 * @since 2014.09
793
			 * @category User
794
			 * @category Developer
795
			 * @see mshop/index/manager/standard/submanagers
796
			 */
797
			$path = 'mshop/index/manager/text/submanagers';
798
799
			foreach( $this->getContext()->getConfig()->get( $path, [] ) as $domain ) {
800
				$this->subManagers[$domain] = $this->getObject()->getSubManager( $domain );
801
			}
802
803
			return $this->subManagers;
804
		}
805
806
		return $this->subManagers;
807
	}
808
}
809