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

817
			$this->saveText( $stmt, /** @scrutinizer ignore-type */ $item->getId(), $siteid, $langId, $url, $map['name'], $content, $date );
Loading history...
818
		}
819
	}
820
821
822
	/**
823
	 * Saves the text record with given set of parameters.
824
	 *
825
	 * @param \Aimeos\Base\DB\Statement\Iface $stmt Prepared SQL statement with place holders
826
	 * @param string $id ID of the product item
827
	 * @param string $siteid Site ID
828
	 * @param string $lang Two letter ISO language code
829
	 * @param string $url Product name in URL
830
	 * @param string $name Name of the product
831
	 * @param string $content Text content to store
832
	 * @param string $date Current timestamp in "YYYY-MM-DD HH:mm:ss" format
833
	 */
834
	protected function saveText( \Aimeos\Base\DB\Statement\Iface $stmt, string $id, string $siteid, string $lang,
835
		string $url, string $name, string $content, string $date )
836
	{
837
		$stmt->bind( 1, $id, \Aimeos\Base\DB\Statement\Base::PARAM_INT );
838
		$stmt->bind( 2, $lang );
839
		$stmt->bind( 3, $url );
840
		$stmt->bind( 4, $name );
841
		$stmt->bind( 5, mb_strtolower( $content ) ); // for case insensitive searches
842
		$stmt->bind( 6, $date ); //mtime
843
		$stmt->bind( 7, $siteid );
844
845
		try {
846
			$stmt->execute()->finish();
847
		} catch( \Aimeos\Base\DB\Exception $e ) { ; } // Ignore duplicates
848
	}
849
850
851
	/**
852
	 * Returns the list of sub-managers available for the index attribute manager.
853
	 *
854
	 * @return \Aimeos\MShop\Index\Manager\Iface[] Associative list of the sub-domain as key and the manager object as value
855
	 */
856
	protected function getSubManagers() : array
857
	{
858
		if( $this->subManagers === null )
859
		{
860
			$this->subManagers = [];
861
			$config = $this->context()->config();
862
863
			/** mshop/index/manager/text/submanagers
864
			 * A list of sub-manager names used for indexing associated items to texts
865
			 *
866
			 * All items referenced by a product (e.g. texts, prices, media,
867
			 * etc.) are added to the product index via specialized index
868
			 * managers. You can add the name of new sub-managers to add more
869
			 * data to the index or remove existing ones if you don't want to
870
			 * index that data at all.
871
			 *
872
			 * This option configures the sub-managers that cares about
873
			 * indexing data associated to product texts.
874
			 *
875
			 * @param string List of index sub-manager names
876
			 * @since 2014.09
877
			 * @category User
878
			 * @category Developer
879
			 * @see mshop/index/manager/submanagers
880
			 */
881
			foreach( $config->get( 'mshop/index/manager/text/submanagers', [] ) as $domain )
882
			{
883
				$name = $config->get( 'mshop/index/manager/text/' . $domain . '/name' );
884
				$this->subManagers[$domain] = $this->object()->getSubManager( $domain, $name );
885
			}
886
887
			return $this->subManagers;
888
		}
889
890
		return $this->subManagers;
891
	}
892
}
893