Test Failed
Push — hotfix/fix-counts ( e9420b...4803a6 )
by Paul
05:49
created

CountsManager::build()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 19
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 12.4085

Importance

Changes 0
Metric Value
cc 5
eloc 14
nc 7
nop 1
dl 0
loc 19
ccs 5
cts 15
cp 0.3333
crap 12.4085
rs 9.4888
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A CountsManager::setPostCounts() 0 6 1
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Database;
4
5
use GeminiLabs\SiteReviews\Application;
6
use GeminiLabs\SiteReviews\Database;
7
use GeminiLabs\SiteReviews\Database\OptionManager;
8
use GeminiLabs\SiteReviews\Database\ReviewManager;
9
use GeminiLabs\SiteReviews\Database\SqlQueries;
10
use GeminiLabs\SiteReviews\Modules\Polylang;
11
use GeminiLabs\SiteReviews\Modules\Rating;
12
use GeminiLabs\SiteReviews\Review;
13
use WP_Post;
14
use WP_Term;
15
16
class CountsManager
17
{
18
	const LIMIT = 500;
19
	const META_AVERAGE = '_glsr_average';
20
	const META_COUNT = '_glsr_count';
21
	const META_RANKING = '_glsr_ranking';
22
23
	/**
24
	 * @return array
25
	 * @todo verify the additional type checks are needed
26
	 */
27
	public function buildCounts( array $args = [] )
28
	{
29
		$counts = [];
30
		$query = $this->queryReviews( $args );
31
		while( $query ) {
32
			// glsr_log($query);
33
			$types = array_keys( array_flip( glsr_array_column( $query->reviews, 'type' )));
34
			$types = array_unique( array_merge( ['local'], $types ));
35
			foreach( $types as $type ) {
36
				$type = $this->normalizeType( $type );
37
				if( isset( $counts[$type] ))continue;
38
				$counts[$type] = array_fill_keys( range( 0, Rating::MAX_RATING ), 0 );
39
			}
40
			foreach( $query->reviews as $review ) {
41
				$type = $this->normalizeType( $review->type );
42
				$counts[$type][$review->rating]++;
43
			}
44
			$query = $query->has_more
45
				? $this->queryReviews( $args, end( $query->reviews )->ID )
46
				: false;
47
		}
48
		return $counts;
49
	}
50
51
	/**
52
	 * @param int $postId
53
	 * @return array
54
	 */
55
	public function buildPostCounts( $postId )
56
	{
57
		return $this->buildCounts( ['post_ids' => [$postId]] );
58
	}
59
60
	/**
61
	 * @param int $termTaxonomyId
62
	 * @return array
63
	 */
64
	public function buildTermCounts( $termTaxonomyId )
65
	{
66
		return $this->buildCounts( ['term_ids' => [$termTaxonomyId]] );
67
	}
68
69
	/**
70
	 * @return void
71
	 */
72
	public function countAll()
73
	{
74
		$terms = glsr( Database::class )->getTerms( ['fields' => 'all'] );
75
		foreach( $terms as $term ) {
76
			$this->setTermCounts( $term->term_id, $this->buildTermCounts( $term->term_taxonomy_id ));
77
		}
78
		$postIds = glsr( SqlQueries::class )->getReviewsMeta( 'assigned_to' );
79
		foreach( $postIds as $postId ) {
80
			$this->setPostCounts( $postId, $this->buildPostCounts( $postId ));
81
		}
82
		$this->setCounts( $this->buildCounts() );
83
	}
84
85
	/**
86
	 * @return void
87
	 */
88
	public function decrease( Review $review )
89
	{
90
		$this->decreaseCounts( $review );
91
		$this->decreasePostCounts( $review );
92
		$this->decreaseTermCounts( $review );
93
	}
94
95
	/**
96
	 * @return void
97
	 */
98
	public function decreaseCounts( Review $review )
99
	{
100
		$this->setCounts( $this->decreaseRating(
101
			$this->getCounts(),
102
			$review->review_type,
103
			$review->rating
104
		));
105
	}
106
107
	/**
108
	 * @return void
109
	 */
110
	public function decreasePostCounts( Review $review )
111
	{
112
		if( empty( $counts = $this->getPostCounts( $review->assigned_to )))return;
113
		$counts = $this->decreaseRating( $counts, $review->review_type, $review->rating );
114
		$this->setPostCounts( $review->assigned_to, $counts );
115
	}
116
117
	/**
118
	 * @return void
119
	 */
120
	public function decreaseTermCounts( Review $review )
121
	{
122
		foreach( $review->term_ids as $termId ) {
123
			if( empty( $counts = $this->getTermCounts( $termId )))continue;
124
			$counts = $this->decreaseRating( $counts, $review->review_type, $review->rating );
125
			$this->setTermCounts( $termId, $counts );
126
		}
127
	}
128
129
	/**
130
	 * @return array
131
	 */
132
	public function flatten( array $reviewCounts, array $args = [] )
133
	{
134
		$counts = [];
135
		array_walk_recursive( $reviewCounts, function( $num, $index ) use( &$counts ) {
136
			$counts[$index] = $num + intval( glsr_get( $counts, $index, 0 ));
137
		});
138
		$args = wp_parse_args( $args, [
139
			'max' => Rating::MAX_RATING,
140
			'min' => Rating::MIN_RATING,
141
		]);
142
		foreach( $counts as $index => &$num ) {
143
			if( $index >= intval( $args['min'] ) && $index <= intval( $args['max'] ))continue;
144
			$num = 0;
145
		}
146
		return $counts;
147
	}
148
149
	/**
150
	 * @return array
151
	 */
152
	public function get( array $args = [] )
153
	{
154
		$args = $this->normalizeArgs( $args );
155
		$counts = [];
156
		if( $this->isMixedCount( $args )) {
157
			$counts = [$this->buildCounts( $args )]; // force query the database
158
		}
159
		else {
160
			foreach( $args['post_ids'] as $postId ) {
161
				$counts[] = $this->getPostCounts( $postId );
162
			}
163
			foreach( $args['term_ids'] as $termId ) {
164
				$counts[] = $this->getTermCounts( $termId );
165
			}
166
			if( empty( $counts )) {
167
				$counts[] = $this->getCounts();
168
			}
169
		}
170
		return in_array( $args['type'], ['', 'all'] )
171
			? $this->normalize( [$this->flatten( $counts )] )
172
			: $this->normalize( glsr_array_column( $counts, $args['type'] ));
173
	}
174
175
	/**
176
	 * @return array
177
	 */
178
	public function getCounts()
179
	{
180
		$counts = glsr( OptionManager::class )->get( 'counts', [] );
181
		if( !is_array( $counts )) {
182
			glsr_log()->error( '$counts is not an array' )->debug( $counts );
183
			return [];
184
		}
185
		return $counts;
186
	}
187
188
	/**
189
	 * @param int $postId
190
	 * @return array
191
	 */
192
	public function getPostCounts( $postId )
193
	{
194
		return array_filter( (array)get_post_meta( $postId, static::META_COUNT, true ));
195
	}
196
197
	/**
198
	 * @param int $termId
199
	 * @return array
200
	 */
201
	public function getTermCounts( $termId )
202
	{
203
		return array_filter( (array)get_term_meta( $termId, static::META_COUNT, true ));
204
	}
205
206
	/**
207
	 * @return void
208
	 */
209
	public function increase( Review $review )
210
	{
211
		$this->increaseCounts( $review );
212
		$this->increasePostCounts( $review );
213
		$this->increaseTermCounts( $review );
214
	}
215
216
	/**
217
	 * @return void
218
	 */
219
	public function increaseCounts( Review $review )
220
	{
221
		if( empty( $counts = $this->getCounts() )) {
222
			$counts = $this->buildCounts();
223
		}
224
		$this->setCounts( $this->increaseRating( $counts, $review->review_type, $review->rating ));
225
	}
226
227
	/**
228
	 * @return void
229
	 */
230
	public function increasePostCounts( Review $review )
231
	{
232
		if( !( get_post( $review->assigned_to ) instanceof WP_Post ))return;
233
		$counts = $this->getPostCounts( $review->assigned_to );
234
		$counts = empty( $counts )
235
			? $this->buildPostCounts( $review->assigned_to )
236
			: $this->increaseRating( $counts, $review->review_type, $review->rating );
237
		$this->setPostCounts( $review->assigned_to, $counts );
238
	}
239
240
	/**
241
	 * @return void
242
	 */
243
	public function increaseTermCounts( Review $review )
244
	{
245
		$terms = glsr( ReviewManager::class )->normalizeTerms( implode( ',', $review->term_ids ));
246
		foreach( $terms as $term ) {
247
			$counts = $this->getTermCounts( $term['term_id'] );
248
			$counts = empty( $counts )
249
				? $this->buildTermCounts( $term['term_taxonomy_id'] )
250
				: $this->increaseRating( $counts, $review->review_type, $review->rating );
251
			$this->setTermCounts( $term['term_id'], $counts );
252
		}
253
	}
254
255
	/**
256
	 * @return void
257
	 */
258
	public function setCounts( array $reviewCounts )
259
	{
260
		glsr( OptionManager::class )->set( 'counts', $reviewCounts );
261
	}
262
263
	/**
264
	 * @param int $postId
265
	 * @return void
266
	 */
267
	public function setPostCounts( $postId, array $reviewCounts )
268
	{
269
		$ratingCounts = $this->flatten( $reviewCounts );
270
		update_post_meta( $postId, static::META_COUNT, $reviewCounts );
271
		update_post_meta( $postId, static::META_AVERAGE, glsr( Rating::class )->getAverage( $ratingCounts ));
272
		update_post_meta( $postId, static::META_RANKING, glsr( Rating::class )->getRanking( $ratingCounts ));
273
	}
274
275
	/**
276
	 * @param int $termId
277
	 * @return void
278
	 */
279
	public function setTermCounts( $termId, array $reviewCounts )
280
	{
281
		$term = get_term( $termId, Application::TAXONOMY );
282
		if( !isset( $term->term_id ))return;
283
		$ratingCounts = $this->flatten( $reviewCounts );
284
		update_term_meta( $termId, static::META_COUNT, $reviewCounts );
285
		update_term_meta( $termId, static::META_AVERAGE, glsr( Rating::class )->getAverage( $ratingCounts ));
286
		update_term_meta( $termId, static::META_RANKING, glsr( Rating::class )->getRanking( $ratingCounts ));
287
	}
288
289
	/**
290
	 * @param string $type
291
	 * @param int $rating
292
	 * @return array
293
	 */
294
	protected function decreaseRating( array $reviewCounts, $type, $rating )
295
	{
296
		if( isset( $reviewCounts[$type][$rating] )) {
297
			$reviewCounts[$type][$rating] = max( 0, $reviewCounts[$type][$rating] - 1 );
298
		}
299
		return $reviewCounts;
300
	}
301
302
	/**
303
	 * @param string $type
304
	 * @param int $rating
305
	 * @return array
306
	 */
307
	protected function increaseRating( array $reviewCounts, $type, $rating )
308
	{
309
		if( !array_key_exists( $type, glsr()->reviewTypes )) {
310
			return $reviewCounts;
311
		}
312
		if( !array_key_exists( $type, $reviewCounts )) {
313
			$reviewCounts[$type] = [];
314
		}
315
		$reviewCounts = $this->normalize( $reviewCounts );
316
		$reviewCounts[$type][$rating] = intval( $reviewCounts[$type][$rating] ) + 1;
317
		return $reviewCounts;
318
	}
319
320
	/**
321
	 * @return bool
322
	 */
323
	protected function isMixedCount( array $args )
324
	{
325
		return !empty( $args['post_ids'] ) && !empty( $args['term_ids'] );
326
	}
327
328
	/**
329
	 * @return array
330
	 */
331
	protected function normalize( array $reviewCounts )
332
	{
333
		if( empty( $reviewCounts )) {
334
			$reviewCounts = [[]];
335
		}
336
		foreach( $reviewCounts as &$counts ) {
337
			foreach( range( 0, Rating::MAX_RATING ) as $index ) {
338
				if( isset( $counts[$index] ))continue;
339
				$counts[$index] = 0;
340
			}
341
			ksort( $counts );
342
		}
343
		return $reviewCounts;
344
	}
345
346
	/**
347
	 * @return array
348
	 */
349
	protected function normalizeArgs( array $args )
350
	{
351
		$args = wp_parse_args( array_filter( $args ), [
352
			'post_ids' => [],
353
			'term_ids' => [],
354
			'type' => 'local',
355
		]);
356
		$args['post_ids'] = glsr( Polylang::class )->getPostIds( $args['post_ids'] );
357
		$args['type'] = $this->normalizeType( $args['type'] );
358
		return $args;
359
	}
360
361
	/**
362
	 * @param string $type
363
	 * @return string
364
	 */
365
	protected function normalizeType( $type )
366
	{
367
		return empty( $type ) || !is_string( $type )
368
			? 'local'
369
			: $type;
370
	}
371
372
	/**
373
	 * @param int $lastPostId
374
	 * @return object
375
	 */
376
	protected function queryReviews( array $args = [], $lastPostId = 0 )
377
	{
378
		$reviews = glsr( SqlQueries::class )->getReviewCounts( $args, $lastPostId, static::LIMIT );
379
		$hasMore = is_array( $reviews )
380
			? count( $reviews ) == static::LIMIT
381
			: false;
382
		return (object) [
383
			'has_more' => $hasMore,
384
			'reviews' => $reviews,
385
		];
386
	}
387
}
388