Passed
Push — master ( 78492a...4f268f )
by Paul
04:26
created

Schema::buildSchemaValues()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 2
dl 0
loc 8
ccs 0
cts 8
cp 0
crap 12
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Modules;
4
5
use DateTime;
6
use GeminiLabs\SchemaOrg\Review as ReviewSchema;
7
use GeminiLabs\SchemaOrg\Schema as SchemaOrg;
8
use GeminiLabs\SchemaOrg\Thing as ThingSchema;
9
use GeminiLabs\SiteReviews\Database;
10
use GeminiLabs\SiteReviews\Database\OptionManager;
11
use GeminiLabs\SiteReviews\Helper;
12
use GeminiLabs\SiteReviews\Modules\Rating;
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 $reviews;
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( Database::class )->getReviews( $this->args )->results as $review ) {
36
			$reviews[] = $this->buildReview( $review );
37
		}
38
		if( !empty( $reviews )) {
39
			array_walk( $reviews, function( &$review ) {
40
				unset( $review['@context'] );
41
				unset( $review['itemReviewed'] );
42
			});
43
			$schema['review'] = $reviews;
44
		}
45
		return $schema;
46
	}
47
48
	/**
49
	 * @param null|array $args
50
	 * @return array
51
	 */
52
	public function buildSummary( $args = null )
53
	{
54
		if( is_array( $args )) {
55
			$this->args = $args;
56
		}
57
		$buildSummary = glsr( Helper::class )->buildMethodName( $this->getSchemaOptionValue( 'type' ), 'buildSummaryFor' );
58
		$count = $this->getReviewCount();
59
		$schema = method_exists( $this, $buildSummary )
60
			? $this->$buildSummary()
61
			: $this->buildSummaryForCustom();
62
		if( !empty( $count )) {
63
			$schema->aggregateRating( SchemaOrg::AggregateRating()
64
				->ratingValue( $this->getRatingValue() )
65
				->reviewCount( $count )
66
				->bestRating( Rating::MAX_RATING )
67
				->worstRating( Rating::MIN_RATING )
68
			);
69
		}
70
		$schema = $schema->toArray();
71
		$args = wp_parse_args( ['count' => -1], $this->args );
72
		return apply_filters( sprintf( 'site-reviews/schema/%s', $schema['@type'] ), $schema, $args );
73
	}
74
75
	/**
76
	 * @return void
77
	 */
78
	public function render()
79
	{
80
		if( is_null( glsr()->schemas ))return;
81
		printf( '<script type="application/ld+json">%s</script>', json_encode(
82
			apply_filters( 'site-reviews/schema/all', glsr()->schemas ),
83
			JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
84
		));
85
	}
86
87
	/**
88
	 * @return void
89
	 */
90
	public function store( array $schema )
91
	{
92
		$schemas = (array) glsr()->schemas;
93
		$schemas[] = $schema;
94
		glsr()->schemas = array_map( 'unserialize', array_unique( array_map( 'serialize', $schemas )));
95
	}
96
97
	/**
98
	 * @param object $review
99
	 * @return array
100
	 */
101
	protected function buildReview( $review )
102
	{
103
		$schema = SchemaOrg::Review()
104
			->doIf( !in_array( 'title', $this->args['hide'] ), function( ReviewSchema $schema ) use( $review ) {
105
				$schema->name( $review->title );
106
			})
107
			->doIf( !in_array( 'excerpt', $this->args['hide'] ), function( ReviewSchema $schema ) use( $review ) {
108
				$schema->reviewBody( $review->content );
109
			})
110
			->datePublished(( new DateTime( $review->date ))->format( DateTime::ISO8601 ))
0 ignored issues
show
Bug introduced by
new DateTime($review->da...rmat(DateTime::ISO8601) of type string is incompatible with the type DateTimeInterface expected by parameter $datePublished of GeminiLabs\SchemaOrg\CreativeWork::datePublished(). ( Ignorable by Annotation )

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

110
			->datePublished(/** @scrutinizer ignore-type */ ( new DateTime( $review->date ))->format( DateTime::ISO8601 ))
Loading history...
111
			->author( SchemaOrg::Person()->name( $review->author ))
112
			->itemReviewed( $this->getSchemaType()->name( $this->getSchemaOptionValue( 'name' )));
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getSchemaType()->...emaOptionValue('name')) targeting GeminiLabs\SchemaOrg\Type::__call() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
113
		if( !empty( $review->rating )) {
114
			$schema->reviewRating( SchemaOrg::Rating()
115
				->ratingValue( $review->rating )
116
				->bestRating( Rating::MAX_RATING )
117
				->worstRating( Rating::MIN_RATING )
118
			);
119
		}
120
		return apply_filters( 'site-reviews/schema/review', $schema->toArray(), $review, $this->args );
121
	}
122
123
	/**
124
	 * @return ThingSchema
125
	 */
126
	protected function buildSchemaValues( ThingSchema $schema, array $values = [] )
127
	{
128
		foreach( $values as $value ) {
129
			$option = $this->getSchemaOptionValue( $value );
130
			if( empty( $option ))continue;
131
			$schema->$value( $option );
132
		}
133
		return $schema;
134
	}
135
136
	/**
137
	 * @return ThingSchema
138
	 */
139
	protected function buildSummaryForCustom()
140
	{
141
		return $this->buildSchemaValues( $this->getSchemaType(), [
142
			'description', 'image', 'name', 'url',
143
		]);
144
	}
145
146
	/**
147
	 * @return ThingSchema
148
	 */
149
	protected function buildSummaryForLocalBusiness()
150
	{
151
		return $this->buildSchemaValues( $this->buildSummaryForCustom(), [
152
			'address', 'priceRange', 'telephone',
153
		]);
154
	}
155
156
	/**
157
	 * @return ThingSchema
158
	 */
159
	protected function buildSummaryForProduct()
160
	{
161
		$offers = $this->buildSchemaValues( SchemaOrg::AggregateOffer(), [
162
			'highPrice', 'lowPrice', 'priceCurrency',
163
		]);
164
		return $this->buildSummaryForCustom()
165
			->offers( $offers )
166
			->setProperty( '@id', $this->getSchemaOptionValue( 'url' ));
167
	}
168
169
	/**
170
	 * @return int|float
171
	 */
172
	protected function getRatingValue()
173
	{
174
		return glsr( Rating::class )->getAverage( $this->getReviews() );
175
	}
176
177
	/**
178
	 * @return int
179
	 */
180
	protected function getReviewCount()
181
	{
182
		return count( $this->getReviews() );
183
	}
184
185
	/**
186
	 * @return array
187
	 */
188
	protected function getReviews( $force = false )
189
	{
190
		if( !isset( $this->reviews ) || $force ) {
191
			$args = wp_parse_args( ['count' => -1], $this->args );
192
			$this->reviews = glsr( Database::class )->getReviews( $args )->results;
193
		}
194
		return $this->reviews;
195
	}
196
197
	/**
198
	 * @param string $option
199
	 * @param string $fallback
200
	 * @return string
201
	 */
202
	protected function getSchemaOption( $option, $fallback )
203
	{
204
		$option = strtolower( $option );
205
		if( $schemaOption = trim( (string)get_post_meta( intval( get_the_ID() ), 'schema_'.$option, true ))) {
206
			return $schemaOption;
207
		}
208
		$setting = glsr( OptionManager::class )->get( 'settings.schema.'.$option );
209
		if( is_array( $setting )) {
210
			return $this->getSchemaOptionDefault( $setting, $fallback );
0 ignored issues
show
Bug introduced by
$setting of type array is incompatible with the type string expected by parameter $setting of GeminiLabs\SiteReviews\M...etSchemaOptionDefault(). ( Ignorable by Annotation )

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

210
			return $this->getSchemaOptionDefault( /** @scrutinizer ignore-type */ $setting, $fallback );
Loading history...
211
		}
212
		return !empty( $setting )
213
			? $setting
214
			: $fallback;
215
	}
216
217
	/**
218
	 * @param string $setting
219
	 * @param string $fallback
220
	 * @return string
221
	 */
222
	protected function getSchemaOptionDefault( $setting, $fallback )
223
	{
224
		$setting = wp_parse_args( $setting, [
225
			'custom' => '',
226
			'default' => $fallback,
227
		]);
228
		return $setting['default'] != 'custom'
229
			? $setting['default']
230
			: $setting['custom'];
231
	}
232
233
	/**
234
	 * @param string $option
235
	 * @param string $fallback
236
	 * @return void|string
237
	 */
238
	protected function getSchemaOptionValue( $option, $fallback = 'post' )
239
	{
240
		$value = $this->getSchemaOption( $option, $fallback );
241
		if( $value != $fallback ) {
242
			return $value;
243
		}
244
		if( !is_single() && !is_page() )return;
245
		// @todo make this dynamic
246
		switch( $option ) {
247
			case 'description':
248
				return $this->getThingDescription();
249
			case 'image':
250
				return $this->getThingImage();
251
			case 'name':
252
				return $this->getThingName();
253
			case 'url':
254
				return $this->getThingUrl();
255
		}
256
	}
257
258
	/**
259
	 * @return \GeminiLabs\SchemaOrg\Type
260
	 */
261
	protected function getSchemaType()
262
	{
263
		$type = $this->getSchemaOption( 'type', 'LocalBusiness' );
264
		return SchemaOrg::$type( $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