Passed
Push — master ( 9c19c1...8479fb )
by Paul
14:24 queued 07:17
created

Schema::setAndGetKeyValue()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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