Test Failed
Push — develop ( 289f4b...2384b4 )
by Paul
09:15 queued 10s
created

ProductController   A

Complexity

Total Complexity 37

Size/Duplication

Total Lines 251
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 37
eloc 120
c 1
b 0
f 0
dl 0
loc 251
rs 9.44

12 Methods

Rating   Name   Duplication   Size   Complexity  
A buildMetaQuery() 0 6 1
A parseProductQuery() 0 19 4
A registerBlockPatterns() 0 8 1
A filterReviewCallbackHasProductOwner() 0 16 4
A filterReviewAuthorTagValue() 0 11 3
A registerBlocks() 0 4 1
A verifyProductOwner() 0 3 1
B filterBlockRenderCallback() 0 40 7
A filterAssignedPostsPostId() 0 21 5
A filterReviewFormBuild() 0 14 4
A registerProductAttributes() 0 12 2
A isProductOwner() 0 16 4
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Integrations\SureCart\Controllers;
4
5
use GeminiLabs\SiteReviews\Contracts\ControllerContract;
6
use GeminiLabs\SiteReviews\Database\CountManager;
7
use GeminiLabs\SiteReviews\HookProxy;
8
use GeminiLabs\SiteReviews\Modules\Html\Builder;
9
use GeminiLabs\SiteReviews\Modules\Html\Tags\ReviewTag;
10
use GeminiLabs\SiteReviews\Review;
11
use SureCart\Models\Product;
0 ignored issues
show
Bug introduced by
The type SureCart\Models\Product was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
use SureCart\Models\Purchase;
0 ignored issues
show
Bug introduced by
The type SureCart\Models\Purchase was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use SureCart\Models\User;
0 ignored issues
show
Bug introduced by
The type SureCart\Models\User was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
15
class ProductController implements ControllerContract
16
{
17
    use HookProxy;
18
19
    /**
20
     * @filter render_block_core/shortcode
21
     */
22
    public function filterAssignedPostsPostId(string $content, array $parsedBlock, ?\WP_Block $parentBlock): string
0 ignored issues
show
Unused Code introduced by
The parameter $parsedBlock is not used and could be removed. ( Ignorable by Annotation )

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

22
    public function filterAssignedPostsPostId(string $content, /** @scrutinizer ignore-unused */ array $parsedBlock, ?\WP_Block $parentBlock): string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
23
    {
24
        $postId = $parentBlock->context['postId'] ?? 0;
25
        $postType = $parentBlock->context['postType'] ?? '';
26
        if ('sc_product' !== $postType) {
27
            return $content;
28
        }
29
        if (!str_contains($content, '[site_review')) {
30
            return $content;
31
        }
32
        if (!str_contains($content, 'assigned_posts')) {
33
            return $content;
34
        }
35
        if (!str_contains($content, 'post_id')) {
36
            return $content;
37
        }
38
        $pattern = '/(assigned_posts\s*=\s*(["\']?))(.*?)\2(?=\s|$)/';
39
        return preg_replace_callback($pattern, function ($match) use ($postId) {
40
            $value = preg_replace('/\bpost_id\b/', $postId, $match[3]);
41
            return $match[1].$value.($match[2] ?? '');
42
        }, $content);
43
    }
44
45
    /**
46
     * @filter block_type_metadata_settings
47
     */
48
    public function filterBlockRenderCallback(array $settings, array $metadata): array
49
    {
50
        $name = $metadata['name'] ?? '';
51
        $targets = [
52
            'surecart/product-list-sort',
53
            'surecart/product-list-sort-radio-group-template',
54
        ];
55
        if (!in_array($name, $targets)) {
56
            return $settings;
57
        }
58
        $controllerPath = wp_normalize_path(
59
            realpath(dirname($metadata['file']).'/'.remove_block_asset_path_prefix('file:./controller.php'))
60
        );
61
        if (!file_exists($controllerPath)) {
62
            return $settings;
63
        }
64
        $settings['render_callback'] = static function ($attributes, $content, $block) use ($controllerPath, $metadata) {
65
            $view = require $controllerPath;
66
            $templatePath = wp_normalize_path(
67
                realpath(dirname($metadata['file']).'/'.remove_block_asset_path_prefix($view))
68
            );
69
            if (isset($options) && isset($params)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $params seems to never exist and therefore isset should always be false.
Loading history...
Comprehensibility Best Practice introduced by
The variable $options seems to never exist and therefore isset should always be false.
Loading history...
70
                $options[] = [
71
                    'checked' => 'asc' === $query_order && 'rating' === $query_order_by,
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $query_order_by seems to be never defined.
Loading history...
Comprehensibility Best Practice introduced by
The variable $query_order seems to be never defined.
Loading history...
72
                    'href' => $params->addArg('order', 'asc')->addArg('orderby', 'rating')->url(),
73
                    'label' => esc_html__('Rating, low to high', 'site-reviews'),
74
                    'value' => 'rating:asc',
75
                ];
76
                $options[] = [
77
                    'checked' => 'desc' === $query_order && 'rating' === $query_order_by,
78
                    'href' => $params->addArg('order', 'desc')->addArg('orderby', 'rating')->url(),
79
                    'label' => esc_html__('Rating, high to low', 'site-reviews'),
80
                    'value' => 'rating:desc',
81
                ];
82
            }
83
            ob_start();
84
            require $templatePath;
85
            return ob_get_clean();
86
        };
87
        return $settings;
88
    }
89
90
    /**
91
     * @filter site-reviews/review/value/author
92
     */
93
    public function filterReviewAuthorTagValue(string $value, ReviewTag $tag): string
94
    {
95
        $ownership = glsr_get_option('integrations.surecart.ownership', [], 'array');
96
        if (!in_array('labeled', $ownership)) {
97
            return $value;
98
        }
99
        if ($tag->review->hasProductOwner()) {
0 ignored issues
show
Bug introduced by
The method hasProductOwner() does not exist on GeminiLabs\SiteReviews\Review. 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

99
        if ($tag->review->/** @scrutinizer ignore-call */ hasProductOwner()) {
Loading history...
100
            $text = esc_attr__('verified owner', 'site-reviews');
101
            $value = sprintf('%s <em data-verified-owner="1">(%s)</em>', $value, $text);
102
        }
103
        return $value;
104
    }
105
106
    /**
107
     * @filter site-reviews/review/call/hasProductOwner
108
     */
109
    public function filterReviewCallbackHasProductOwner(Review $review): bool
110
    {
111
        $verified = get_post_meta($review->ID, '_sc_verified', true);
112
        if ('' !== $verified) {
113
            return (bool) $verified;
114
        }
115
        $review->refresh(); // refresh the review first!
116
        $verified = false;
117
        foreach ($review->assigned_posts as $postId) {
118
            if ('sc_product' === get_post_type($postId)) {
119
                $verified = $this->isProductOwner($review->author_id, $postId);
120
                break; // only check the first product
121
            }
122
        }
123
        update_post_meta($review->ID, '_sc_verified', (int) $verified);
124
        return $verified;
125
    }
126
127
    /**
128
     * @filter site-reviews/build/template/reviews-form
129
     */
130
    public function filterReviewFormBuild(string $template, array $data): string
0 ignored issues
show
Unused Code introduced by
The parameter $data is not used and could be removed. ( Ignorable by Annotation )

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

130
    public function filterReviewFormBuild(string $template, /** @scrutinizer ignore-unused */ array $data): string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
131
    {
132
        if ('sc_product' !== get_post_type()) {
133
            return $template;
134
        }
135
        $ownership = glsr_get_option('integrations.surecart.ownership', [], 'array');
136
        if (!in_array('restricted', $ownership)) {
137
            return $template;
138
        }
139
        if ($this->isProductOwner(get_current_user_id(), get_the_ID())) {
0 ignored issues
show
Bug introduced by
It seems like get_the_ID() can also be of type false; however, parameter $productId of GeminiLabs\SiteReviews\I...oller::isProductOwner() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

139
        if ($this->isProductOwner(get_current_user_id(), /** @scrutinizer ignore-type */ get_the_ID())) {
Loading history...
140
            return $template;
141
        }
142
        return glsr(Builder::class)->p([
143
            'text' => esc_html__('Only logged in customers who have purchased this product may leave a review.', 'woocommerce'),
144
        ]);
145
    }
146
147
    /**
148
     * @action parse_query
149
     */
150
    public function parseProductQuery(\WP_Query $query): void
151
    {
152
        if ('sc_product' !== $query->get('post_type')) {
153
            return;
154
        }
155
        if ('rating' !== $query->get('orderby')) {
156
            return;
157
        }
158
        $metaQuery = $query->get('meta_query', []);
159
        $order = $query->get('order', 'desc');
160
        if ('bayesian' === glsr_get_option('integrations.surecart.sorting')) {
161
            $metaQuery[] = $this->buildMetaQuery('glsr_ranking', CountManager::META_RANKING);
162
            $query->set('meta_query', $metaQuery);
163
            $query->set('orderby', ['glsr_ranking' => $order]);
164
        } else {
165
            $metaQuery[] = $this->buildMetaQuery('glsr_average', CountManager::META_AVERAGE);
166
            $metaQuery[] = $this->buildMetaQuery('glsr_reviews', CountManager::META_REVIEWS);
167
            $query->set('meta_query', $metaQuery);
168
            $query->set('orderby', ['glsr_average' => $order, 'glsr_reviews' => $order]);
169
        }
170
    }
171
172
    /**
173
     * @action init:11
174
     */
175
    public function registerBlocks(): void
176
    {
177
        register_block_type_from_metadata(glsr()->path('assets/blocks/surecart_product_rating'));
178
        register_block_type_from_metadata(glsr()->path('assets/blocks/surecart_product_reviews'));
179
    }
180
181
    /**
182
     * @action init
183
     */
184
    public function registerBlockPatterns(): void
185
    {
186
        register_block_pattern(glsr()->id.'/surecart-product-reviews', [
187
            'title' => _x('Product Reviews', 'admin-text', 'site-reviews'),
188
            'categories' => ['surecart_product_page'],
189
            'blockTypes' => ['surecart/product-page'],
190
            'priority' => 2,
191
            'content' => '
192
                <!-- wp:group {"layout":{"type":"constrained"}} -->
193
                <div class="wp-block-group">
194
                    <!-- wp:columns {"align":"wide"} -->
195
                    <div class="wp-block-columns alignwide">
196
                        <!-- wp:column {"width":"100%","className":"is-style-default","layout":{"type":"default"}} -->
197
                        <div class="wp-block-column is-style-default" style="flex-basis:100%">
198
                            <!-- wp:heading {"className":"is-style-text-subtitle"} -->
199
                            <h2 class="wp-block-heading is-style-text-subtitle">Reviews</h2>
200
                            <!-- /wp:heading -->
201
                            <!-- wp:site-reviews/reviews {"assigned_posts":["post_id"],"hide":["title"],"id":"reviews","pagination":"ajax","schema":1} /-->
202
                            <!-- wp:heading {"className":"is-style-text-subtitle"} -->
203
                            <h2 class="wp-block-heading is-style-text-subtitle">Submit a Review</h2>
204
                            <!-- /wp:heading -->
205
                            <!-- wp:site-reviews/form {"assigned_posts":["post_id"],"hide":["name","email","title"],"reviews_id":"reviews"} /--></div>
206
                        <!-- /wp:column -->
207
                        </div>
208
                    <!-- /wp:columns -->
209
                    </div>
210
                <!-- /wp:group -->',
211
            // 'postTypes' => ['sc_product'],
212
            // 'templateTypes' => ['sc_product'],
213
        ]);
214
    }
215
216
    /**
217
     * @filter surecart/product/attributes_set
218
     */
219
    public function registerProductAttributes(Product $product): void
220
    {
221
        $postId = $product->post->ID ?? 0;
222
        if (0 === $postId) {
223
            return;
224
        }
225
        $ratingInfo = glsr_get_ratings([
226
            'assigned_posts' => $postId,
227
        ]);
228
        $product->setAttribute('rating', $ratingInfo->average);
229
        $product->setAttribute('reviews', $ratingInfo->reviews);
230
        $product->setAttribute('ranking', $ratingInfo->ranking);
231
    }
232
233
    /**
234
     * @action site-reviews/review/created
235
     */
236
    public function verifyProductOwner(Review $review): void
237
    {
238
        $review->hasProductOwner();
239
    }
240
241
    protected function buildMetaQuery(string $orderbyKey, string $metaKey): array
242
    {
243
        return [
244
            'relation' => 'OR',
245
            $orderbyKey => ['key' => $metaKey, 'compare' => 'NOT EXISTS'], // this comes first!
246
            ['key' => $metaKey, 'compare' => 'EXISTS'],
247
        ];
248
    }
249
250
    protected function isProductOwner(int $userId, int $productId): bool
251
    {
252
        if (!$user = User::getUserBy('id', $userId)) {
253
            return false;
254
        }
255
        if (!$customer = $user->customer()) {
256
            return false;
257
        }
258
        if (!$product = sc_get_product($productId)) {
0 ignored issues
show
Bug introduced by
The function sc_get_product was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

258
        if (!$product = /** @scrutinizer ignore-call */ sc_get_product($productId)) {
Loading history...
259
            return false;
260
        }
261
        $purchases = Purchase::where([
262
            'customer_ids' => [$customer->id],
263
            'product_ids' => [$product->id],
264
        ])->get();
265
        return !empty($purchases);
266
    }
267
}
268