Completed
Push — master ( 8b8334...2005c2 )
by Aimeos
07:11
created

Standard::getSubClientNames()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
/**
4
 * @copyright Metaways Infosystems GmbH, 2014
5
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
6
 * @copyright Aimeos (aimeos.org), 2015
7
 * @package Client
8
 * @subpackage Html
9
 */
10
11
12
namespace Aimeos\Client\Html\Account\Watch;
13
14
15
/**
16
 * Default implementation of account watch HTML client.
17
 *
18
 * @package Client
19
 * @subpackage Html
20
 */
21
class Standard
22
	extends \Aimeos\Client\Html\Common\Client\Factory\Base
23
	implements \Aimeos\Client\Html\Common\Client\Factory\Iface
24
{
25
	/** client/html/account/watch/standard/subparts
26
	 * List of HTML sub-clients rendered within the account watch 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/account/watch/standard/subparts';
59
	private $subPartNames = array();
60
	private $cache;
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
61
62
63
	/**
64
	 * Returns the HTML code for insertion into the body.
65
	 *
66
	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
67
	 * @param array &$tags Result array for the list of tags that are associated to the output
68
	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
69
	 * @return string HTML code
70
	 */
71
	public function getBody( $uid = '', array &$tags = array(), &$expire = null )
72
	{
73
		$context = $this->getContext();
74
		$view = $this->getView();
75
76
		try
77
		{
78
			$view = $this->setViewParams( $view, $tags, $expire );
79
80
			$html = '';
81
			foreach( $this->getSubClients() as $subclient ) {
82
				$html .= $subclient->setView( $view )->getBody( $uid, $tags, $expire );
83
			}
84
			$view->watchBody = $html;
85
		}
86
		catch( \Aimeos\Client\Html\Exception $e )
87
		{
88
			$error = array( $this->getContext()->getI18n()->dt( 'client', $e->getMessage() ) );
89
			$view->watchErrorList = $view->get( 'watchErrorList', array() ) + $error;
90
		}
91
		catch( \Aimeos\Controller\Frontend\Exception $e )
92
		{
93
			$error = array( $this->getContext()->getI18n()->dt( 'controller/frontend', $e->getMessage() ) );
94
			$view->watchErrorList = $view->get( 'watchErrorList', array() ) + $error;
95
		}
96
		catch( \Aimeos\MShop\Exception $e )
97
		{
98
			$error = array( $this->getContext()->getI18n()->dt( 'mshop', $e->getMessage() ) );
99
			$view->watchErrorList = $view->get( 'watchErrorList', array() ) + $error;
100
		}
101
		catch( \Exception $e )
102
		{
103
			$context->getLogger()->log( $e->getMessage() . PHP_EOL . $e->getTraceAsString() );
104
105
			$error = array( $context->getI18n()->dt( 'client', 'A non-recoverable error occured' ) );
106
			$view->watchErrorList = $view->get( 'watchErrorList', array() ) + $error;
107
		}
108
109
		/** client/html/account/watch/standard/template-body
110
		 * Relative path to the HTML body template of the account watch client.
111
		 *
112
		 * The template file contains the HTML code and processing instructions
113
		 * to generate the result shown in the body of the frontend. The
114
		 * configuration string is the path to the template file relative
115
		 * to the templates directory (usually in client/html/templates).
116
		 *
117
		 * You can overwrite the template file configuration in extensions and
118
		 * provide alternative templates. These alternative templates should be
119
		 * named like the default one but with the string "standard" replaced by
120
		 * an unique name. You may use the name of your project for this. If
121
		 * you've implemented an alternative client class as well, "standard"
122
		 * should be replaced by the name of the new class.
123
		 *
124
		 * @param string Relative path to the template creating code for the HTML page body
125
		 * @since 2014.03
126
		 * @category Developer
127
		 * @see client/html/account/watch/standard/template-header
128
		 */
129
		$tplconf = 'client/html/account/watch/standard/template-body';
130
		$default = 'account/watch/body-default.php';
131
132
		return $view->render( $view->config( $tplconf, $default ) );
133
	}
134
135
136
	/**
137
	 * Returns the HTML string for insertion into the header.
138
	 *
139
	 * @param string $uid Unique identifier for the output if the content is placed more than once on the same page
140
	 * @param array &$tags Result array for the list of tags that are associated to the output
141
	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
142
	 * @return string|null String including HTML tags for the header on error
143
	 */
144
	public function getHeader( $uid = '', array &$tags = array(), &$expire = null )
145
	{
146
		try
147
		{
148
			$view = $this->setViewParams( $this->getView(), $tags, $expire );
149
150
			$html = '';
151
			foreach( $this->getSubClients() as $subclient ) {
152
				$html .= $subclient->setView( $view )->getHeader( $uid, $tags, $expire );
153
			}
154
			$view->watchHeader = $html;
155
156
			/** client/html/account/watch/standard/template-header
157
			 * Relative path to the HTML header template of the account watch client.
158
			 *
159
			 * The template file contains the HTML code and processing instructions
160
			 * to generate the HTML code that is inserted into the HTML page header
161
			 * of the rendered page in the frontend. The configuration string is the
162
			 * path to the template file relative to the templates directory (usually
163
			 * in client/html/templates).
164
			 *
165
			 * You can overwrite the template file configuration in extensions and
166
			 * provide alternative templates. These alternative templates should be
167
			 * named like the default one but with the string "standard" replaced by
168
			 * an unique name. You may use the name of your project for this. If
169
			 * you've implemented an alternative client class as well, "standard"
170
			 * should be replaced by the name of the new class.
171
			 *
172
			 * @param string Relative path to the template creating code for the HTML page head
173
			 * @since 2014.03
174
			 * @category Developer
175
			 * @see client/html/account/watch/standard/template-body
176
			 */
177
			$tplconf = 'client/html/account/watch/standard/template-header';
178
			$default = 'account/watch/header-default.php';
179
180
			return $view->render( $view->config( $tplconf, $default ) );
181
		}
182
		catch( \Exception $e )
183
		{
184
			$this->getContext()->getLogger()->log( $e->getMessage() . PHP_EOL . $e->getTraceAsString() );
185
		}
186
	}
187
188
189
	/**
190
	 * Returns the sub-client given by its name.
191
	 *
192
	 * @param string $type Name of the client type
193
	 * @param string|null $name Name of the sub-client (Default if null)
194
	 * @return \Aimeos\Client\Html\Iface Sub-client object
195
	 */
196
	public function getSubClient( $type, $name = null )
197
	{
198
		/** client/html/account/watch/decorators/excludes
199
		 * Excludes decorators added by the "common" option from the account watch html client
200
		 *
201
		 * Decorators extend the functionality of a class by adding new aspects
202
		 * (e.g. log what is currently done), executing the methods of the underlying
203
		 * class only in certain conditions (e.g. only for logged in users) or
204
		 * modify what is returned to the caller.
205
		 *
206
		 * This option allows you to remove a decorator added via
207
		 * "client/html/common/decorators/default" before they are wrapped
208
		 * around the html client.
209
		 *
210
		 *  client/html/account/watch/decorators/excludes = array( 'decorator1' )
211
		 *
212
		 * This would remove the decorator named "decorator1" from the list of
213
		 * common decorators ("\Aimeos\Client\Html\Common\Decorator\*") added via
214
		 * "client/html/common/decorators/default" to the html client.
215
		 *
216
		 * @param array List of decorator names
217
		 * @since 2014.05
218
		 * @category Developer
219
		 * @see client/html/common/decorators/default
220
		 * @see client/html/account/watch/decorators/global
221
		 * @see client/html/account/watch/decorators/local
222
		 */
223
224
		/** client/html/account/watch/decorators/global
225
		 * Adds a list of globally available decorators only to the account watch html client
226
		 *
227
		 * Decorators extend the functionality of a class by adding new aspects
228
		 * (e.g. log what is currently done), executing the methods of the underlying
229
		 * class only in certain conditions (e.g. only for logged in users) or
230
		 * modify what is returned to the caller.
231
		 *
232
		 * This option allows you to wrap global decorators
233
		 * ("\Aimeos\Client\Html\Common\Decorator\*") around the html client.
234
		 *
235
		 *  client/html/account/watch/decorators/global = array( 'decorator1' )
236
		 *
237
		 * This would add the decorator named "decorator1" defined by
238
		 * "\Aimeos\Client\Html\Common\Decorator\Decorator1" only to the html client.
239
		 *
240
		 * @param array List of decorator names
241
		 * @since 2014.05
242
		 * @category Developer
243
		 * @see client/html/common/decorators/default
244
		 * @see client/html/account/watch/decorators/excludes
245
		 * @see client/html/account/watch/decorators/local
246
		 */
247
248
		/** client/html/account/watch/decorators/local
249
		 * Adds a list of local decorators only to the account watch html client
250
		 *
251
		 * Decorators extend the functionality of a class by adding new aspects
252
		 * (e.g. log what is currently done), executing the methods of the underlying
253
		 * class only in certain conditions (e.g. only for logged in users) or
254
		 * modify what is returned to the caller.
255
		 *
256
		 * This option allows you to wrap local decorators
257
		 * ("\Aimeos\Client\Html\Account\Decorator\*") around the html client.
258
		 *
259
		 *  client/html/account/watch/decorators/local = array( 'decorator2' )
260
		 *
261
		 * This would add the decorator named "decorator2" defined by
262
		 * "\Aimeos\Client\Html\Account\Decorator\Decorator2" only to the html client.
263
		 *
264
		 * @param array List of decorator names
265
		 * @since 2014.05
266
		 * @category Developer
267
		 * @see client/html/common/decorators/default
268
		 * @see client/html/account/watch/decorators/excludes
269
		 * @see client/html/account/watch/decorators/global
270
		 */
271
272
		return $this->createSubClient( 'account/watch/' . $type, $name );
273
	}
274
275
276
	/**
277
	 * Processes the input, e.g. store given values.
278
	 * A view must be available and this method doesn't generate any output
279
	 * besides setting view variables.
280
	 */
281
	public function process()
282
	{
283
		$view = $this->getView();
284
		$context = $this->getContext();
285
		$userId = $context->getUserId();
286
		$ids = (array) $view->param( 'wat_id', array() );
287
288
		if( $userId != null && !empty( $ids ) )
289
		{
290
			$typeId = $this->getTypeItem( 'customer/lists/type', 'product', 'watch' )->getId();
291
			$manager = \Aimeos\MShop\Factory::createManager( $context, 'customer/lists' );
292
			$items = $this->getListItems( $manager, $ids, $typeId, $userId );
293
294
			switch( $view->param( 'wat_action' ) )
295
			{
296
				case 'add':
297
298
					/** client/html/account/watch/standard/maxitems
299
					 * Maximum number of products that can be watched in parallel
300
					 *
301
					 * This option limits the number of products that can be watched
302
					 * after the users added the products to their watch list.
303
					 * It must be a positive integer value greater than 0.
304
					 *
305
					 * Note: It's recommended to set this value not too high as this
306
					 * leads to a high memory consumption when the e-mails are generated
307
					 * to notify the customers. The memory used will up to 100*maxitems
308
					 * of the footprint of one product item including the associated
309
					 * texts, prices and media.
310
					 *
311
					 * @param integer Number of products
312
					 * @since 2014.09
313
					 * @category User
314
					 * @category Developer
315
					 */
316
					$max = $context->getConfig()->get( 'client/html/account/watch/standard/maxitems', 100 );
317
					$cnt = count( $ids );
318
319
					if( $this->checkLimit( $manager, $typeId, $userId, $max, $cnt ) === false )
320
					{
321
						$error = sprintf( $context->getI18n()->dt( 'client', 'You can only watch up to %1$s products' ), $max );
322
						$view->watchErrorList = $view->get( 'watchErrorList', array() ) + array( $error );
323
						break;
324
					}
325
326
					$this->addItems( $manager, $items, $ids, $typeId, $userId );
327
					break;
328
329
				case 'edit':
330
331
					$config = array(
332
						'timeframe' => $view->param( 'wat_timeframe', 7 ),
333
						'pricevalue' => $view->param( 'wat_pricevalue', '0.00' ),
334
						'price' => $view->param( 'wat_price', 0 ),
335
						'stock' => $view->param( 'wat_stock', 0 ),
336
						'currency' => $context->getLocale()->getCurrencyId(),
337
					);
338
					$this->editItems( $manager, $items, $ids, $config );
339
					break;
340
341
				case 'delete':
342
343
					$this->deleteItems( $manager, $items, $ids );
344
					break;
345
			}
346
		}
347
348
		parent::process();
349
	}
350
351
352
	/**
353
	 * Tests if the maximum number of entries per user is already reached
354
	 *
355
	 * @param \Aimeos\MShop\Common\Manager\Iface $manager Customer list manager
356
	 * @param string $typeId List type ID of the referenced items
357
	 * @param string $userId Unique user ID
358
	 * @param integer $max Maximum number of items that are allowed
359
	 * @param integer $cnt Number of items that should be added
360
	 * @return boolean True if items can be added, false if not
361
	 */
362
	protected function checkLimit( \Aimeos\MShop\Common\Manager\Iface $manager, $typeId, $userId, $max, $cnt )
363
	{
364
		$search = $manager->createSearch();
365
		$expr = array(
366
			$search->compare( '==', 'customer.lists.parentid', $userId ),
367
			$search->compare( '==', 'customer.lists.typeid', $typeId ),
368
			$search->compare( '==', 'customer.lists.domain', 'product' ),
369
		);
370
		$search->setConditions( $search->combine( '&&', $expr ) );
371
		$search->setSlice( 0, 0 );
372
373
		$total = 0;
374
		$manager->searchItems( $search, array(), $total );
375
376
		if( $total + $cnt > $max ) {
377
			return false;
378
		}
379
380
		return true;
381
	}
382
383
384
	/**
385
	 * Adds one or more list items to the given user
386
	 *
387
	 * @param \Aimeos\MShop\Common\Manager\Iface $manager Customer list manager
388
	 * @param array $listItems Associative list of the reference IDs as keys and the list items as values
389
	 * @param array $ids List of referenced IDs
390
	 * @param string $typeId List type ID of the referenced items
391
	 * @param string $userId Unique user ID
392
	 */
393
	protected function addItems( \Aimeos\MShop\Common\Manager\Iface $manager, array $listItems, array $ids, $typeId, $userId )
394
	{
395
		$item = $manager->createItem();
396
		$item->setParentId( $userId );
397
		$item->setTypeId( $typeId );
398
		$item->setDomain( 'product' );
399
		$item->setStatus( 1 );
400
401
		foreach( $ids as $id )
402
		{
403
			if( !isset( $listItems[$id] ) )
404
			{
405
				$item->setId( null );
406
				$item->setRefId( $id );
407
408
				$manager->saveItem( $item );
409
				$manager->moveItem( $item->getId() );
1 ignored issue
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Aimeos\MShop\Common\Manager\Iface as the method moveItem() does only exist in the following implementations of said interface: Aimeos\MShop\Attribute\Manager\Lists\Standard, Aimeos\MShop\Catalog\Manager\Decorator\Base, Aimeos\MShop\Catalog\Manager\Decorator\Sitecheck, Aimeos\MShop\Catalog\Manager\Lists\Standard, Aimeos\MShop\Catalog\Manager\Standard, Aimeos\MShop\Common\Manager\Lists\Base, Aimeos\MShop\Customer\Manager\Lists\Standard, Aimeos\MShop\Locale\Manager\Site\Standard, Aimeos\MShop\Media\Manager\Lists\Standard, Aimeos\MShop\Price\Manager\Lists\Standard, Aimeos\MShop\Product\Manager\Lists\Standard, Aimeos\MShop\Service\Manager\Lists\Standard, Aimeos\MShop\Supplier\Manager\Lists\Standard, Aimeos\MShop\Text\Manager\Lists\Standard.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
410
			}
411
		}
412
	}
413
414
415
	/**
416
	 * Removes the list items for the given reference IDs
417
	 *
418
	 * @param \Aimeos\MShop\Common\Manager\Iface $manager Customer list manager
419
	 * @param array $listItems Associative list of the reference IDs as keys and the list items as values
420
	 * @param array $ids List of referenced IDs
421
	 */
422
	protected function deleteItems( \Aimeos\MShop\Common\Manager\Iface $manager, array $listItems, array $ids )
423
	{
424
		$listIds = array();
425
426
		foreach( $ids as $id )
427
		{
428
			if( isset( $listItems[$id] ) ) {
429
				$listIds[] = $listItems[$id]->getId();
430
			}
431
		}
432
433
		$manager->deleteItems( $listIds );
434
	}
435
436
437
	/**
438
	 * Updates the list items for the given reference IDs
439
	 *
440
	 * @param \Aimeos\MShop\Common\Manager\Iface $manager Customer list manager
441
	 * @param array $listItems Associative list of the reference IDs as keys and the list items as values
442
	 * @param array $ids List of referenced IDs
443
	 * @param array $config Configuration settins with "timeframe", "pricevalue", "price", "stock" and "currency"
444
	 */
445
	protected function editItems( \Aimeos\MShop\Common\Manager\Iface $manager, array $listItems, array $ids, array $config )
446
	{
447
		foreach( $ids as $id )
448
		{
449
			if( isset( $listItems[$id] ) )
450
			{
451
				$item = $listItems[$id];
452
				$time = time() + ( $config['timeframe'] + 1 ) * 86400;
453
454
				$item->setDateEnd( date( 'Y-m-d 00:00:00', $time ) );
455
				$item->setConfig( $config );
456
457
				$manager->saveItem( $item );
458
			}
459
		}
460
	}
461
462
463
	/**
464
	 * Returns the list items associated to the given user ID
465
	 *
466
	 * @param \Aimeos\MShop\Common\Manager\Iface $manager Customer list manager
467
	 * @param array $refIds IDs of the referenced items
468
	 * @param string $typeId List type ID of the referenced items
469
	 * @param string $userId Unique user ID
470
	 * @return array Associative list of the reference IDs as keys and the list items as values
471
	 */
472
	protected function getListItems( \Aimeos\MShop\Common\Manager\Iface $manager, array $refIds, $typeId, $userId )
473
	{
474
		$search = $manager->createSearch();
475
		$expr = array(
476
			$search->compare( '==', 'customer.lists.parentid', $userId ),
477
			$search->compare( '==', 'customer.lists.refid', $refIds ),
478
			$search->compare( '==', 'customer.lists.domain', 'product' ),
479
			$search->compare( '==', 'customer.lists.typeid', $typeId ),
480
		);
481
		$search->setConditions( $search->combine( '&&', $expr ) );
482
483
		$items = array();
484
		foreach( $manager->searchItems( $search ) as $item ) {
485
			$items[$item->getRefId()] = $item;
486
		}
487
488
		return $items;
489
	}
490
491
492
	/**
493
	 * Returns the list of sub-client names configured for the client.
494
	 *
495
	 * @return array List of HTML client names
496
	 */
497
	protected function getSubClientNames()
498
	{
499
		return $this->getContext()->getConfig()->get( $this->subPartPath, $this->subPartNames );
500
	}
501
502
503
	/**
504
	 * Returns the sanitized page from the parameters for the product list.
505
	 *
506
	 * @param \Aimeos\MW\View\Iface $view View instance with helper for retrieving the required parameters
507
	 * @return integer Page number starting from 1
508
	 */
509
	protected function getProductListPage( \Aimeos\MW\View\Iface $view )
510
	{
511
		$page = (int) $view->param( 'wat_page', 1 );
512
		return ( $page < 1 ? 1 : $page );
513
	}
514
515
516
	/**
517
	 * Returns the sanitized page size from the parameters for the product list.
518
	 *
519
	 * @param \Aimeos\MW\View\Iface $view View instance with helper for retrieving the required parameters
520
	 * @return integer Page size
521
	 */
522
	protected function getProductListSize( \Aimeos\MW\View\Iface $view )
523
	{
524
		/** client/html/account/watch/size
525
		 * The number of products shown in a list page for watch products
526
		 *
527
		 * Limits the number of products that is shown in the list pages to the
528
		 * given value. If more products are available, the products are split
529
		 * into bunches which will be shown on their own list page. The user is
530
		 * able to move to the next page (or previous one if it's not the first)
531
		 * to display the next (or previous) products.
532
		 *
533
		 * The value must be an integer number from 1 to 100. Negative values as
534
		 * well as values above 100 are not allowed. The value can be overwritten
535
		 * per request if the "l_size" parameter is part of the URL.
536
		 *
537
		 * @param integer Number of products
538
		 * @since 2014.09
539
		 * @category User
540
		 * @category Developer
541
		 * @see client/html/catalog/lists/size
542
		 */
543
		$defaultSize = $this->getContext()->getConfig()->get( 'client/html/account/watch/size', 48 );
544
545
		$size = (int) $view->param( 'watch-size', $defaultSize );
546
		return ( $size < 1 || $size > 100 ? $defaultSize : $size );
547
	}
548
549
550
	/**
551
	 * Sets the necessary parameter values in the view.
552
	 *
553
	 * @param \Aimeos\MW\View\Iface $view The view object which generates the HTML output
554
	 * @param array &$tags Result array for the list of tags that are associated to the output
555
	 * @param string|null &$expire Result variable for the expiration date of the output (null for no expiry)
556
	 * @return \Aimeos\MW\View\Iface Modified view object
557
	 */
558
	protected function setViewParams( \Aimeos\MW\View\Iface $view, array &$tags = array(), &$expire = null )
559
	{
560
		if( !isset( $this->cache ) )
561
		{
562
			$total = 0;
563
			$productIds = array();
564
			$context = $this->getContext();
565
			$typeItem = $this->getTypeItem( 'customer/lists/type', 'product', 'watch' );
566
567
			$size = $this->getProductListSize( $view );
568
			$current = $this->getProductListPage( $view );
569
			$last = ( $total != 0 ? ceil( $total / $size ) : 1 );
570
571
572
			$manager = \Aimeos\MShop\Factory::createManager( $context, 'customer/lists' );
573
574
			$search = $manager->createSearch();
575
			$expr = array(
576
				$search->compare( '==', 'customer.lists.parentid', $context->getUserId() ),
577
				$search->compare( '==', 'customer.lists.typeid', $typeItem->getId() ),
578
				$search->compare( '==', 'customer.lists.domain', 'product' ),
579
			);
580
			$search->setConditions( $search->combine( '&&', $expr ) );
581
			$search->setSortations( array( $search->sort( '-', 'customer.lists.position' ) ) );
582
			$search->setSlice( ( $current - 1 ) * $size, $size );
583
584
			$view->watchListItems = $manager->searchItems( $search, array(), $total );
585
586
587
			/** client/html/account/watch/domains
588
			 * A list of domain names whose items should be available in the account watch view template
589
			 *
590
			 * The templates rendering product details usually add the images,
591
			 * prices and texts associated to the product item. If you want to
592
			 * display additional or less content, you can configure your own
593
			 * list of domains (attribute, media, price, product, text, etc. are
594
			 * domains) whose items are fetched from the storage. Please keep
595
			 * in mind that the more domains you add to the configuration, the
596
			 * more time is required for fetching the content!
597
			 *
598
			 * @param array List of domain names
599
			 * @since 2014.09
600
			 * @category Developer
601
			 * @see client/html/catalog/domains
602
			 */
603
			$default = array( 'text', 'price', 'media' );
604
			$domains = $context->getConfig()->get( 'client/html/account/watch/domains', $default );
605
606
			foreach( $view->watchListItems as $listItem ) {
607
				$productIds[] = $listItem->getRefId();
608
			}
609
610
			$controller = \Aimeos\Controller\Frontend\Factory::createController( $context, 'catalog' );
611
612
			$view->watchProductItems = $controller->getProductItems( $productIds, $domains );
613
			$view->watchPageFirst = 1;
614
			$view->watchPagePrev = ( $current > 1 ? $current - 1 : 1 );
615
			$view->watchPageNext = ( $current < $last ? $current + 1 : $last );
616
			$view->watchPageLast = $last;
617
			$view->watchPageCurr = $current;
618
619
			$this->cache = $view;
620
		}
621
622
		return $this->cache;
623
	}
624
}