Passed
Push — master ( 4b2a91...859578 )
by Aimeos
05:39
created

Standard::saveItems()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 5
rs 10
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 Index
9
 */
10
11
12
namespace Aimeos\MShop\Index\Manager;
13
14
15
/**
16
 * Index index manager for searching in product tables.
17
 *
18
 * @package MShop
19
 * @subpackage Index
20
 */
21
class Standard
22
	extends \Aimeos\MShop\Index\Manager\DBBase
23
	implements \Aimeos\MShop\Index\Manager\Iface, \Aimeos\MShop\Common\Manager\Factory\Iface
24
{
25
	private $subManagers;
26
27
28
	/**
29
	 * Counts the number products that are available for the values of the given key.
30
	 *
31
	 * @param \Aimeos\MW\Criteria\Iface $search Search criteria
32
	 * @param string $key Search key (usually the ID) to aggregate products for
33
	 * @return integer[] List of ID values as key and the number of counted products as value
34
	 */
35
	public function aggregate( \Aimeos\MW\Criteria\Iface $search, $key )
36
	{
37
		/** mshop/index/manager/standard/aggregate/mysql
38
		 * Counts the number of records grouped by the values in the key column and matched by the given criteria
39
		 *
40
		 * @see mshop/index/manager/standard/aggregate/ansi
41
		 */
42
43
		/** mshop/index/manager/standard/aggregate/ansi
44
		 * Counts the number of records grouped by the values in the key column and matched by the given criteria
45
		 *
46
		 * Groups all records by the values in the key column and counts their
47
		 * occurence. The matched records can be limited by the given criteria
48
		 * from the order database. The records must be from one of the sites
49
		 * that are configured via the context item. If the current site is part
50
		 * of a tree of sites, the statement can count all records from the
51
		 * current site and the complete sub-tree of sites.
52
		 *
53
		 * As the records can normally be limited by criteria from sub-managers,
54
		 * their tables must be joined in the SQL context. This is done by
55
		 * using the "internaldeps" property from the definition of the ID
56
		 * column of the sub-managers. These internal dependencies specify
57
		 * the JOIN between the tables and the used columns for joining. The
58
		 * ":joins" placeholder is then replaced by the JOIN strings from
59
		 * the sub-managers.
60
		 *
61
		 * To limit the records matched, conditions can be added to the given
62
		 * criteria object. It can contain comparisons like column names that
63
		 * must match specific values which can be combined by AND, OR or NOT
64
		 * operators. The resulting string of SQL conditions replaces the
65
		 * ":cond" placeholder before the statement is sent to the database
66
		 * server.
67
		 *
68
		 * This statement doesn't return any records. Instead, it returns pairs
69
		 * of the different values found in the key column together with the
70
		 * number of records that have been found for that key values.
71
		 *
72
		 * The SQL statement should conform to the ANSI standard to be
73
		 * compatible with most relational database systems. This also
74
		 * includes using double quotes for table and column names.
75
		 *
76
		 * @param string SQL statement for aggregating order items
77
		 * @since 2014.09
78
		 * @category Developer
79
		 * @see mshop/index/manager/standard/count/ansi
80
		 * @see mshop/index/manager/standard/optimize/ansi
81
		 * @see mshop/index/manager/standard/search/ansi
82
		 */
83
		return $this->aggregateBase( $search, $key, 'mshop/index/manager/standard/aggregate', array( 'product' ) );
84
	}
85
86
87
	/**
88
	 * Removes multiple items from the index.
89
	 *
90
	 * @param string[] $ids list of product IDs
91
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
92
	 */
93
	public function deleteItems( array $ids )
94
	{
95
		$this->getManager()->deleteItems( $ids );
96
		return $this->clearItems( $ids )->clearCache( $ids );
97
	}
98
99
100
	/**
101
	 * Returns the available manager types
102
	 *
103
	 * @param boolean $withsub Return also the resource type of sub-managers if true
104
	 * @return array Type of the manager and submanagers, subtypes are separated by slashes
105
	 */
106
	public function getResourceType( $withsub = true )
107
	{
108
		return $this->getResourceTypeBase( 'index', 'mshop/index/manager/submanagers', [], $withsub );
109
	}
110
111
112
	/**
113
	 * Returns a list of objects describing the available criterias for searching.
114
	 *
115
	 * @param boolean $withsub Return also attributes of sub-managers if true
116
	 * @return \Aimeos\MW\Criteria\Attribute\Iface[] List of search attribute items
117
	 */
118
	public function getSearchAttributes( $withsub = true )
119
	{
120
		$list = parent::getSearchAttributes( $withsub );
121
122
		/** mshop/index/manager/standard/submanagers
123
		 * Replaced by mshop/index/manager/submanagers since 2016.01
124
		 *
125
		 * @see mshop/index/manager/standard/submanagers
126
		 */
127
		$path = 'mshop/index/manager/submanagers';
128
129
		return $list + $this->getSearchAttributesBase( [], $path, [], $withsub );
130
	}
131
132
133
	/**
134
	 * Returns a new manager for product extensions.
135
	 *
136
	 * @param string $manager Name of the sub manager type in lower case
137
	 * @param string|null $name Name of the implementation, will be from configuration (or Default) if null
138
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager for different extensions, e.g stock, tags, locations, etc.
139
	 */
140
	public function getSubManager( $manager, $name = null )
141
	{
142
		return $this->getSubManagerBase( 'index', $manager, $name );
143
	}
144
145
146
	/**
147
	 * Optimizes the index if necessary.
148
	 * Execution of this operation can take a very long time and shouldn't be
149
	 * called through a web server enviroment.
150
	 *
151
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
152
	 */
153
	public function optimize()
154
	{
155
		/** mshop/index/manager/standard/optimize/mysql
156
		 * Optimizes the stored product data for retrieving the records faster
157
		 *
158
		 * @see mshop/index/manager/standard/optimize/ansi
159
		 */
160
161
		/** mshop/index/manager/standard/optimize/ansi
162
		 * Optimizes the stored product data for retrieving the records faster
163
		 *
164
		 * The SQL statement should reorganize the data in the DBMS storage to
165
		 * optimize access to the records of the table or tables. Some DBMS
166
		 * offer specialized statements to optimize indexes and records. This
167
		 * statement doesn't return any records.
168
		 *
169
		 * The SQL statement should conform to the ANSI standard to be
170
		 * compatible with most relational database systems. This also
171
		 * includes using double quotes for table and column names.
172
		 *
173
		 * @param string SQL statement for optimizing the stored product data
174
		 * @since 2014.09
175
		 * @category Developer
176
		 * @see mshop/index/manager/standard/count/ansi
177
		 * @see mshop/index/manager/standard/search/ansi
178
		 * @see mshop/index/manager/standard/aggregate/ansi
179
		 */
180
		return $this->optimizeBase( 'mshop/index/manager/standard/optimize' );
181
	}
182
183
184
	/**
185
	 * Removes old entries from the storage.
186
	 *
187
	 * @param string[] $siteids List of IDs for sites whose entries should be deleted
188
	* @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
189
	 */
190
	public function cleanup( array $siteids )
191
	{
192
		foreach( $this->getSubManagers() as $submanager ) {
193
			$submanager->cleanup( $siteids );
194
		}
195
196
		return $this;
197
	}
198
199
200
	/**
201
	 * Removes all entries not touched after the given timestamp in the index.
202
	 * This can be a long lasting operation.
203
	 *
204
	 * @param string $timestamp Timestamp in ISO format (YYYY-MM-DD HH:mm:ss)
205
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
206
	 */
207
	public function cleanupIndex( $timestamp )
208
	{
209
		foreach( $this->getSubManagers() as $submanager ) {
210
			$submanager->cleanupIndex( $timestamp );
211
		}
212
213
		return $this;
214
	}
215
216
217
	/**
218
	 * Rebuilds the index for searching products or specified list of products.
219
	 * This can be a long lasting operation.
220
	 *
221
	 * @param \Aimeos\MShop\Product\Item\Iface[] $items Associative list of product IDs as keys and items as values
222
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
223
	 */
224
	public function rebuildIndex( array $items = [] )
225
	{
226
		$context = $this->getContext();
227
		$config = $context->getConfig();
228
229
		/** mshop/index/manager/standard/chunksize
230
		 * Number of products that should be indexed at once
231
		 *
232
		 * When rebuilding the product index, several products are updated at
233
		 * once within a transaction. This speeds up the time that is needed
234
		 * for reindexing.
235
		 *
236
		 * Usually, the more products are updated in one bunch, the faster the
237
		 * process of rebuilding the index will be up to a certain limit. The
238
		 * downside of big bunches is a higher memory consumption that can
239
		 * exceed the maximum allowed memory of the process.
240
		 *
241
		 * @param integer Number of products
242
		 * @since 2014.09
243
		 * @category User
244
		 * @category Developer
245
		 * @see mshop/index/manager/standard/domains
246
		 * @see mshop/index/manager/standard/index
247
		 * @see mshop/index/manager/standard/subdomains
248
		 * @see mshop/index/manager/submanagers
249
		 */
250
		$size = $config->get( 'mshop/index/manager/standard/chunksize', 1000 );
251
252
		/** mshop/index/manager/standard/index
253
		 * Index mode for products which determines what products are added to the index
254
		 *
255
		 * By default, only products that have been added to a category are
256
		 * part of the index. Thus, it's possible to have special products like
257
		 * rebate products that are necessary if you use coupon codes in your
258
		 * shop but won't be found by e.g. when searching for products.
259
		 *
260
		 * Alternatively, you can add all products to the index, even those
261
		 * which are not listed in any category. This mode should only be
262
		 * used in special cases when you have no rebate or similar products
263
		 * that shouldn't be found by users.
264
		 *
265
		 * @param integer Number of products
266
		 * @since 2014.09
267
		 * @category User
268
		 * @category Developer
269
		 * @see mshop/index/manager/standard/chunksize
270
		 * @see mshop/index/manager/standard/domains
271
		 * @see mshop/index/manager/standard/subdomains
272
		 * @see mshop/index/manager/submanagers
273
		 * @deprecated 2020.01
274
		 */
275
		$mode = $config->get( 'mshop/index/manager/standard/index', 'categorized' );
276
277
		/** mshop/index/manager/standard/domains
278
		 * A list of domain names whose items should be retrieved together with the product
279
		 *
280
		 * To speed up the indexing process, items like texts, prices, media,
281
		 * attributes etc. which have been associated to products can be
282
		 * retrieved together with the products.
283
		 *
284
		 * Please note that the index submanagers expect that the items
285
		 * associated to the products are fetched together with the products.
286
		 * Thus, if you leave out a domain, this information won't be part
287
		 * of the indexed product and therefore won't be found when searching
288
		 * the index.
289
		 *
290
		 * @param string List of MShop domain names
291
		 * @since 2014.09
292
		 * @category Developer
293
		 * @see mshop/index/manager/standard/chunksize
294
		 * @see mshop/index/manager/standard/index
295
		 * @see mshop/index/manager/standard/subdomains
296
		 * @see mshop/index/manager/submanagers
297
		 */
298
		$domains = $config->get( 'mshop/index/manager/standard/domains', [] );
299
300
		$manager = \Aimeos\MShop::create( $context, 'product' );
301
		$search = $manager->createSearch();
302
		$search->setSortations( array( $search->sort( '+', 'product.id' ) ) );
303
		$defaultConditions = $search->getConditions();
304
305
		// @deprecated 2020.01
306
		$prodIds = [];
307
		foreach( $items as $item ) {
308
			$prodIds[] = $item->getId(); // don't rely on array keys
309
		}
310
311
312
		// @deprecated 2020.01
313
		if( $mode === 'all' )
314
		{
315
			if( !empty( $prodIds ) )
316
			{
317
				$expr = array( $search->compare( '==', 'product.id', $prodIds ), $defaultConditions );
318
				$search->setConditions( $search->combine( '&&', $expr ) );
319
			}
320
321
			$this->writeIndex( $search, $domains, $size );
322
			return;
323
		}
324
325
326
		// index categorized product items only
327
		$catalogListManager = \Aimeos\MShop::create( $context, 'catalog/lists' );
328
		$catalogSearch = $catalogListManager->createSearch( true );
329
330
		$expr = array(
331
			$catalogSearch->compare( '==', 'catalog.lists.domain', 'product' ),
332
			$catalogSearch->getConditions(),
333
		);
334
335
		if( !empty( $prodIds ) ) {
336
			$expr[] = $catalogSearch->compare( '==', 'catalog.lists.refid', $prodIds );
337
		}
338
339
		$catalogSearch->setConditions( $catalogSearch->combine( '&&', $expr ) );
340
		$catalogSearch->setSortations( array( $catalogSearch->sort( '+', 'catalog.lists.refid' ) ) );
341
342
		$start = 0;
343
344
		do
345
		{
346
			$catalogSearch->setSlice( $start, $size );
347
			$result = $catalogListManager->aggregate( $catalogSearch, 'catalog.lists.refid' );
348
349
			$expr = array(
350
				$search->compare( '==', 'product.id', array_keys( $result ) ),
351
				$defaultConditions,
352
			);
353
			$search->setConditions( $search->combine( '&&', $expr ) );
354
355
			$this->writeIndex( $search, $domains, $size );
356
357
			$start += $size;
358
		}
359
		while( count( $result ) === $size );
360
361
		return $this;
362
	}
363
364
365
	/**
366
	 * Adds a new product to the storage or updates an existing one.
367
	 *
368
	 * @param \Aimeos\MShop\Product\Item\Iface $item Product item that should be saved to the storage
369
	 * @param boolean $fetch True if the new ID should be returned in the item
370
	 * @return \Aimeos\MShop\Product\Item\Iface Updated item including the generated ID
371
	 */
372
	public function saveItem( \Aimeos\MShop\Common\Item\Iface $item, $fetch = true )
373
	{
374
		$item = parent::saveItem( $item, $fetch );
375
		$this->rebuildIndex( [$item->getId() => $item] );
376
		return $item;
377
	}
378
379
380
	/**
381
	 * Adds or updates a list of product items.
382
	 *
383
	 * @param \Aimeos\MShop\Common\Item\Iface[] $items List of item object whose data should be saved
384
	 * @param boolean $fetch True if the new ID should be returned in the item
385
	 * @return \Aimeos\MShop\Common\Item\Iface[] Saved item objects
386
	 */
387
	public function saveItems( array $items, $fetch = true )
388
	{
389
		$items = parent::saveItems( $items, $fetch );
390
		$this->rebuildIndex( $items );
391
		return $items;
392
	}
393
394
395
	/**
396
	 * Searches for items matching the given criteria.
397
	 *
398
	 * @param \Aimeos\MW\Criteria\Iface $search Search criteria object
399
	 * @param string[] $ref List of domains to fetch list items and referenced items for
400
	 * @param integer|null &$total Number of items that are available in total
401
	 * @return array List of items implementing \Aimeos\MShop\Product\Item\Iface
402
	 */
403
	public function searchItems( \Aimeos\MW\Criteria\Iface $search, array $ref = [], &$total = null )
404
	{
405
		/** mshop/index/manager/standard/search/mysql
406
		 * Retrieves the records matched by the given criteria in the database
407
		 *
408
		 * @see mshop/index/manager/standard/search/ansi
409
		 */
410
411
		/** mshop/index/manager/standard/search/ansi
412
		 * Retrieves the records matched by the given criteria in the database
413
		 *
414
		 * Fetches the records matched by the given criteria from the order
415
		 * database. The records must be from one of the sites that are
416
		 * configured via the context item. If the current site is part of
417
		 * a tree of sites, the SELECT statement can retrieve all records
418
		 * from the current site and the complete sub-tree of sites.
419
		 *
420
		 * As the records can normally be limited by criteria from sub-managers,
421
		 * their tables must be joined in the SQL context. This is done by
422
		 * using the "internaldeps" property from the definition of the ID
423
		 * column of the sub-managers. These internal dependencies specify
424
		 * the JOIN between the tables and the used columns for joining. The
425
		 * ":joins" placeholder is then replaced by the JOIN strings from
426
		 * the sub-managers.
427
		 *
428
		 * To limit the records matched, conditions can be added to the given
429
		 * criteria object. It can contain comparisons like column names that
430
		 * must match specific values which can be combined by AND, OR or NOT
431
		 * operators. The resulting string of SQL conditions replaces the
432
		 * ":cond" placeholder before the statement is sent to the database
433
		 * server.
434
		 *
435
		 * If the records that are retrieved should be ordered by one or more
436
		 * columns, the generated string of column / sort direction pairs
437
		 * replaces the ":order" placeholder. In case no ordering is required,
438
		 * the complete ORDER BY part including the "\/*-orderby*\/...\/*orderby-*\/"
439
		 * markers is removed to speed up retrieving the records. Columns of
440
		 * sub-managers can also be used for ordering the result set but then
441
		 * no index can be used.
442
		 *
443
		 * The number of returned records can be limited and can start at any
444
		 * number between the begining and the end of the result set. For that
445
		 * the ":size" and ":start" placeholders are replaced by the
446
		 * corresponding values from the criteria object. The default values
447
		 * are 0 for the start and 100 for the size value.
448
		 *
449
		 * The SQL statement should conform to the ANSI standard to be
450
		 * compatible with most relational database systems. This also
451
		 * includes using double quotes for table and column names.
452
		 *
453
		 * @param string SQL statement for searching items
454
		 * @since 2014.03
455
		 * @category Developer
456
		 * @see mshop/index/manager/standard/count/ansi
457
		 * @see mshop/index/manager/standard/optimize/ansi
458
		 * @see mshop/index/manager/standard/aggregate/ansi
459
		 */
460
		$cfgPathSearch = 'mshop/index/manager/standard/search';
461
462
		/** mshop/index/manager/standard/count/mysql
463
		 * Counts the number of records matched by the given criteria in the database
464
		 *
465
		 * @see mshop/index/manager/standard/count/ansi
466
		 */
467
468
		/** mshop/index/manager/standard/count/ansi
469
		 * Counts the number of records matched by the given criteria in the database
470
		 *
471
		 * Counts all records matched by the given criteria from the order
472
		 * database. The records must be from one of the sites that are
473
		 * configured via the context item. If the current site is part of
474
		 * a tree of sites, the statement can count all records from the
475
		 * current site and the complete sub-tree of sites.
476
		 *
477
		 * As the records can normally be limited by criteria from sub-managers,
478
		 * their tables must be joined in the SQL context. This is done by
479
		 * using the "internaldeps" property from the definition of the ID
480
		 * column of the sub-managers. These internal dependencies specify
481
		 * the JOIN between the tables and the used columns for joining. The
482
		 * ":joins" placeholder is then replaced by the JOIN strings from
483
		 * the sub-managers.
484
		 *
485
		 * To limit the records matched, conditions can be added to the given
486
		 * criteria object. It can contain comparisons like column names that
487
		 * must match specific values which can be combined by AND, OR or NOT
488
		 * operators. The resulting string of SQL conditions replaces the
489
		 * ":cond" placeholder before the statement is sent to the database
490
		 * server.
491
		 *
492
		 * Both, the strings for ":joins" and for ":cond" are the same as for
493
		 * the "search" SQL statement.
494
		 *
495
		 * Contrary to the "search" statement, it doesn't return any records
496
		 * but instead the number of records that have been found. As counting
497
		 * thousands of records can be a long running task, the maximum number
498
		 * of counted records is limited for performance reasons.
499
		 *
500
		 * The SQL statement should conform to the ANSI standard to be
501
		 * compatible with most relational database systems. This also
502
		 * includes using double quotes for table and column names.
503
		 *
504
		 * @param string SQL statement for counting items
505
		 * @since 2014.03
506
		 * @category Developer
507
		 * @see mshop/index/manager/standard/search/ansi
508
		 * @see mshop/index/manager/standard/optimize/ansi
509
		 * @see mshop/index/manager/standard/aggregate/ansi
510
		 */
511
		$cfgPathCount = 'mshop/index/manager/standard/count';
512
513
		return $this->searchItemsIndexBase( $search, $ref, $total, $cfgPathSearch, $cfgPathCount );
514
	}
515
516
517
	/**
518
	 * Removes multiple items from the index.
519
	 *
520
	 * @param string[] $ids list of product IDs
521
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
522
	 */
523
	protected function clearItems( array $ids )
524
	{
525
		foreach( $this->getSubManagers() as $submanager ) {
526
			$submanager->deleteItems( $ids );
527
		}
528
529
		return $this;
530
	}
531
532
533
	/**
534
	 * Deletes the cache entries using the given product IDs.
535
	 *
536
	 * @param string[] $ids List of product IDs
537
	 * @return \Aimeos\MShop\Index\Manager\Iface Manager object for chaining method calls
538
	 */
539
	protected function clearCache( array $ids )
540
	{
541
		$tags = [];
542
543
		foreach( $ids as $id ) {
544
			$tags[] = 'product-' . $id;
545
		}
546
547
		$this->getContext()->getCache()->deleteByTags( $tags );
548
		return $this;
549
	}
550
551
552
	/**
553
	 * Re-writes the index entries for all products that are search result of given criteria
554
	 *
555
	 * @param \Aimeos\MW\Criteria\Iface $search Search criteria
556
	 * @param string[] $domains List of domains to be
557
	 * @param integer $size Size of a chunk of products to handle at a time
558
	 */
559
	protected function writeIndex( \Aimeos\MW\Criteria\Iface $search, array $domains, $size )
560
	{
561
		$manager = \Aimeos\MShop::create( $this->getContext(), 'product' );
562
		$submanagers = $this->getSubManagers();
563
		$start = 0;
564
565
		do
566
		{
567
			$search->setSlice( $start, $size );
568
			$products = $manager->searchItems( $search, $domains );
569
			$prodIds = array_keys( $products );
570
571
			try
572
			{
573
				$this->begin();
574
575
				$this->clearItems( $prodIds );
576
577
				foreach( $submanagers as $submanager ) {
578
					$submanager->rebuildIndex( $products );
579
				}
580
581
				$this->commit();
582
			}
583
			catch( \Exception $e )
584
			{
585
				$this->rollback();
586
				throw $e;
587
			}
588
589
			$this->clearCache( $prodIds );
590
591
			$count = count( $products );
592
			$start += $count;
593
		}
594
		while( $count == $search->getSliceSize() );
595
	}
596
597
598
	/**
599
	 * Returns the list of sub-managers available for the index attribute manager.
600
	 *
601
	 * @return array Associative list of the sub-domain as key and the manager object as value
602
	 */
603
	protected function getSubManagers()
604
	{
605
		if( $this->subManagers === null )
606
		{
607
			$this->subManagers = [];
608
609
			/** mshop/index/manager/submanagers
610
			 * A list of sub-manager names used for indexing associated items
611
			 *
612
			 * All items referenced by a product (e.g. texts, prices, media,
613
			 * etc.) are added to the product index via specialized index
614
			 * managers. You can add the name of new sub-managers to add more
615
			 * data to the index or remove existing ones if you don't want to
616
			 * index that data at all.
617
			 *
618
			 * Caution: Please note that the list of sub-manager names should
619
			 * correspond to the list of domains that are fetched together with
620
			 * the products as the sub-manager depends on the items being
621
			 * retrieved there and fetching items that won't be indexed is a
622
			 * waste of resources.
623
			 *
624
			 * @param string List of index sub-manager names
625
			 * @since 2016.02
626
			 * @category User
627
			 * @category Developer
628
			 * @see mshop/index/manager/standard/chunksize
629
			 * @see mshop/index/manager/standard/domains
630
			 * @see mshop/index/manager/standard/index
631
			 * @see mshop/index/manager/standard/subdomains
632
			 */
633
			$path = 'mshop/index/manager/submanagers';
634
635
			foreach( $this->getContext()->getConfig()->get( $path, [] ) as $domain ) {
636
				$this->subManagers[$domain] = $this->getObject()->getSubManager( $domain );
637
			}
638
639
			return $this->subManagers;
640
		}
641
642
		return $this->subManagers;
643
	}
644
}
645