Passed
Push — master ( 227e6c...6addba )
by Paul
03:57
created

Schema::buildReview()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 20
ccs 0
cts 20
cp 0
crap 6
rs 9.6
c 0
b 0
f 0
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Modules;
4
5
use DateTime;
6
use GeminiLabs\SiteReviews\Database;
7
use GeminiLabs\SiteReviews\Database\OptionManager;
8
use GeminiLabs\SiteReviews\Helper;
9
use GeminiLabs\SiteReviews\Modules\Rating;
10
use GeminiLabs\SiteReviews\Modules\Schema\UnknownType;
11
use WP_Post;
12
13
class Schema
14
{
15
	/**
16
	 * @var array
17
	 */
18
	protected $args;
19
20
	/**
21
	 * @var array
22
	 */
23
	protected $reviews;
24
25
	/**
26
	 * @return array
27
	 */
28
	public function build( array $args = [] )
29
	{
30
		$this->args = $args;
31
		$schema = $this->buildSummary( $args );
32
		$reviews = [];
33
		foreach( glsr( Database::class )->getReviews( $this->args )->results as $review ) {
34
			// Only include critic reviews that have been directly produced by your site, not reviews from third- party sites or syndicated reviews.
35
			// @see https://developers.google.com/search/docs/data-types/review
36
			if( $review->review_type != 'local' )continue;
37
			$reviews[] = $this->buildReview( $review );
38
		}
39
		if( !empty( $reviews )) {
40
			array_walk( $reviews, function( &$review ) {
41
				unset( $review['@context'] );
42
				unset( $review['itemReviewed'] );
43
			});
44
			$schema['review'] = $reviews;
45
		}
46
		return $schema;
47
	}
48
49
	/**
50
	 * @param null|array $args
51
	 * @return array
52
	 */
53
	public function buildSummary( $args = null )
54
	{
55
		if( is_array( $args )) {
56
			$this->args = $args;
57
		}
58
		$buildSummary = glsr( Helper::class )->buildMethodName( $this->getSchemaOptionValue( 'type' ), 'buildSummaryFor' );
59
		$count = $this->getReviewCount();
60
		$schema = method_exists( $this, $buildSummary )
61
			? $this->$buildSummary()
62
			: $this->buildSummaryForCustom();
63
		if( !empty( $count )) {
64
			$schema->aggregateRating( $this->getSchemaType( 'AggregateRating' )
0 ignored issues
show
Bug introduced by
The method aggregateRating() does not exist on GeminiLabs\SiteReviews\Modules\Schema\UnknownType. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

64
			$schema->/** @scrutinizer ignore-call */ 
65
            aggregateRating( $this->getSchemaType( 'AggregateRating' )
Loading history...
65
				->ratingValue( $this->getRatingValue() )
0 ignored issues
show
Bug introduced by
The method ratingValue() does not exist on GeminiLabs\SiteReviews\Modules\Schema\UnknownType. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

65
				->/** @scrutinizer ignore-call */ ratingValue( $this->getRatingValue() )
Loading history...
66
				->reviewCount( $count )
0 ignored issues
show
Bug introduced by
The method reviewCount() does not exist on GeminiLabs\SiteReviews\Modules\Schema\UnknownType. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

66
				->/** @scrutinizer ignore-call */ reviewCount( $count )
Loading history...
67
				->bestRating( Rating::MAX_RATING )
0 ignored issues
show
Bug introduced by
The method bestRating() does not exist on GeminiLabs\SiteReviews\Modules\Schema\UnknownType. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

67
				->/** @scrutinizer ignore-call */ bestRating( Rating::MAX_RATING )
Loading history...
68
				->worstRating( Rating::MIN_RATING )
0 ignored issues
show
Bug introduced by
The method worstRating() does not exist on GeminiLabs\SiteReviews\Modules\Schema\UnknownType. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

68
				->/** @scrutinizer ignore-call */ worstRating( Rating::MIN_RATING )
Loading history...
69
			);
70
		}
71
		$schema = $schema->toArray();
72
		$args = wp_parse_args( ['count' => -1], $this->args );
73
		return apply_filters( sprintf( 'site-reviews/schema/%s', $schema['@type'] ), $schema, $args );
74
	}
75
76
	/**
77
	 * @return void
78
	 */
79
	public function render()
80
	{
81
		if( is_null( glsr()->schemas ))return;
82
		printf( '<script type="application/ld+json">%s</script>', json_encode(
83
			apply_filters( 'site-reviews/schema/all', glsr()->schemas ),
84
			JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
85
		));
86
	}
87
88
	/**
89
	 * @return void
90
	 */
91
	public function store( array $schema )
92
	{
93
		$schemas = glsr()->schemas;
94
		$schemas[] = $schema;
95
		glsr()->schemas = array_map( 'unserialize', array_unique( array_map( 'serialize', $schemas )));
96
	}
97
98
	/**
99
	 * @param object $review
100
	 * @return array
101
	 */
102
	protected function buildReview( $review )
103
	{
104
		$schema = $this->getSchemaType( 'Review' )
105
			->doIf( !in_array( 'title', $this->args['hide'] ), function( $schema ) use( $review ) {
106
				$schema->name( $review->title );
107
			})
108
			->doIf( !in_array( 'excerpt', $this->args['hide'] ), function( $schema ) use( $review ) {
109
				$schema->reviewBody( $review->content );
110
			})
111
			->datePublished(( new DateTime( $review->date )))
0 ignored issues
show
Bug introduced by
The method datePublished() does not exist on GeminiLabs\SiteReviews\Modules\Schema\UnknownType. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

111
			->/** @scrutinizer ignore-call */ datePublished(( new DateTime( $review->date )))
Loading history...
112
			->author( $this->getSchemaType( 'Person' )->name( $review->author ))
0 ignored issues
show
Bug introduced by
The method author() does not exist on GeminiLabs\SiteReviews\Modules\Schema\UnknownType. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

112
			->/** @scrutinizer ignore-call */ author( $this->getSchemaType( 'Person' )->name( $review->author ))
Loading history...
Bug introduced by
The method name() does not exist on GeminiLabs\SiteReviews\Modules\Schema\UnknownType. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

112
			->author( $this->getSchemaType( 'Person' )->/** @scrutinizer ignore-call */ name( $review->author ))
Loading history...
113
			->itemReviewed( $this->getSchemaType()->name( $this->getSchemaOptionValue( 'name' )));
0 ignored issues
show
Bug introduced by
The method itemReviewed() does not exist on GeminiLabs\SiteReviews\Modules\Schema\UnknownType. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

113
			->/** @scrutinizer ignore-call */ itemReviewed( $this->getSchemaType()->name( $this->getSchemaOptionValue( 'name' )));
Loading history...
114
		if( !empty( $review->rating )) {
115
			$schema->reviewRating( $this->getSchemaType( 'Rating' )
0 ignored issues
show
Bug introduced by
The method reviewRating() does not exist on GeminiLabs\SiteReviews\Modules\Schema\UnknownType. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

115
			$schema->/** @scrutinizer ignore-call */ 
116
            reviewRating( $this->getSchemaType( 'Rating' )
Loading history...
116
				->ratingValue( $review->rating )
117
				->bestRating( Rating::MAX_RATING )
118
				->worstRating( Rating::MIN_RATING )
119
			);
120
		}
121
		return apply_filters( 'site-reviews/schema/review', $schema->toArray(), $review, $this->args );
122
	}
123
124
	/**
125
	 * @param mixed $schema
126
	 * @return mixed
127
	 */
128
	protected function buildSchemaValues( $schema, array $values = [] )
129
	{
130
		foreach( $values as $value ) {
131
			$option = $this->getSchemaOptionValue( $value );
132
			if( empty( $option ))continue;
133
			$schema->$value( $option );
134
		}
135
		return $schema;
136
	}
137
138
	/**
139
	 * @return mixed
140
	 */
141
	protected function buildSummaryForCustom()
142
	{
143
		return $this->buildSchemaValues( $this->getSchemaType(), [
144
			'description', 'image', 'name', 'url',
145
		]);
146
	}
147
148
	/**
149
	 * @return mixed
150
	 */
151
	protected function buildSummaryForLocalBusiness()
152
	{
153
		return $this->buildSchemaValues( $this->buildSummaryForCustom(), [
154
			'address', 'priceRange', 'telephone',
155
		]);
156
	}
157
158
	/**
159
	 * @return mixed
160
	 */
161
	protected function buildSummaryForProduct()
162
	{
163
		$offers = $this->buildSchemaValues( $this->getSchemaType( 'AggregateOffer' ), [
164
			'highPrice', 'lowPrice', 'priceCurrency',
165
		]);
166
		return $this->buildSummaryForCustom()
167
			->offers( $offers )
0 ignored issues
show
Bug introduced by
The method offers() does not exist on GeminiLabs\SiteReviews\Modules\Schema\UnknownType. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

167
			->/** @scrutinizer ignore-call */ offers( $offers )
Loading history...
168
			->setProperty( '@id', $this->getSchemaOptionValue( 'url' ));
169
	}
170
171
	/**
172
	 * @return int|float
173
	 */
174
	protected function getRatingValue()
175
	{
176
		return glsr( Rating::class )->getAverage( $this->getReviews() );
177
	}
178
179
	/**
180
	 * @return int
181
	 */
182
	protected function getReviewCount()
183
	{
184
		return count( $this->getReviews() );
185
	}
186
187
	/**
188
	 * @return array
189
	 */
190
	protected function getReviews( $force = false )
191
	{
192
		if( !isset( $this->reviews ) || $force ) {
193
			$args = wp_parse_args( ['count' => -1], $this->args );
194
			$this->reviews = glsr( Database::class )->getReviews( $args )->results;
195
		}
196
		return $this->reviews;
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