Completed
Push — master ( ae96d3...309746 )
by Aimeos
02:00
created

Standard::validateIds()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 6
nc 3
nop 1
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2017
6
 * @package Controller
7
 * @subpackage Frontend
8
 */
9
10
11
namespace Aimeos\Controller\Frontend\Product;
12
13
14
/**
15
 * Default implementation of the product frontend controller.
16
 *
17
 * @package Controller
18
 * @subpackage Frontend
19
 */
20
class Standard
21
	extends \Aimeos\Controller\Frontend\Base
22
	implements Iface, \Aimeos\Controller\Frontend\Common\Iface
23
{
24
	/**
25
	 * Returns the given search filter with the conditions attached for filtering by attribute.
26
	 *
27
	 * @param \Aimeos\MW\Criteria\Iface $filter Criteria object used for product search
28
	 * @param array $attrIds List of attribute IDs for faceted search
29
	 * @param array $optIds List of OR-combined attribute IDs for faceted search
30
	 * @param array $attrIds Associative list of OR-combined attribute IDs per attribute type for faceted search
31
	 * @return \Aimeos\MW\Criteria\Iface Criteria object containing the conditions for searching
32
	 * @since 2017.03
33
	 */
34
	public function addFilterAttribute( \Aimeos\MW\Criteria\Iface $filter, array $attrIds, array $optIds, array $oneIds )
35
	{
36
		if( !empty( $attrIds ) )
37
		{
38
			$attrIds = $this->validateIds( $attrIds );
39
40
			$func = $filter->createFunction( 'index.attributeaggregate', array( $attrIds ) );
41
			$expr = array(
42
				$filter->compare( '==', $func, count( $attrIds ) ),
43
				$filter->getConditions(),
44
			);
45
			$filter->setConditions( $filter->combine( '&&', $expr ) );
46
		}
47
48
		if( !empty( $optIds ) )
49
		{
50
			$optIds = $this->validateIds( $optIds );
51
52
			$func = $filter->createFunction( 'index.attributeaggregate', array( $optIds ) );
53
			$expr = array(
54
				$filter->compare( '>', $func, 0 ),
55
				$filter->getConditions(),
56
			);
57
			$filter->setConditions( $filter->combine( '&&', $expr ) );
58
		}
59
60
		foreach( $oneIds as $type => $list )
61
		{
62
			if( ( $list = $this->validateIds( (array) $list ) ) !== [] )
63
			{
64
				$func = $filter->createFunction( 'index.attributeaggregate', array( $list ) );
65
				$expr = array(
66
					$filter->compare( '>', $func, 0 ),
67
					$filter->getConditions(),
68
				);
69
				$filter->setConditions( $filter->combine( '&&', $expr ) );
70
			}
71
		}
72
73
		return $filter;
74
	}
75
76
77
	/**
78
	 * Returns the given search filter with the conditions attached for filtering by category.
79
	 *
80
	 * @param \Aimeos\MW\Criteria\Iface $filter Criteria object used for product search
81
	 * @param string|array $catId Selected category by the user
82
	 * @param integer $level Constant for current category only, categories of next level (LEVEL_LIST) or whole subtree (LEVEL_SUBTREE)
83
	 * @param string|null $sort Sortation of the product list like "name", "code", "price" and "position", null for no sortation
84
	 * @param string $direction Sort direction of the product list ("+", "-")
85
	 * @param string $listtype List type of the product associated to the category, usually "default"
86
	 * @return \Aimeos\MW\Criteria\Iface Criteria object containing the conditions for searching
87
	 * @since 2017.03
88
	 */
89
	public function addFilterCategory( \Aimeos\MW\Criteria\Iface $filter, $catId,
90
		$level = \Aimeos\MW\Tree\Manager\Base::LEVEL_ONE, $sort = null, $direction = '+', $listtype = 'default' )
91
	{
92
		$catIds = ( !is_array( $catId ) ? explode( ',', $catId ) : $catId );
93
94
		if( $level != \Aimeos\MW\Tree\Manager\Base::LEVEL_ONE )
95
		{
96
			$list = [];
97
			$cntl = \Aimeos\Controller\Frontend\Factory::createController( $this->getContext(), 'catalog' );
98
99
			foreach( $catIds as $catId )
100
			{
101
				$tree = $cntl->getCatalogTree( $catId, [], $level );
102
				$list = array_merge( $list, $this->getCatalogIdsFromTree( $tree ) );
103
			}
104
105
			$catIds = $list;
106
		}
107
108
		$expr = array( $filter->compare( '==', 'index.catalog.id', array_unique( $catIds ) ) );
109
		$expr[] = $filter->getConditions();
110
111
		if( $sort === 'relevance' )
112
		{
113
			$cmpfunc = $filter->createFunction( 'index.catalog.position', array( $listtype, $catIds ) );
114
			$expr[] = $filter->compare( '>=', $cmpfunc, 0 );
115
116
			$sortfunc = $filter->createFunction( 'sort:index.catalog.position', array( $listtype, $catIds ) );
117
			$filter->setSortations( array( $filter->sort( $direction, $sortfunc ) ) );
118
		}
119
120
		$filter->setConditions( $filter->combine( '&&', $expr ) );
121
122
		return $filter;
123
	}
124
125
126
	/**
127
	 * Returns the given search filter with the conditions attached for filtering by text.
128
	 *
129
	 * @param \Aimeos\MW\Criteria\Iface $filter Criteria object used for product search
130
	 * @param string $input Search string entered by the user
131
	 * @param string|null $sort Sortation of the product list like "name", "code", "price" and "position", null for no sortation
132
	 * @param string $direction Sort direction of the product list ("+", "-")
133
	 * @param string $listtype List type of the text associated to the product, usually "default"
134
	 * @return \Aimeos\MW\Criteria\Iface Criteria object containing the conditions for searching
135
	 * @since 2017.03
136
	 */
137
	public function addFilterText( \Aimeos\MW\Criteria\Iface $filter, $input, $sort = null, $direction = '+', $listtype = 'default' )
138
	{
139
		$langid = $this->getContext()->getLocale()->getLanguageId();
140
		$cmpfunc = $filter->createFunction( 'index.text.relevance', array( $listtype, $langid, $input ) );
141
		$expr = array( $filter->compare( '>', $cmpfunc, 0 ), $filter->getConditions() );
142
143
		return $filter->setConditions( $filter->combine( '&&', $expr ) );
144
	}
145
146
147
	/**
148
	 * Returns the aggregated count of products for the given key.
149
	 *
150
	 * @param \Aimeos\MW\Criteria\Iface $filter Critera object which contains the filter conditions
151
	 * @param string $key Search key to aggregate for, e.g. "index.attribute.id"
152
	 * @return array Associative list of key values as key and the product count for this key as value
153
	 * @since 2017.03
154
	 */
155
	public function aggregate( \Aimeos\MW\Criteria\Iface $filter, $key )
156
	{
157
		return \Aimeos\MShop\Factory::createManager( $this->getContext(), 'index' )->aggregate( $filter, $key );
158
	}
159
160
161
	/**
162
	 * Returns the default product filter.
163
	 *
164
	 * @param string|null $sort Sortation of the product list like "name", "code", "price" and "position", null for no sortation
165
	 * @param string $direction Sort direction of the product list ("+", "-")
166
	 * @param integer $start Position in the list of found products where to begin retrieving the items
167
	 * @param integer $size Number of products that should be returned
168
	 * @param string $listtype Type of the product list, e.g. default, promotion, etc.
169
	 * @return \Aimeos\MW\Criteria\Iface Criteria object containing the conditions for searching
170
	 * @since 2017.03
171
	 */
172
	public function createFilter( $sort = null, $direction = '+', $start = 0, $size = 100, $listtype = 'default' )
173
	{
174
		$sortations = [];
175
		$context = $this->getContext();
176
		$manager = \Aimeos\MShop\Factory::createManager( $context, 'index' );
177
178
179
		if( $context->getConfig()->get( 'controller/frontend/product/ignore-dates', false ) )
180
		{
181
			$search = $manager->createSearch();
182
			$search->setConditions( $search->compare( '>', 'product.status', 0 ) );
183
		}
184
		else
185
		{
186
			$search = $manager->createSearch( true );
187
		}
188
189
190
		$expr = array( $search->compare( '!=', 'index.catalog.id', null ) );
191
192
		switch( $sort )
193
		{
194
			case 'code':
195
				$sortations[] = $search->sort( $direction, 'product.code' );
196
				break;
197
198
			case 'name':
199
				$langid = $context->getLocale()->getLanguageId();
200
201
				$cmpfunc = $search->createFunction( 'index.text.value', array( $listtype, $langid, 'name', 'product' ) );
202
				$expr[] = $search->compare( '>=', $cmpfunc, '' );
203
204
				$sortfunc = $search->createFunction( 'sort:index.text.value', array( $listtype, $langid, 'name' ) );
205
				$sortations[] = $search->sort( $direction, $sortfunc );
206
				break;
207
208
			case 'price':
209
				$currencyid = $context->getLocale()->getCurrencyId();
210
211
				$cmpfunc = $search->createFunction( 'index.price.value', array( $listtype, $currencyid, 'default' ) );
212
				$expr[] = $search->compare( '>=', $cmpfunc, '0.00' );
213
214
				$sortfunc = $search->createFunction( 'sort:index.price.value', array( $listtype, $currencyid, 'default' ) );
215
				$sortations[] = $search->sort( $direction, $sortfunc );
216
				break;
217
		}
218
219
		$expr[] = $search->getConditions();
220
221
		$search->setConditions( $search->combine( '&&', $expr ) );
222
		$search->setSortations( $sortations );
223
		$search->setSlice( $start, $size );
224
225
		return $search;
226
	}
227
228
229
	/**
230
	 * Returns the product for the given product ID from the product
231
	 *
232
	 * @param string $productId Unique product ID
233
	 * @param string[] $domains Domain names of items that are associated with the products and that should be fetched too
234
	 * @return \Aimeos\MShop\Product\Item\Iface Product item including the referenced domains items
235
	 * @since 2017.03
236
	 */
237
	public function getItem( $productId, array $domains = array( 'attribute', 'media', 'price', 'product', 'product/property', 'text' ) )
238
	{
239
		$items = $this->getItems( [$productId], $domains );
240
241
		if( ( $item = reset( $items ) ) !== false ) {
242
			return $item;
243
		}
244
245
		throw new \Aimeos\Controller\Frontend\Exception( sprintf( 'Product item with ID "%1$s" not found', $productId ) );
246
	}
247
248
249
	/**
250
	 * Returns the product for the given product ID from the product
251
	 *
252
	 * @param string[] $productIds List of unique product ID
253
	 * @param string[] $domains Domain names of items that are associated with the products and that should be fetched too
254
	 * @return \Aimeos\MShop\Product\Item\Iface[] Associative list of product IDs as keys and product items as values
255
	 * @since 2017.03
256
	 */
257
	public function getItems( array $productIds, array $domains = array( 'media', 'price', 'text' ) )
258
	{
259
		$context = $this->getContext();
260
		$manager = \Aimeos\MShop\Factory::createManager( $context, 'product' );
261
262
		/** controller/frontend/order/ignore-dates
263
		 * Ignore start and end dates of products
264
		 *
265
		 * Usually, products are only shown in the product list if their start/end
266
		 * dates are not set or if the current date is withing the start/end date
267
		 * range of the product. This settings will list all products that wouldn't
268
		 * be shown due to their start/end dates but they still can't be bought.
269
		 *
270
		 * @param boolean True to show products whose start/end date range doesn't match the current date, false to hide them
271
		 * @since 2017.08
272
		 * @category Developer
273
		 */
274
		if( $context->getConfig()->get( 'controller/frontend/product/ignore-dates', false ) )
275
		{
276
			$search = $manager->createSearch();
277
			$search->setConditions( $search->compare( '>', 'product.status', 0 ) );
278
		}
279
		else
280
		{
281
			$search = $manager->createSearch( true );
282
		}
283
284
		$expr = array(
285
			$search->compare( '==', 'product.id', $productIds ),
286
			$search->getConditions(),
287
		);
288
		$search->setConditions( $search->combine( '&&', $expr ) );
289
		$search->setSlice( 0, count( $productIds ) );
290
291
		return $manager->searchItems( $search, $domains );
292
	}
293
294
295
	/**
296
	 * Returns the products from the product filtered by the given criteria object.
297
	 *
298
	 * @param \Aimeos\MW\Criteria\Iface $filter Critera object which contains the filter conditions
299
	 * @param string[] $domains Domain names of items that are associated with the products and that should be fetched too
300
	 * @param integer &$total Parameter where the total number of found products will be stored in
301
	 * @return array Ordered list of product items implementing \Aimeos\MShop\Product\Item\Iface
302
	 * @since 2017.03
303
	 */
304
	public function searchItems( \Aimeos\MW\Criteria\Iface $filter, array $domains = array( 'media', 'price', 'text' ), &$total = null )
305
	{
306
		return \Aimeos\MShop\Factory::createManager( $this->getContext(), 'index' )->searchItems( $filter, $domains, $total );
307
	}
308
309
310
	/**
311
	 * Returns the list of catalog IDs for the given catalog tree
312
	 *
313
	 * @param \Aimeos\MShop\Catalog\Item\Iface $item Catalog item with children
314
	 * @return array List of catalog IDs
315
	 */
316
	protected function getCatalogIdsFromTree( \Aimeos\MShop\Catalog\Item\Iface $item )
317
	{
318
		$list = array( $item->getId() );
319
320
		foreach( $item->getChildren() as $child ) {
321
			$list = array_merge( $list, $this->getCatalogIdsFromTree( $child ) );
322
		}
323
324
		return $list;
325
	}
326
327
328
	/**
329
	 * Validates the given IDs as integers
330
	 *
331
	 * @param array $ids List of IDs to validate
332
	 * @return array List of validated IDs
333
	 */
334
	protected function validateIds( array $ids )
335
	{
336
		$list = [];
337
338
		foreach( $ids as $id )
339
		{
340
			if( $id != '' ) {
341
				$list[] = (int) $id;
342
			}
343
		}
344
345
		return $ids;
346
	}
347
}
348