Passed
Push — master ( 49ec8d...5de802 )
by Paul
03:41
created

Schema::getRatingValue()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 0
cts 3
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 0
crap 2
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Modules;
4
5
use DateTime;
6
use GeminiLabs\SiteReviews\Application;
7
use GeminiLabs\SiteReviews\Database\CountsManager;
8
use GeminiLabs\SiteReviews\Database\OptionManager;
9
use GeminiLabs\SiteReviews\Database\ReviewManager;
10
use GeminiLabs\SiteReviews\Helper;
11
use GeminiLabs\SiteReviews\Modules\Rating;
12
use GeminiLabs\SiteReviews\Modules\Schema\UnknownType;
13
use WP_Post;
14
15
class Schema
16
{
17
	/**
18
	 * @var array
19
	 */
20
	protected $args;
21
22
	/**
23
	 * @var array
24
	 */
25
	protected $ratingCounts;
26
27
	/**
28
	 * @return array
29
	 */
30
	public function build( array $args = [] )
31
	{
32
		$this->args = $args;
33
		$schema = $this->buildSummary( $args );
34
		$reviews = [];
35
		foreach( glsr( ReviewManager::class )->get( $this->args )->results as $review ) {
36
			// Only include critic reviews that have been directly produced by your site, not reviews from third- party sites or syndicated reviews.
37
			// @see https://developers.google.com/search/docs/data-types/review
38
			if( $review->review_type != 'local' )continue;
39
			$reviews[] = $this->buildReview( $review );
40
		}
41
		if( !empty( $reviews )) {
42
			array_walk( $reviews, function( &$review ) {
43
				unset( $review['@context'] );
44
				unset( $review['itemReviewed'] );
45
			});
46
			$schema['review'] = $reviews;
47
		}
48
		return $schema;
49
	}
50
51
	/**
52
	 * @param null|array $args
53
	 * @return array
54
	 */
55
	public function buildSummary( $args = null )
56
	{
57
		if( is_array( $args )) {
58
			$this->args = $args;
59
		}
60
		$buildSummary = glsr( Helper::class )->buildMethodName( $this->getSchemaOptionValue( 'type' ), 'buildSummaryFor' );
61
		$count = array_sum( $this->getRatingCounts() );
62
		$schema = method_exists( $this, $buildSummary )
63
			? $this->$buildSummary()
64
			: $this->buildSummaryForCustom();
65
		if( !empty( $count )) {
66
			$schema->aggregateRating(
67
				$this->getSchemaType( 'AggregateRating' )
68
					->ratingValue( $this->getRatingValue() )
69
					->reviewCount( $count )
70
					->bestRating( Rating::MAX_RATING )
71
					->worstRating( Rating::MIN_RATING )
72
			);
73
		}
74
		$schema = $schema->toArray();
75
		return apply_filters( 'site-reviews/schema/'.$schema['@type'], $schema, $args );
76
	}
77
78
	/**
79
	 * @return void
80
	 */
81
	public function render()
82
	{
83
		if( is_null( glsr()->schemas ))return;
84
		printf( '<script type="application/ld+json">%s</script>', json_encode(
85
			apply_filters( 'site-reviews/schema/all', glsr()->schemas ),
86
			JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
87
		));
88
	}
89
90
	/**
91
	 * @return void
92
	 */
93
	public function store( array $schema )
94
	{
95
		$schemas = glsr()->schemas;
96
		$schemas[] = $schema;
97
		glsr()->schemas = array_map( 'unserialize', array_unique( array_map( 'serialize', $schemas )));
98
	}
99
100
	/**
101
	 * @param object $review
102
	 * @return array
103
	 */
104
	protected function buildReview( $review )
105
	{
106
		$schema = $this->getSchemaType( 'Review' )
107
			->doIf( !in_array( 'title', $this->args['hide'] ), function( $schema ) use( $review ) {
108
				$schema->name( $review->title );
109
			})
110
			->doIf( !in_array( 'excerpt', $this->args['hide'] ), function( $schema ) use( $review ) {
111
				$schema->reviewBody( $review->content );
112
			})
113
			->datePublished(( new DateTime( $review->date )))
114
			->author( $this->getSchemaType( 'Person' )->name( $review->author ))
115
			->itemReviewed( $this->getSchemaType()->name( $this->getSchemaOptionValue( 'name' )));
116
		if( !empty( $review->rating )) {
117
			$schema->reviewRating(
118
				$this->getSchemaType( 'Rating' )
119
					->ratingValue( $review->rating )
120
					->bestRating( Rating::MAX_RATING )
121
					->worstRating( Rating::MIN_RATING )
122
			);
123
		}
124
		return apply_filters( 'site-reviews/schema/review', $schema->toArray(), $review, $this->args );
125
	}
126
127
	/**
128
	 * @param mixed $schema
129
	 * @return mixed
130
	 */
131
	protected function buildSchemaValues( $schema, array $values = [] )
132
	{
133
		foreach( $values as $value ) {
134
			$option = $this->getSchemaOptionValue( $value );
135
			if( empty( $option ))continue;
136
			$schema->$value( $option );
137
		}
138
		return $schema;
139
	}
140
141
	/**
142
	 * @return mixed
143
	 */
144
	protected function buildSummaryForCustom()
145
	{
146
		return $this->buildSchemaValues( $this->getSchemaType(), [
147
			'description', 'image', 'name', 'url',
148
		]);
149
	}
150
151
	/**
152
	 * @return mixed
153
	 */
154
	protected function buildSummaryForLocalBusiness()
155
	{
156
		return $this->buildSchemaValues( $this->buildSummaryForCustom(), [
157
			'address', 'priceRange', 'telephone',
158
		]);
159
	}
160
161
	/**
162
	 * @return mixed
163
	 */
164
	protected function buildSummaryForProduct()
165
	{
166
		$offers = $this->buildSchemaValues( $this->getSchemaType( 'AggregateOffer' ), [
167
			'highPrice', 'lowPrice', 'priceCurrency',
168
		]);
169
		return $this->buildSummaryForCustom()
170
			->offers( $offers )
171
			->setProperty( '@id', $this->getSchemaOptionValue( 'url' ));
172
	}
173
174
	/**
175
	 * @return array
176
	 */
177
	protected function getRatingCounts()
178
	{
179
		if( !isset( $this->ratingCounts )) {
180
			$counts = glsr( CountsManager::class )->get([
181
				'post_ids' => array_filter( array_map( 'trim', explode( ',', $this->args['assigned_to'] ))),
182
				'term_ids' => array_filter( array_map( 'trim', explode( ',', $this->args['category'] ))),
183
			]);
184
			$this->ratingCounts = glsr( CountsManager::class )->flatten( $counts, [
185
				'min' => $this->args['rating'],
186
			]);
187
		}
188
		return $this->ratingCounts;
189
	}
190
191
	/**
192
	 * @return int|float
193
	 */
194
	protected function getRatingValue()
195
	{
196
		return glsr( Rating::class )->getAverage( $this->getRatingCounts() );
197
	}
198
199
	/**
200
	 * @param string $option
201
	 * @param string $fallback
202
	 * @return string
203
	 */
204
	protected function getSchemaOption( $option, $fallback )
205
	{
206
		$option = strtolower( $option );
207
		if( $schemaOption = trim( (string)get_post_meta( intval( get_the_ID() ), 'schema_'.$option, true ))) {
208
			return $schemaOption;
209
		}
210
		$setting = glsr( OptionManager::class )->get( 'settings.schema.'.$option );
211
		if( is_array( $setting )) {
212
			return $this->getSchemaOptionDefault( $setting, $fallback );
213
		}
214
		return !empty( $setting )
215
			? $setting
216
			: $fallback;
217
	}
218
219
	/**
220
	 * @param string $fallback
221
	 * @return string
222
	 */
223
	protected function getSchemaOptionDefault( array $setting, $fallback )
224
	{
225
		$setting = wp_parse_args( $setting, [
226
			'custom' => '',
227
			'default' => $fallback,
228
		]);
229
		return $setting['default'] != 'custom'
230
			? $setting['default']
231
			: $setting['custom'];
232
	}
233
234
	/**
235
	 * @param string $option
236
	 * @param string $fallback
237
	 * @return void|string
238
	 */
239
	protected function getSchemaOptionValue( $option, $fallback = 'post' )
240
	{
241
		$value = $this->getSchemaOption( $option, $fallback );
242
		if( $value != $fallback ) {
243
			return $value;
244
		}
245
		if( !is_single() && !is_page() )return;
246
		$method = glsr( Helper::class )->buildMethodName( $option, 'getThing' );
247
		if( method_exists( $this, $method )) {
248
			return $this->$method();
249
		}
250
	}
251
252
	/**
253
	 * @param null|string $type
254
	 * @return mixed
255
	 */
256
	protected function getSchemaType( $type = null )
257
	{
258
		if( !is_string( $type )) {
259
			$type = $this->getSchemaOption( 'type', 'LocalBusiness' );
260
		}
261
		$className = glsr( Helper::class )->buildClassName( $type, 'Modules\Schema' );
262
		return class_exists( $className )
263
			? new $className()
264
			: new UnknownType( $type );
265
	}
266
267
	/**
268
	 * @return string
269
	 */
270
	protected function getThingDescription()
271
	{
272
		$post = get_post();
273
		if( !( $post instanceof WP_Post )) {
274
			return '';
275
		}
276
		$text = strip_shortcodes( wp_strip_all_tags( $post->post_excerpt ));
277
		return wp_trim_words( $text, apply_filters( 'excerpt_length', 55 ));
278
	}
279
280
	/**
281
	 * @return string
282
	 */
283
	protected function getThingImage()
284
	{
285
		return (string)get_the_post_thumbnail_url( null, 'large' );
286
	}
287
288
	/**
289
	 * @return string
290
	 */
291
	protected function getThingName()
292
	{
293
		return get_the_title();
294
	}
295
296
	/**
297
	 * @return string
298
	 */
299
	protected function getThingUrl()
300
	{
301
		return (string)get_the_permalink();
302
	}
303
}
304