Passed
Push — master ( 9a0039...2f021f )
by Aimeos
07:58
created

Standard::get()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
/**
4
 * @license LGPLv3, http://opensource.org/licenses/LGPL-3.0
5
 * @copyright Aimeos (aimeos.org), 2018-2021
6
 * @package Controller
7
 * @subpackage Frontend
8
 */
9
10
11
namespace Aimeos\Controller\Frontend\Review;
12
13
14
/**
15
 * Default implementation of the review 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
	private $filter;
25
	private $manager;
26
27
28
	/**
29
	 * Common initialization for controller classes
30
	 *
31
	 * @param \Aimeos\MShop\Context\Item\Iface $context Common MShop context object
32
	 */
33
	public function __construct( \Aimeos\MShop\Context\Item\Iface $context )
34
	{
35
		parent::__construct( $context );
36
37
		$this->manager = \Aimeos\MShop::create( $context, 'review' );
38
		$this->filter = $this->manager->filter( true );
39
	}
40
41
42
	/**
43
	 * Clones objects in controller and resets values
44
	 */
45
	public function __clone()
46
	{
47
		$this->filter = clone $this->filter;
48
	}
49
50
51
	/**
52
	 * Returns the aggregated count of products for the given key.
53
	 *
54
	 * @param string $key Search key to aggregate for, e.g. "review.rating"
55
	 * @param string|null $value Search key for aggregating the value column
56
	 * @param string|null $type Type of the aggregation, empty string for count or "sum"
57
	 * @return \Aimeos\Map Associative list of key values as key and the product count for this key as value
58
	 * @since 2020.10
59
	 */
60
	public function aggregate( string $key, string $value = null, string $type = null ) : \Aimeos\Map
61
	{
62
		$filter = clone $this->filter;
63
		$cond = $filter->is( 'review.status', '>', 0 );
64
65
		$filter->setSortations( $this->getSortations() );
66
		$filter->setConditions( $filter->and( array_merge( $this->getConditions(), [$cond] ) ) );
67
68
		return $this->manager->aggregate( $filter, $key, $value, $type );
69
	}
70
71
72
	/**
73
	 * Adds generic condition for filtering
74
	 *
75
	 * @param string $operator Comparison operator, e.g. "==", "!=", "<", "<=", ">=", ">", "=~", "~="
76
	 * @param string $key Search key defined by the review manager, e.g. "review.status"
77
	 * @param array|string $value Value or list of values to compare to
78
	 * @return \Aimeos\Controller\Frontend\Review\Iface Review controller for fluent interface
79
	 * @since 2020.10
80
	 */
81
	public function compare( string $operator, string $key, $value ) : Iface
82
	{
83
		$this->addExpression( $this->filter->compare( $operator, $key, $value ) );
84
		return $this;
85
	}
86
87
88
	/**
89
	 * Returns a new rating item
90
	 *
91
	 * @param array $vals Associative list of key/value pairs to initialize the item
92
	 * @return \Aimeos\MShop\Review\Item\Iface New review item
93
	 */
94
	public function create( array $vals = [] ) : \Aimeos\MShop\Review\Item\Iface
95
	{
96
		return $this->manager->create()->setOrderProductId( $vals['review.orderproductid'] ?? '' )->fromArray( $vals );
97
	}
98
99
100
	/**
101
	 * Deletes the review item for the given ID or IDs
102
	 *
103
	 * @param array|string $id Unique review ID or list of IDs
104
	 * @return \Aimeos\Controller\Frontend\Review\Iface Review controller for fluent interface
105
	 * @since 2020.10
106
	 */
107
	public function delete( $ids ) : Iface
108
	{
109
		$ids = (array) $ids;
110
		$filter = $this->manager->filter()->add( ['review.id' => $ids] );
111
		$this->manager->delete( $this->manager->search( $filter->slice( 0, count( $ids ) ) )->toArray() );
112
113
		return $this;
114
	}
115
116
117
	/**
118
	 * Sets the review domain for filtering
119
	 *
120
	 * @param string $domain Domain (e.g. "product") of the reviewed items
121
	 * @return \Aimeos\Controller\Frontend\Review\Iface Review controller for fluent interface
122
	 * @since 2020.10
123
	 */
124
	public function domain( string $domain ) : Iface
125
	{
126
		$this->addExpression( $this->filter->compare( '==', 'review.domain', $domain ) );
127
		return $this;
128
	}
129
130
131
	/**
132
	 * Restricts the reviews to a specific domain item
133
	 *
134
	 * @param string $domain Domain the reviews belong to (e.g. "product")
135
	 * @param array|string|null $refid Id of the item the reviews belong to, list of or NULL for all reviews from the domain
136
	 * @return \Aimeos\Controller\Frontend\Review\Iface Review controller for fluent interface
137
	 * @since 2020.10
138
	 */
139
	public function for( string $domain, $refid ) : Iface
140
	{
141
		$this->addExpression( $this->filter->compare( '==', 'review.domain', $domain ) );
142
143
		if( $refid !== null ) {
144
			$this->addExpression( $this->filter->compare( '==', 'review.refid', $refid ) );
145
		}
146
147
		return $this;
148
	}
149
150
151
	/**
152
	 * Returns the review item for the given ID
153
	 *
154
	 * @param string $id Unique review ID
155
	 * @return \Aimeos\MShop\Review\Item\Iface Review object
156
	 */
157
	public function get( string $id ) : \Aimeos\MShop\Review\Item\Iface
158
	{
159
		return $this->manager->get( $id, [], true );
160
	}
161
162
163
	/**
164
	 * Returns the reviews for the logged-in user
165
	 *
166
	 * @param int &$total Parameter where the total number of found reviews will be stored in
167
	 * @return \Aimeos\Map Ordered list of review items implementing \Aimeos\MShop\Review\Item\Iface
168
	 * @since 2020.10
169
	 */
170
	public function list( int &$total = null ) : \Aimeos\Map
171
	{
172
		$filter = clone $this->filter;
173
		$cond = $filter->is( 'review.customerid', '==', $this->getContext()->getUserId() );
174
175
		$filter->setConditions( $filter->and( array_merge( $this->getConditions(), [$cond] ) ) );
176
		$filter->setSortations( $this->getSortations() );
177
178
		return $this->manager->search( $filter, [], $total );
179
	}
180
181
182
	/**
183
	 * Parses the given array and adds the conditions to the list of conditions
184
	 *
185
	 * @param array $conditions List of conditions, e.g. ['>' => ['review.rating' => 3]]
186
	 * @return \Aimeos\Controller\Frontend\Review\Iface Review controller for fluent interface
187
	 * @since 2020.10
188
	 */
189
	public function parse( array $conditions ) : Iface
190
	{
191
		if( ( $cond = $this->filter->parse( $conditions ) ) !== null ) {
192
			$this->addExpression( $cond );
193
		}
194
195
		return $this;
196
	}
197
198
199
	/**
200
	 * Adds or updates a review
201
	 *
202
	 * @param \Aimeos\MShop\Review\Item\Iface $item Review item including required data
203
	 * @return \Aimeos\Controller\Frontend\Review\Iface Review controller for fluent interface
204
	 * @since 2020.10
205
	 */
206
	public function save( \Aimeos\MShop\Review\Item\Iface $item ) : \Aimeos\MShop\Review\Item\Iface
207
	{
208
		$domain = $item->getDomain();
209
210
		if( !in_array( $domain, ['product'] ) )
211
		{
212
			$msg = sprintf( 'Domain "%1$s" is not supported', $domain );
213
			throw new \Aimeos\Controller\Frontend\Review\Exception( $msg );
214
		}
215
216
		$context = $this->getContext();
217
		$manager = \Aimeos\MShop::create( $context, 'order/base' );
218
219
		$filter = $manager->filter( true )->add( [
220
			'order.base.product.id' => $item->getOrderProductId(),
221
			'order.base.customerid' => $context->getUserId()
222
		] );
223
		$manager->search( $filter->slice( 0, 1 ) )->first( new \Aimeos\Controller\Frontend\Review\Exception(
224
			sprintf( 'You can only add a review if you have ordered a product' )
225
		) );
226
227
		$orderProductItem = \Aimeos\MShop::create( $context, 'order/base/product' )->get( $item->getOrderProductId() );
0 ignored issues
show
Bug introduced by
It seems like $item->getOrderProductId() can also be of type null; however, parameter $id of Aimeos\MShop\Common\Manager\Iface::get() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

227
		$orderProductItem = \Aimeos\MShop::create( $context, 'order/base/product' )->get( /** @scrutinizer ignore-type */ $item->getOrderProductId() );
Loading history...
228
229
		$filter = $this->manager->filter()->add( [
230
			'review.customerid' => $context->getUserId(),
231
			'review.id' => $item->getId()
232
		] );
233
234
		/** controller/frontend/review/status
235
		 * Default status for new reviews
236
		 *
237
		 * By default, new reviews are stored with the status "in review" so they
238
		 * need to be approved by an admin or editor. Possible status values are:
239
		 *
240
		 * * 1 : enabled
241
		 * * 0 : disabled
242
		 * * -1 : in review
243
		 *
244
		 * @param integer Review status value
245
		 * @since 2020.10
246
		 */
247
		$status = $context->getConfig()->get( 'controller/frontend/review/status', -1 );
248
249
		$real = $this->manager->search( $filter->slice( 0, 1 ) )->first( $this->manager->create() );
250
251
		$real = $real->setCustomerId( $context->getUserId() )
252
			->setOrderProductId( $orderProductItem->getId() )
253
			->setRefId( $orderProductItem->getProductId() )
254
			->setComment( $item->getComment() )
255
			->setRating( $item->getRating() )
256
			->setName( $item->getName() )
257
			->setDomain( 'product' )
258
			->setStatus( $status );
259
260
		$item = $this->manager->save( $real );
261
262
		$filter = $this->manager->filter( true )->add( [
263
			'review.refid' => $item->getRefId(),
0 ignored issues
show
Bug introduced by
The method getRefId() does not exist on Aimeos\MShop\Common\Item\Iface. Did you maybe mean getId()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

263
			'review.refid' => $item->/** @scrutinizer ignore-call */ getRefId(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
264
			'review.domain' => $domain,
265
		] );
266
267
		if( $status > 0
268
			&& ( $entry = $this->manager->aggregate( $filter, 'review.refid', 'review.rating', 'rate' )->first( [] ) ) !== []
269
			&& !empty( $cnt = current( $entry ) )
270
		) {
271
			$rateManager = \Aimeos\MShop::create( $context, $domain === 'product' ? 'index' : $domain );
272
			$rateManager->rate( $item->getRefId(), key( $entry ) / $cnt, $cnt );
273
274
			$context->cache()->deleteByTags( [$domain, $domain . '-' . $item->getRefId()] );
0 ignored issues
show
Bug introduced by
Are you sure $item->getRefId() of type Aimeos\MShop\Common\Item\Iface|Aimeos\Map|mixed can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

274
			$context->cache()->deleteByTags( [$domain, $domain . '-' . /** @scrutinizer ignore-type */ $item->getRefId()] );
Loading history...
275
		}
276
277
		return $item;
278
	}
279
280
281
	/**
282
	 * Returns the reviews filtered by the previously assigned conditions
283
	 *
284
	 * @param int &$total Parameter where the total number of found reviews will be stored in
285
	 * @return \Aimeos\Map Ordered list of review items implementing \Aimeos\MShop\Review\Item\Iface
286
	 * @since 2020.10
287
	 */
288
	public function search( int &$total = null ) : \Aimeos\Map
289
	{
290
		$filter = clone $this->filter;
291
		$cond = $filter->is( 'review.status', '>', 0 );
292
293
		$maxsize = $this->getContext()->config()->get( 'controller/frontend/common/max-size', 250 );
294
		$filter->slice( $filter->getOffset(), min( $filter->getLimit(), $maxsize ) );
295
296
		$filter->setSortations( $this->getSortations() );
297
		$filter->setConditions( $filter->and( array_merge( $this->getConditions(), [$cond] ) ) );
298
299
		return $this->manager->search( $filter, [], $total );
300
	}
301
302
303
	/**
304
	 * Sets the start value and the number of returned review items for slicing the list of found review items
305
	 *
306
	 * @param int $start Start value of the first review item in the list
307
	 * @param int $limit Number of returned review items
308
	 * @return \Aimeos\Controller\Frontend\Review\Iface Review controller for fluent interface
309
	 * @since 2020.10
310
	 */
311
	public function slice( int $start, int $limit ) : Iface
312
	{
313
		$this->filter->slice( $start, $limit );
314
		return $this;
315
	}
316
317
318
	/**
319
	 * Sets the sorting of the result list
320
	 *
321
	 * @param string|null $key Sorting key of the result list like "mtime" or "rating", null for no sorting
322
	 * @return \Aimeos\Controller\Frontend\Review\Iface Review controller for fluent interface
323
	 * @since 2020.10
324
	 */
325
	public function sort( string $key = null ) : Iface
326
	{
327
		$list = $this->splitKeys( $key );
328
329
		foreach( $list as $sortkey )
330
		{
331
			$direction = ( $sortkey[0] === '-' ? '-' : '+' );
332
			$sortkey = ltrim( $sortkey, '+-' );
333
334
			switch( $sortkey )
335
			{
336
				case 'ctime':
337
					$this->addExpression( $this->filter->sort( $direction, 'review.ctime' ) );
338
					break;
339
				case 'rating':
340
					$this->addExpression( $this->filter->sort( $direction, 'review.rating' ) );
341
					break;
342
				default:
343
					$this->addExpression( $this->filter->sort( $direction, $sortkey ) );
344
			}
345
		}
346
347
		return $this;
348
	}
349
350
351
	/**
352
	 * Returns the manager used by the controller
353
	 *
354
	 * @return \Aimeos\MShop\Common\Manager\Iface Manager object
355
	 */
356
	protected function getManager() : \Aimeos\MShop\Common\Manager\Iface
357
	{
358
		return $this->manager;
359
	}
360
}
361