Passed
Push — develop ( ad8937...eb8b4a )
by Paul
05:58
created

ProductController::option()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
eloc 9
c 0
b 0
f 0
dl 0
loc 14
ccs 0
cts 11
cp 0
rs 9.9666
cc 3
nc 3
nop 2
crap 12
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Integrations\WooCommerce\Controllers;
4
5
use GeminiLabs\SiteReviews\Database\CountManager;
6
use GeminiLabs\SiteReviews\Defaults\SiteReviewsDefaults;
7
use GeminiLabs\SiteReviews\Helpers\Arr;
8
use GeminiLabs\SiteReviews\Helpers\Cast;
9
use GeminiLabs\SiteReviews\Helpers\Str;
10
use GeminiLabs\SiteReviews\Modules\Html\Builder;
11
use GeminiLabs\SiteReviews\Modules\Html\Template;
12
use GeminiLabs\SiteReviews\Modules\Rating;
13
use GeminiLabs\SiteReviews\Modules\Sanitizer;
14
use GeminiLabs\SiteReviews\Modules\Schema;
15
use GeminiLabs\SiteReviews\Modules\Style;
16
17
class ProductController
18
{
19
    /**
20
     * @param string $template
21
     * @return string
22
     * @filter comments_template
23
     */
24
    public function filterCommentsTemplate($template)
25
    {
26
        if (current_theme_supports('woocommerce') && 'product' === get_post_type()) {
27
            return glsr()->path('views/integrations/woocommerce/overrides/single-product-reviews.php');
28
        }
29
        return $template;
30
    }
31
32
    /**
33
     * @param string $html
34
     * @param int $rating
35
     * @param int $count
36
     * @return string
37
     * @filter woocommerce_product_get_rating_html
38
     */
39
    public function filterGetRatingHtml($html, $rating, $count)
40
    {
41
        return glsr(Builder::class)->div([
42
            'class' => 'glsr glsr-'.glsr(Style::class)->styleClasses(),
43
            'text' => glsr_star_rating($rating, $count, ['theme' => glsr_get_option('addons.woocommerce.style')]),
44
        ]);
45
    }
46
47
    /**
48
     * @param string $html
49
     * @param int $rating
50
     * @param int $count
51
     * @return string
52
     * @filter woocommerce_get_star_rating_html
53
     */
54
    public function filterGetStarRatingHtml($html, $rating, $count)
55
    {
56
        return glsr_star_rating($rating, $count, ['theme' => glsr_get_option('addons.woocommerce.style')]);
57
    }
58
59
    /**
60
     * @param mixed $value
61
     * @param \WC_Product $product
62
     * @return float
63
     * @filter woocommerce_product_get_average_rating
64
     */
65
    public function filterProductAverageRating($value, $product)
66
    {
67
        return Cast::toFloat(get_post_meta($product->get_id(), CountManager::META_AVERAGE, true));
68
    }
69
70
    /**
71
     * @param array $tabs
72
     * @return array
73
     * @filter woocommerce_product_data_tabs
74
     */
75
    public function filterProductDataTabs($tabs)
76
    {
77
        $tabs[glsr()->id] = [
78
            'label' => glsr()->name,
79
            'target' => glsr()->id,
80
            'priority' => 100,
81
            'class' => [],
82
        ];
83
        return $tabs;
84
    }
85
86
    /**
87
     * @param array $metaQuery
88
     * @return array
89
     * @filter woocommerce_product_query_meta_query
90
     */
91
    public function filterProductMetaQuery($metaQuery)
92
    {
93
        global $wp_query;
94
        $orderby = filter_input(INPUT_GET, 'orderby');
95
        if (!$orderby && !is_search()) {
96
            $orderby = apply_filters('woocommerce_default_catalog_orderby', get_option('woocommerce_default_catalog_orderby'));
97
        }
98
        if ('rating' !== $orderby) {
99
            return $metaQuery;
100
        }
101
        if ('bayesian' === glsr_get_option('addons.woocommerce.sorting')) {
102
            $metaQuery[] = $this->buildMetaQuery('glsr_ranking', CountManager::META_RANKING);
103
            $wp_query->set('orderby', ['glsr_ranking' => 'DESC']);
104
        } else {
105
            $metaQuery[] = $this->buildMetaQuery('glsr_average', CountManager::META_AVERAGE);
106
            $metaQuery[] = $this->buildMetaQuery('glsr_reviews', CountManager::META_REVIEWS);
107
            $wp_query->set('orderby', ['glsr_average' => 'DESC', 'glsr_reviews' => 'DESC']);
108
        }
109
        return $metaQuery;
110
    }
111
112
    /**
113
     * @param array $args
114
     * @param string $orderby
115
     * @return array
116
     * @filter woocommerce_get_catalog_ordering_args
117
     */
118
    public function filterProductPostClauses($args, $orderby)
119
    {
120
        if ('rating' === $orderby) {
121
            remove_filter('posts_clauses', [WC()->query, 'order_by_rating_post_clauses']);
122
        }
123
        return $args;
124
    }
125
126
    /**
127
     * @param mixed $value
128
     * @param \WC_Product $product
129
     * @return array
130
     * @filter woocommerce_product_get_rating_counts
131
     */
132
    public function filterProductRatingCounts($value, $product)
133
    {
134
        return glsr_get_ratings(['assigned_posts' => $product->get_id()])->ratings;
135
    }
136
137
    /**
138
     * @param mixed $value
139
     * @param \WC_Product $product
140
     * @return int
141
     * @filter woocommerce_product_get_review_count
142
     */
143
    public function filterProductReviewCount($value, $product)
144
    {
145
        return Cast::toInt(get_post_meta($product->get_id(), CountManager::META_REVIEWS, true));
146
    }
147
148
    /**
149
     * @param array $tabs
150
     * @return array
151
     * @filter woocommerce_product_tabs
152
     */
153
    public function filterProductTabs($tabs)
154
    {
155
        global $product;
156
        if ($product->get_reviews_allowed()) {
157
            $ratings = glsr_get_ratings(['assigned_posts' => 'post_id']);
158
            $tabs['reviews'] = [
159
                'callback' => [$this, 'renderReviews'],
160
                'priority' => 30,
161
                'title' => sprintf(__('Reviews (%d)', 'site-reviews'), $ratings->reviews),
162
            ];
163
        }
164
        return $tabs;
165
    }
166
167
    /**
168
     * @param array $taxQuery
169
     * @return array
170
     * @filter woocommerce_product_query_tax_query
171
     */
172
    public function filterProductTaxQuery($taxQuery)
173
    {
174
        foreach ($taxQuery as $key => $query) {
175
            if (!empty($query['rating_filter'])) {
176
                $filteredRatings = [];
177
                $field = Arr::get($query, 'field');
178
                $taxonomy = Arr::get($query, 'taxonomy');
179
                foreach (Arr::consolidate(Arr::get($query, 'terms')) as $value) {
180
                    $term = get_term_by($field, $value, $taxonomy);
181
                    $filteredRatings[] = Cast::toInt(Str::removePrefix(Arr::get($term, 'slug'), 'rated-'));
182
                }
183
                unset($taxQuery[$key]);
184
                break;
185
            }
186
        }
187
        if (!empty($filteredRatings)) {
188
            $this->setMetaQueriesForFilteredRatings($filteredRatings);
189
        }
190
        return $taxQuery;
191
    }
192
193
    /**
194
     * @param array $markup
195
     * @param \WC_Product $product
196
     * @return array
197
     * @filter woocommerce_structured_data_product
198
     */
199
    public function filterStructuredData($markup, $product)
200
    {
201
        $args = glsr(SiteReviewsDefaults::class)->merge([
202
            'assigned_posts' => $product->get_id(),
203
            'display' => 5, // only get the latest 5 reviews
204
            'rating' => 1, // minimum rating
205
        ]);
206
        $schema = glsr(Schema::class)->build($args, glsr_get_reviews($args));
207
        if (array_key_exists('review', $schema)) {
208
            $markup['review'] = $schema['review'];
209
        } else {
210
            unset($markup['review']);
211
        }
212
        return $markup;
213
    }
214
215
    /**
216
     * @param array $args
217
     * @return array
218
     * @filter woocommerce_top_rated_products_widget_args
219
     */
220
    public function filterWidgetArgsTopRatedProducts($args)
221
    {
222
        if ('bayesian' === glsr_get_option('addons.woocommerce.sorting')) {
223
            $args['meta_query'][] = $this->buildMetaQuery('glsr_ranking', CountManager::META_RANKING);
224
            $args['orderby'] = ['glsr_ranking' => 'DESC'];
225
        } else {
226
            $args['meta_query'][] = $this->buildMetaQuery('glsr_average', CountManager::META_AVERAGE);
227
            $args['meta_query'][] = $this->buildMetaQuery('glsr_reviews', CountManager::META_REVIEWS);
228
            $args['orderby'] = ['glsr_average' => 'DESC', 'glsr_reviews' => 'DESC'];
229
        }
230
        return $args;
231
    }
232
233
    /**
234
     * @param string $template
235
     * @param string $templateName
236
     * @return string
237
     * @filter wc_get_template
238
     */
239
    public function filterWoocommerceTemplate($template, $templateName)
240
    {
241
        if ('loop/rating.php' === $templateName) {
242
            return glsr()->path('views/integrations/woocommerce/overrides/loop-rating.php');
243
        }
244
        if ('single-product-reviews.php' === $templateName) {
245
            return glsr()->path('views/integrations/woocommerce/overrides/single-product-reviews.php');
246
        }
247
        return $template;
248
    }
249
250
    /**
251
     * @param \WP_Query $query
252
     * @return void
253
     * @action pre_get_posts
254
     */
255
    public function modifyProductQuery($query)
256
    {
257
        if (!is_a($query, 'Automattic\WooCommerce\Blocks\Utils\BlocksWpQuery')) {
258
            return;
259
        }
260
        if ('rating' !== $query->get('orderby')) {
261
            return;
262
        }
263
        $metaQuery = $query->get('meta_query');
264
        if (empty($metaQuery)) {
265
            $metaQuery = [];
266
        }
267
        if ('bayesian' === glsr_get_option('addons.woocommerce.sorting')) {
268
            $metaQuery[] = $this->buildMetaQuery('glsr_ranking', CountManager::META_RANKING);
269
            $query->set('meta_query', $metaQuery);
270
            $query->set('orderby', ['glsr_ranking' => 'DESC']);
271
        } else {
272
            $metaQuery[] = $this->buildMetaQuery('glsr_average', CountManager::META_AVERAGE);
273
            $metaQuery[] = $this->buildMetaQuery('glsr_reviews', CountManager::META_REVIEWS);
274
            $query->set('meta_query', $metaQuery);
275
            $query->set('orderby', ['glsr_average' => 'DESC', 'glsr_reviews' => 'DESC']);
276
        }
277
    }
278
279
    /**
280
     * @action admin_head
281
     */
282
    public function printInlineStyle(): void
283
    {
284
        echo '<style type="text/css">#woocommerce-product-data ul.wc-tabs li.site-reviews_tab a::before { content: "\f459"; }</style>';
285
    }
286
287
    /**
288
     * @return void
289
     * @action woocommerce_after_shop_loop_item_title
290
     */
291
    public function renderLoopRating()
292
    {
293
        global $product;
294
        if (!wc_review_ratings_enabled()) {
295
            return;
296
        }
297
        $ratings = glsr_get_ratings(['assigned_posts' => 'post_id']);
298
        if (0 >= $ratings->average && 'no' === glsr_get_option('addons.woocommerce.display_empty')) {
299
            return;
300
        }
301
        glsr(Template::class)->render('templates/woocommerce/loop/rating', [
302
            'product' => $product,
303
            'ratings' => $ratings,
304
            'style' => 'glsr glsr-'.glsr(Style::class)->styleClasses(),
305
            'theme' => glsr_get_option('addons.woocommerce.style'),
306
        ]);
307
    }
308
309
    /**
310
     * @action woocommerce_product_data_panels
311
     */
312
    public function renderProductDataPanel(): void
313
    {
314
        global $product_object;
315
        glsr(Template::class)->render('views/integrations/woocommerce/product-data-panel', [
316
            'product' => $product_object,
317
        ]);
318
    }
319
320
    /**
321
     * @return void
322
     * @see $this->filterProductTabs()
323
     */
324
    public function renderReviews()
325
    {
326
        global $product;
327
        if ($product->get_reviews_allowed()) {
328
            $isVerifiedOwner = wc_customer_bought_product('', get_current_user_id(), $product->get_id());
329
            glsr(Template::class)->render('templates/woocommerce/reviews', [
330
                'form' => do_shortcode($this->option($product, 'form')),
331
                'product' => $product,
332
                'ratings' => glsr_get_ratings(['assigned_posts' => 'post_id']),
333
                'reviews' => do_shortcode($this->option($product, 'reviews')),
334
                'summary' => do_shortcode($this->option($product, 'summary')),
335
                'verified' => $isVerifiedOwner || 'no' === get_option('woocommerce_review_rating_verification_required'),
336
            ]);
337
        }
338
    }
339
340
    /**
341
     * @return void
342
     * @action woocommerce_single_product_summary
343
     */
344
    public function renderTitleRating()
345
    {
346
        global $product;
347
        $ratings = glsr_get_ratings(['assigned_posts' => 'post_id']);
348
        if (0 >= $ratings->average && 'no' === glsr_get_option('addons.woocommerce.display_empty')) {
349
            return;
350
        }
351
        glsr(Template::class)->render('templates/woocommerce/rating', [
352
            'product' => $product,
353
            'ratings' => $ratings,
354
            'style' => 'glsr glsr-'.glsr(Style::class)->styleClasses(),
355
            'theme' => glsr_get_option('addons.woocommerce.style'),
356
        ]);
357
    }
358
359
    /**
360
     * @action woocommerce_admin_process_product_object
361
     */
362
    public function updateProductData(\WC_Product $product): void
363
    {
364
        $shortcodes = [
365
            'site_reviews',
366
            'site_reviews_form',
367
            'site_reviews_summary',
368
        ];
369
        foreach ($shortcodes as $shortcode) {
370
            $value = trim(filter_input(INPUT_POST, $shortcode));
371
            $value = glsr(Sanitizer::class)->sanitizeText($value);
372
            if (empty($value)) {
373
                $product->delete_meta_data($shortcode);
374
                continue;
375
            }
376
            if (1 !== preg_match("/^\[{$shortcode}(\s[^\]]*\]|\])$/", $value)) {
377
                continue;
378
            }
379
            if (!str_contains($value, 'assigned_posts')) {
380
                $value = str_replace($shortcode, sprintf('%s assigned_posts="post_id"', $shortcode), $value);
381
            }
382
            $product->update_meta_data($shortcode, $value);
383
        }
384
    }
385
386
    protected function buildMetaQuery(string $orderbyKey, string $metaKey): array
387
    {
388
        return [
389
            'relation' => 'OR',
390
            $orderbyKey => ['key' => $metaKey, 'compare' => 'NOT EXISTS'], // this comes first!
391
            ['key' => $metaKey, 'compare' => 'EXISTS'],
392
        ];
393
    }
394
395
    protected function option(\WC_Product $product, string $key): string
396
    {
397
        $shortcodes = [
398
            'form' => 'site_reviews_form',
399
            'reviews' => 'site_reviews',
400
            'summary' => 'site_reviews_summary',
401
        ];
402
        if (!array_key_exists($key, $shortcodes)) {
403
            return '';
404
        }
405
        if ($override = $product->get_meta($shortcodes[$key])) {
406
            return $override;
407
        }
408
        return glsr_get_option('addons.woocommerce.'.$key);
409
    }
410
411
    /**
412
     * @param int[] $ratings
413
     */
414
    protected function setMetaQueriesForFilteredRatings(array $ratings): void
415
    {
416
        global $wp_query;
417
        $ratings = Arr::uniqueInt($ratings);
418
        if (!empty($ratings)) {
419
            $metaQuery = Arr::consolidate($wp_query->get('meta_query'));
420
            $metaQueries = ['relation' => 'OR'];
421
            foreach ($ratings as $rating) {
422
                $metaQueries[] = [
423
                    'key' => CountManager::META_AVERAGE,
424
                    'compare' => 'BETWEEN',
425
                    'value' => [$rating - .5, $rating + .49], // compare the rating to a rounded average range
426
                ];
427
            }
428
            $metaQuery[] = $metaQueries;
429
            $wp_query->set('meta_query', $metaQuery);
430
        }
431
    }
432
}
433