Completed
Push — master ( af321b...5b68b0 )
by Aimeos
05:16
created

Standard::getHeader()   B

Complexity

Conditions 7
Paths 23

Size

Total Lines 68
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 26
c 1
b 0
f 0
nc 23
nop 1
dl 0
loc 68
rs 8.5706

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Metaways Infosystems GmbH, 2012
6
 * @copyright Aimeos (aimeos.org), 2015-2020
7
 * @package Client
8
 * @subpackage Html
9
 */
10
11
12
namespace Aimeos\Client\Html\Catalog\Detail;
13
14
15
/**
16
 * Default implementation of catalog detail section HTML clients.
17
 *
18
 * @package Client
19
 * @subpackage Html
20
 */
21
class Standard
22
	extends \Aimeos\Client\Html\Catalog\Base
23
	implements \Aimeos\Client\Html\Common\Client\Factory\Iface
24
{
25
	/** client/html/catalog/detail/standard/subparts
26
	 * List of HTML sub-clients rendered within the catalog detail section
27
	 *
28
	 * The output of the frontend is composed of the code generated by the HTML
29
	 * clients. Each HTML client can consist of serveral (or none) sub-clients
30
	 * that are responsible for rendering certain sub-parts of the output. The
31
	 * sub-clients can contain HTML clients themselves and therefore a
32
	 * hierarchical tree of HTML clients is composed. Each HTML client creates
33
	 * the output that is placed inside the container of its parent.
34
	 *
35
	 * At first, always the HTML code generated by the parent is printed, then
36
	 * the HTML code of its sub-clients. The order of the HTML sub-clients
37
	 * determines the order of the output of these sub-clients inside the parent
38
	 * container. If the configured list of clients is
39
	 *
40
	 *  array( "subclient1", "subclient2" )
41
	 *
42
	 * you can easily change the order of the output by reordering the subparts:
43
	 *
44
	 *  client/html/<clients>/subparts = array( "subclient1", "subclient2" )
45
	 *
46
	 * You can also remove one or more parts if they shouldn't be rendered:
47
	 *
48
	 *  client/html/<clients>/subparts = array( "subclient1" )
49
	 *
50
	 * As the clients only generates structural HTML, the layout defined via CSS
51
	 * should support adding, removing or reordering content by a fluid like
52
	 * design.
53
	 *
54
	 * @param array List of sub-client names
55
	 * @since 2014.03
56
	 * @category Developer
57
	 */
58
	private $subPartPath = 'client/html/catalog/detail/standard/subparts';
59
60
	/** client/html/catalog/detail/service/name
61
	 * Name of the shipping cost part used by the catalog detail client implementation
62
	 *
63
	 * Use "Myname" if your class is named "\Aimeos\Client\Html\Catalog\Detail\Service\Myname".
64
	 * The name is case-sensitive and you should avoid camel case names like "MyName".
65
	 *
66
	 * @param string Last part of the client class name
67
	 * @since 2017.01
68
	 * @category Developer
69
	 */
70
71
	/** client/html/catalog/detail/seen/name
72
	 * Name of the seen part used by the catalog detail client implementation
73
	 *
74
	 * Use "Myname" if your class is named "\Aimeos\Client\Html\Catalog\Detail\Seen\Myname".
75
	 * The name is case-sensitive and you should avoid camel case names like "MyName".
76
	 *
77
	 * @param string Last part of the client class name
78
	 * @since 2014.03
79
	 * @category Developer
80
	 */
81
82
	/** client/html/catalog/detail/supplier/name
83
	 * Name of the supplier part used by the catalog detail client implementation
84
	 *
85
	 * Use "Myname" if your class is named "\Aimeos\Client\Html\Catalog\Detail\Supplier\Myname".
86
	 * The name is case-sensitive and you should avoid camel case names like "MyName".
87
	 *
88
	 * @param string Last part of the client class name
89
	 * @since 2014.03
90
	 * @category Developer
91
	 */
92
	private $subPartNames = ['seen', 'service', 'supplier'];
93
94
	private $tags = [];
95
	private $expire;
96
	private $view;
97
98
99
	/**
100
	 * Returns the HTML code for insertion into the body.
101
	 *
102
	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
103
	 * @return string HTML code
104
	 */
105
	public function getBody( string $uid = '' ) : string
106
	{
107
		$prefixes = ['d'];
108
		$view = $this->getView();
109
		$context = $this->getContext();
110
111
		$code = $context->getConfig()->get( 'client/html/catalog/detail/prodcode-default' );
112
		$id = $context->getConfig()->get( 'client/html/catalog/detail/prodid-default', $code );
113
114
		if( !$view->param( 'd_prodid', $id ) && !$view->param( 'd_name' ) ) {
115
			return '';
116
		}
117
118
		/** client/html/catalog/detail/cache
119
		 * Enables or disables caching only for the catalog detail component
120
		 *
121
		 * Disable caching for components can be useful if you would have too much
122
		 * entries to cache or if the component contains non-cacheable parts that
123
		 * can't be replaced using the modifyBody() and modifyHeader() methods.
124
		 *
125
		 * @param boolean True to enable caching, false to disable
126
		 * @category Developer
127
		 * @category User
128
		 * @see client/html/catalog/filter/cache
129
		 * @see client/html/catalog/lists/cache
130
		 * @see client/html/catalog/stage/cache
131
		 */
132
133
		/** client/html/catalog/detail
134
		 * All parameters defined for the catalog detail component and its subparts
135
		 *
136
		 * This returns all settings related to the detail component.
137
		 * Please refer to the single settings for details.
138
		 *
139
		 * @param array Associative list of name/value settings
140
		 * @category Developer
141
		 * @see client/html/catalog#detail
142
		 */
143
		$confkey = 'client/html/catalog/detail';
144
145
		if( ( $html = $this->getCached( 'body', $uid, $prefixes, $confkey ) ) === null )
146
		{
147
			/** client/html/catalog/detail/standard/template-body
148
			 * Relative path to the HTML body template of the catalog detail client.
149
			 *
150
			 * The template file contains the HTML code and processing instructions
151
			 * to generate the result shown in the body of the frontend. The
152
			 * configuration string is the path to the template file relative
153
			 * to the templates directory (usually in client/html/templates).
154
			 *
155
			 * You can overwrite the template file configuration in extensions and
156
			 * provide alternative templates. These alternative templates should be
157
			 * named like the default one but with the string "standard" replaced by
158
			 * an unique name. You may use the name of your project for this. If
159
			 * you've implemented an alternative client class as well, "standard"
160
			 * should be replaced by the name of the new class.
161
			 *
162
			 * @param string Relative path to the template creating code for the HTML page body
163
			 * @since 2014.03
164
			 * @category Developer
165
			 * @see client/html/catalog/detail/standard/template-header
166
			 */
167
			$tplconf = 'client/html/catalog/detail/standard/template-body';
168
			$default = 'catalog/detail/body-standard';
169
170
			try
171
			{
172
				if( !isset( $this->view ) ) {
173
					$view = $this->view = $this->getObject()->addData( $view, $this->tags, $this->expire );
174
				}
175
176
				$output = '';
177
				foreach( $this->getSubClients() as $subclient ) {
178
					$output .= $subclient->setView( $view )->getBody( $uid );
179
				}
180
				$view->detailBody = $output;
181
182
				$html = $view->render( $view->config( $tplconf, $default ) );
183
				$this->setCached( 'body', $uid, $prefixes, $confkey, $html, $this->tags, $this->expire );
184
185
				return $html;
186
			}
187
			catch( \Aimeos\Client\Html\Exception $e )
188
			{
189
				$error = array( $context->getI18n()->dt( 'client', $e->getMessage() ) );
190
				$view->detailErrorList = array_merge( $view->get( 'detailErrorList', [] ), $error );
191
			}
192
			catch( \Aimeos\Controller\Frontend\Exception $e )
193
			{
194
				$error = array( $context->getI18n()->dt( 'controller/frontend', $e->getMessage() ) );
195
				$view->detailErrorList = array_merge( $view->get( 'detailErrorList', [] ), $error );
196
			}
197
			catch( \Aimeos\MShop\Exception $e )
198
			{
199
				$error = array( $context->getI18n()->dt( 'mshop', $e->getMessage() ) );
200
				$view->detailErrorList = array_merge( $view->get( 'detailErrorList', [] ), $error );
201
			}
202
			catch( \Exception $e )
203
			{
204
				$error = array( $context->getI18n()->dt( 'client', 'A non-recoverable error occured' ) );
205
				$view->detailErrorList = array_merge( $view->get( 'detailErrorList', [] ), $error );
206
				$this->logException( $e );
207
			}
208
209
			$html = $view->render( $view->config( $tplconf, $default ) );
210
		}
211
		else
212
		{
213
			$html = $this->modifyBody( $html, $uid );
214
		}
215
216
		return $html;
217
	}
218
219
220
	/**
221
	 * Returns the HTML string for insertion into the header.
222
	 *
223
	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
224
	 * @return string|null String including HTML tags for the header on error
225
	 */
226
	public function getHeader( string $uid = '' ) : ?string
227
	{
228
		$prefixes = ['d'];
229
		$view = $this->getView();
230
		$context = $this->getContext();
231
		$confkey = 'client/html/catalog/detail';
232
233
		$code = $context->getConfig()->get( 'client/html/catalog/detail/prodcode-default' );
234
		$id = $context->getConfig()->get( 'client/html/catalog/detail/prodid-default', $code );
235
236
		if( !$view->param( 'd_prodid', $id ) && !$view->param( 'd_name' ) ) {
237
			return '';
238
		}
239
240
		if( ( $html = $this->getCached( 'header', $uid, $prefixes, $confkey ) ) === null )
241
		{
242
			/** client/html/catalog/detail/standard/template-header
243
			 * Relative path to the HTML header template of the catalog detail client.
244
			 *
245
			 * The template file contains the HTML code and processing instructions
246
			 * to generate the HTML code that is inserted into the HTML page header
247
			 * of the rendered page in the frontend. The configuration string is the
248
			 * path to the template file relative to the templates directory (usually
249
			 * in client/html/templates).
250
			 *
251
			 * You can overwrite the template file configuration in extensions and
252
			 * provide alternative templates. These alternative templates should be
253
			 * named like the default one but with the string "standard" replaced by
254
			 * an unique name. You may use the name of your project for this. If
255
			 * you've implemented an alternative client class as well, "standard"
256
			 * should be replaced by the name of the new class.
257
			 *
258
			 * @param string Relative path to the template creating code for the HTML page head
259
			 * @since 2014.03
260
			 * @category Developer
261
			 * @see client/html/catalog/detail/standard/template-body
262
			 */
263
			$tplconf = 'client/html/catalog/detail/standard/template-header';
264
			$default = 'catalog/detail/header-standard';
265
266
			try
267
			{
268
				if( !isset( $this->view ) ) {
269
					$view = $this->view = $this->getObject()->addData( $view, $this->tags, $this->expire );
270
				}
271
272
				$output = '';
273
				foreach( $this->getSubClients() as $subclient ) {
274
					$output .= $subclient->setView( $view )->getHeader( $uid );
275
				}
276
				$view->detailHeader = $output;
277
278
				$html = $view->render( $view->config( $tplconf, $default ) );
279
				$this->setCached( 'header', $uid, $prefixes, $confkey, $html, $this->tags, $this->expire );
280
281
				return $html;
282
			}
283
			catch( \Exception $e )
284
			{
285
				$this->logException( $e );
286
			}
287
		}
288
		else
289
		{
290
			$html = $this->modifyHeader( $html, $uid );
291
		}
292
293
		return $html;
294
	}
295
296
297
	/**
298
	 * Returns the sub-client given by its name.
299
	 *
300
	 * @param string $type Name of the client type
301
	 * @param string|null $name Name of the sub-client (Default if null)
302
	 * @return \Aimeos\Client\Html\Iface Sub-client object
303
	 */
304
	public function getSubClient( string $type, string $name = null ) : \Aimeos\Client\Html\Iface
305
	{
306
		/** client/html/catalog/detail/decorators/excludes
307
		 * Excludes decorators added by the "common" option from the catalog detail html client
308
		 *
309
		 * Decorators extend the functionality of a class by adding new aspects
310
		 * (e.g. log what is currently done), executing the methods of the underlying
311
		 * class only in certain conditions (e.g. only for logged in users) or
312
		 * modify what is returned to the caller.
313
		 *
314
		 * This option allows you to remove a decorator added via
315
		 * "client/html/common/decorators/default" before they are wrapped
316
		 * around the html client.
317
		 *
318
		 *  client/html/catalog/detail/decorators/excludes = array( 'decorator1' )
319
		 *
320
		 * This would remove the decorator named "decorator1" from the list of
321
		 * common decorators ("\Aimeos\Client\Html\Common\Decorator\*") added via
322
		 * "client/html/common/decorators/default" to the html client.
323
		 *
324
		 * @param array List of decorator names
325
		 * @since 2014.05
326
		 * @category Developer
327
		 * @see client/html/common/decorators/default
328
		 * @see client/html/catalog/detail/decorators/global
329
		 * @see client/html/catalog/detail/decorators/local
330
		 */
331
332
		/** client/html/catalog/detail/decorators/global
333
		 * Adds a list of globally available decorators only to the catalog detail html client
334
		 *
335
		 * Decorators extend the functionality of a class by adding new aspects
336
		 * (e.g. log what is currently done), executing the methods of the underlying
337
		 * class only in certain conditions (e.g. only for logged in users) or
338
		 * modify what is returned to the caller.
339
		 *
340
		 * This option allows you to wrap global decorators
341
		 * ("\Aimeos\Client\Html\Common\Decorator\*") around the html client.
342
		 *
343
		 *  client/html/catalog/detail/decorators/global = array( 'decorator1' )
344
		 *
345
		 * This would add the decorator named "decorator1" defined by
346
		 * "\Aimeos\Client\Html\Common\Decorator\Decorator1" only to the html client.
347
		 *
348
		 * @param array List of decorator names
349
		 * @since 2014.05
350
		 * @category Developer
351
		 * @see client/html/common/decorators/default
352
		 * @see client/html/catalog/detail/decorators/excludes
353
		 * @see client/html/catalog/detail/decorators/local
354
		 */
355
356
		/** client/html/catalog/detail/decorators/local
357
		 * Adds a list of local decorators only to the catalog detail html client
358
		 *
359
		 * Decorators extend the functionality of a class by adding new aspects
360
		 * (e.g. log what is currently done), executing the methods of the underlying
361
		 * class only in certain conditions (e.g. only for logged in users) or
362
		 * modify what is returned to the caller.
363
		 *
364
		 * This option allows you to wrap local decorators
365
		 * ("\Aimeos\Client\Html\Catalog\Decorator\*") around the html client.
366
		 *
367
		 *  client/html/catalog/detail/decorators/local = array( 'decorator2' )
368
		 *
369
		 * This would add the decorator named "decorator2" defined by
370
		 * "\Aimeos\Client\Html\Catalog\Decorator\Decorator2" only to the html client.
371
		 *
372
		 * @param array List of decorator names
373
		 * @since 2014.05
374
		 * @category Developer
375
		 * @see client/html/common/decorators/default
376
		 * @see client/html/catalog/detail/decorators/excludes
377
		 * @see client/html/catalog/detail/decorators/global
378
		 */
379
380
		return $this->createSubClient( 'catalog/detail/' . $type, $name );
381
	}
382
383
384
	/**
385
	 * Modifies the cached body content to replace content based on sessions or cookies.
386
	 *
387
	 * @param string $content Cached content
388
	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
389
	 * @return string Modified body content
390
	 */
391
	public function modifyBody( string $content, string $uid ) : string
392
	{
393
		$content = parent::modifyBody( $content, $uid );
394
395
		return $this->replaceSection( $content, $this->getView()->csrf()->formfield(), 'catalog.detail.csrf' );
396
	}
397
398
399
	/**
400
	 * Processes the input, e.g. store given values.
401
	 *
402
	 * A view must be available and this method doesn't generate any output
403
	 * besides setting view variables if necessary.
404
	 */
405
	public function process()
406
	{
407
		$context = $this->getContext();
408
		$view = $this->getView();
409
410
		try
411
		{
412
			$site = $context->getLocale()->getSiteItem()->getCode();
413
			$params = $this->getClientParams( $view->param() );
414
			$context->getSession()->set( 'aimeos/catalog/detail/params/last/' . $site, $params );
415
416
			parent::process();
417
		}
418
		catch( \Aimeos\Client\Html\Exception $e )
419
		{
420
			$error = array( $context->getI18n()->dt( 'client', $e->getMessage() ) );
421
			$view->detailErrorList = array_merge( $view->get( 'detailErrorList', [] ), $error );
422
		}
423
		catch( \Aimeos\Controller\Frontend\Exception $e )
424
		{
425
			$error = array( $context->getI18n()->dt( 'controller/frontend', $e->getMessage() ) );
426
			$view->detailErrorList = array_merge( $view->get( 'detailErrorList', [] ), $error );
427
		}
428
		catch( \Aimeos\MShop\Exception $e )
429
		{
430
			$error = array( $context->getI18n()->dt( 'mshop', $e->getMessage() ) );
431
			$view->detailErrorList = array_merge( $view->get( 'detailErrorList', [] ), $error );
432
		}
433
		catch( \Exception $e )
434
		{
435
			$error = array( $context->getI18n()->dt( 'client', 'A non-recoverable error occured' ) );
436
			$view->detailErrorList = array_merge( $view->get( 'detailErrorList', [] ), $error );
437
			$this->logException( $e );
438
		}
439
	}
440
441
442
	/**
443
	 * Returns the list of sub-client names configured for the client.
444
	 *
445
	 * @return array List of HTML client names
446
	 */
447
	protected function getSubClientNames() : array
448
	{
449
		return $this->getContext()->getConfig()->get( $this->subPartPath, $this->subPartNames );
450
	}
451
452
453
	/**
454
	 * Sets the necessary parameter values in the view.
455
	 *
456
	 * @param \Aimeos\MW\View\Iface $view The view object which generates the HTML output
457
	 * @param array &$tags Result array for the list of tags that are associated to the output
458
	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
459
	 * @return \Aimeos\MW\View\Iface Modified view object
460
	 */
461
	public function addData( \Aimeos\MW\View\Iface $view, array &$tags = [], string &$expire = null ) : \Aimeos\MW\View\Iface
462
	{
463
		$context = $this->getContext();
464
		$config = $context->getConfig();
465
		$domains = ['attribute', 'media', 'price', 'product', 'product/property', 'text'];
466
467
		/** client/html/catalog/domains
468
		 * A list of domain names whose items should be available in the catalog view templates
469
		 *
470
		 * @see client/html/catalog/detail/domains
471
		 */
472
		$domains = $config->get( 'client/html/catalog/domains', $domains );
473
474
		/** client/html/catalog/detail/domains
475
		 * A list of domain names whose items should be available in the product detail view template
476
		 *
477
		 * The templates rendering product details usually add the images,
478
		 * prices, texts, attributes, products, etc. associated to the product
479
		 * item. If you want to display additional or less content, you can
480
		 * configure your own list of domains (attribute, media, price, product,
481
		 * text, etc. are domains) whose items are fetched from the storage.
482
		 * Please keep in mind that the more domains you add to the configuration,
483
		 * the more time is required for fetching the content!
484
		 *
485
		 * Since version 2014.05 this configuration option overwrites the
486
		 * "client/html/catalog/domains" option that allows to configure the
487
		 * domain names of the items fetched for all catalog related data.
488
		 *
489
		 * @param array List of domain names
490
		 * @since 2014.03
491
		 * @category Developer
492
		 * @see client/html/catalog/domains
493
		 * @see client/html/catalog/lists/domains
494
		 */
495
		$domains = $config->get( 'client/html/catalog/detail/domains', $domains );
496
497
		/** client/html/catalog/detail/prodid-default
498
		 * The default product ID used if none is given as parameter
499
		 *
500
		 * To display a product detail view or a part of it for a specific
501
		 * product, you can configure its ID using this setting. This is
502
		 * most useful in a CMS where the product ID can be configured
503
		 * separately for each content node.
504
		 *
505
		 * @param string Product ID
506
		 * @since 2016.01
507
		 * @category User
508
		 * @category Developer
509
		 * @see client/html/catalog/detail/prodid-default
510
		 * @see client/html/catalog/lists/catid-default
511
		 */
512
		$id = $view->param( 'd_prodid', $config->get( 'client/html/catalog/detail/prodid-default' ) );
513
514
		/** client/html/catalog/detail/prodcode-default
515
		 * The default product code used if none is given as parameter
516
		 *
517
		 * To display a product detail view or a part of it for a specific
518
		 * product, you can configure its code using this setting. This is
519
		 * most useful in a CMS where the product code can be configured
520
		 * separately for each content node.
521
		 *
522
		 * @param string Product code
523
		 * @since 2019.10
524
		 * @category User
525
		 * @category Developer
526
		 * @see client/html/catalog/detail/prodid-default
527
		 * @see client/html/catalog/lists/catid-default
528
		 */
529
		$code = $config->get( 'client/html/catalog/detail/prodcode-default' );
530
531
		$name = $view->param( 'd_name', '' );
532
		$cntl = \Aimeos\Controller\Frontend::create( $context, 'product' )->uses( $domains );
533
534
		$productItem = ( $id ? $cntl->get( $id ) : ( $code ? $cntl->find( $code ) : $cntl->resolve( $name ) ) );
535
		$this->addMetaItems( $productItem, $expire, $tags );
536
537
		$propMap = $attrMap = [];
538
		$propItems = $productItem->getPropertyItems();
539
		$attrItems = $productItem->getRefItems( 'attribute', null, 'default' );
540
		$mediaItems = $productItem->getRefItems( 'media', 'default', 'default' );
541
542
		if( in_array( $productItem->getType(), ['bundle', 'select'] ) )
543
		{
544
			foreach( $productItem->getRefItems( 'product', null, 'default' ) as $subProdId => $subProduct )
545
			{
546
				$propItems->merge( $subProduct->getPropertyItems()->assign( ['parent' => $subProdId] ) );
547
				$mediaItems->merge( $subProduct->getRefItems( 'media', 'default', 'default' ) );
548
				$attrItems->merge( $subProduct->getRefItems( 'attribute', null, 'default' )
549
					->merge( $subProduct->getRefItems( 'attribute', null, 'variant' ) )
550
					->assign( ['parent' => $subProdId] ) );
551
			}
552
		}
553
554
		foreach( $attrItems as $attrId => $attrItem ) {
555
			$attrMap[$attrItem->getType()][$attrId] = $attrItem;
556
		}
557
558
		foreach( $propItems as $propItem ) {
559
			$propMap[$propItem->getType()][$propItem->getId()] = $propItem;
560
		}
561
562
563
		/** client/html/catalog/detail/stock/enable
564
		 * Enables or disables displaying product stock levels in product detail view
565
		 *
566
		 * This configuration option allows shop owners to display product
567
		 * stock levels for each product in the detail views or to disable
568
		 * fetching product stock information.
569
		 *
570
		 * The stock information is fetched via AJAX and inserted via Javascript.
571
		 * This allows to cache product items by leaving out such highly
572
		 * dynamic content like stock levels which changes with each order.
573
		 *
574
		 * @param boolean Value of "1" to display stock levels, "0" to disable displaying them
575
		 * @since 2014.03
576
		 * @category User
577
		 * @category Developer
578
		 * @see client/html/catalog/lists/stock/enable
579
		 * @see client/html/catalog/stock/url/target
580
		 * @see client/html/catalog/stock/url/controller
581
		 * @see client/html/catalog/stock/url/action
582
		 * @see client/html/catalog/stock/url/config
583
		 */
584
585
		if( (bool) $view->config( 'client/html/catalog/detail/stock/enable', true ) === true )
586
		{
587
			$products = $productItem->getRefItems( 'product', null, 'default' )->push( $productItem );
588
			$view->detailStockUrl = $this->getStockUrl( $view, $products );
589
		}
590
591
		$view->detailMediaItems = $mediaItems;
592
		$view->detailProductItem = $productItem;
593
		$view->detailPropertyMap = map( $propMap )->ksort();
594
		$view->detailAttributeMap = map( $attrMap )->ksort();
595
596
		return parent::addData( $view, $tags, $expire );
597
	}
598
}
599