| Total Complexity | 59 | 
| Total Lines | 376 | 
| Duplicated Lines | 0 % | 
| Changes | 1 | ||
| Bugs | 0 | Features | 0 | 
Complex classes like ProductController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use ProductController, and based on these observations, apply Extract Interface, too.
| 1 | <?php  | 
            ||
| 21 | class ProductController implements ControllerContract  | 
            ||
| 22 | { | 
            ||
| 23 | use HookProxy;  | 
            ||
| 24 | |||
| 25 | /**  | 
            ||
| 26 | * @filter render_block_core/shortcode  | 
            ||
| 27 | */  | 
            ||
| 28 | public function filterAssignedPostsPostId(string $content, array $parsedBlock, ?\WP_Block $parentBlock): string  | 
            ||
| 
                                                                                                    
                         1 ignored issue 
                            –
                            show
                         | 
                |||
| 29 |     { | 
            ||
| 30 | $postId = $parentBlock->context['postId'] ?? 0;  | 
            ||
| 31 | $postType = $parentBlock->context['postType'] ?? '';  | 
            ||
| 32 |         if ('sc_product' !== $postType) { | 
            ||
| 33 | return $content;  | 
            ||
| 34 | }  | 
            ||
| 35 |         if (!str_contains($content, '[site_review')) { | 
            ||
| 36 | return $content;  | 
            ||
| 37 | }  | 
            ||
| 38 |         if (!str_contains($content, 'assigned_posts')) { | 
            ||
| 39 | return $content;  | 
            ||
| 40 | }  | 
            ||
| 41 |         if (!str_contains($content, 'post_id')) { | 
            ||
| 42 | return $content;  | 
            ||
| 43 | }  | 
            ||
| 44 | $pattern = '/(assigned_posts\s*=\s*(["\']?))(.*?)\2(?=\s|$)/';  | 
            ||
| 45 |         return preg_replace_callback($pattern, function ($match) use ($postId) { | 
            ||
| 46 |             $value = preg_replace('/\bpost_id\b/', $postId, $match[3]); | 
            ||
| 47 | return $match[1].$value.$match[2];  | 
            ||
| 48 | }, $content);  | 
            ||
| 49 | }  | 
            ||
| 50 | |||
| 51 | /**  | 
            ||
| 52 | * @filter block_type_metadata_settings  | 
            ||
| 53 | */  | 
            ||
| 54 | public function filterBlockRenderCallback(array $settings, array $metadata): array  | 
            ||
| 55 |     { | 
            ||
| 56 | $name = $metadata['name'] ?? '';  | 
            ||
| 57 | $targets = [  | 
            ||
| 58 | 'surecart/product-list-sort',  | 
            ||
| 59 | 'surecart/product-list-sort-radio-group-template',  | 
            ||
| 60 | ];  | 
            ||
| 61 |         if (!in_array($name, $targets)) { | 
            ||
| 62 | return $settings;  | 
            ||
| 63 | }  | 
            ||
| 64 | $controllerPath = wp_normalize_path(  | 
            ||
| 65 |             realpath(dirname($metadata['file']).'/'.remove_block_asset_path_prefix('file:./controller.php')) | 
            ||
| 66 | );  | 
            ||
| 67 |         if (!file_exists($controllerPath)) { | 
            ||
| 68 | return $settings;  | 
            ||
| 69 | }  | 
            ||
| 70 |         $settings['render_callback'] = static function ($attributes, $content, $block) use ($controllerPath, $metadata) { | 
            ||
| 71 | $view = require $controllerPath;  | 
            ||
| 72 | $templatePath = wp_normalize_path(  | 
            ||
| 73 | realpath(dirname($metadata['file']).'/'.remove_block_asset_path_prefix($view))  | 
            ||
| 74 | );  | 
            ||
| 75 | if (isset($options)  | 
            ||
| 
                                                                                                    
                         1 ignored issue 
                            –
                            show
                         | 
                |||
| 76 | && isset($params)  | 
            ||
| 
                                                                                                    
                         1 ignored issue 
                            –
                            show
                         | 
                |||
| 77 | && isset($query_order)  | 
            ||
| 
                                                                                                    
                         1 ignored issue 
                            –
                            show
                         | 
                |||
| 78 |                 && isset($query_order_by)) { | 
            ||
| 
                                                                                                    
                         1 ignored issue 
                            –
                            show
                         | 
                |||
| 79 | $options[] = [  | 
            ||
| 80 | 'checked' => 'asc' === $query_order && 'rating' === $query_order_by,  | 
            ||
| 81 |                     'href' => $params->addArg('order', 'asc')->addArg('orderby', 'rating')->url(), | 
            ||
| 82 |                     'label' => esc_html__('Rating, low to high', 'site-reviews'), | 
            ||
| 83 | 'value' => 'rating:asc',  | 
            ||
| 84 | ];  | 
            ||
| 85 | $options[] = [  | 
            ||
| 86 | 'checked' => 'desc' === $query_order && 'rating' === $query_order_by,  | 
            ||
| 87 |                     'href' => $params->addArg('order', 'desc')->addArg('orderby', 'rating')->url(), | 
            ||
| 88 |                     'label' => esc_html__('Rating, high to low', 'site-reviews'), | 
            ||
| 89 | 'value' => 'rating:desc',  | 
            ||
| 90 | ];  | 
            ||
| 91 | }  | 
            ||
| 92 | ob_start();  | 
            ||
| 93 | require $templatePath;  | 
            ||
| 94 | return ob_get_clean();  | 
            ||
| 95 | };  | 
            ||
| 96 | return $settings;  | 
            ||
| 97 | }  | 
            ||
| 98 | |||
| 99 | /**  | 
            ||
| 100 | * @param string[] $columns  | 
            ||
| 101 | *  | 
            ||
| 102 | * @filter manage_sc-products_columns  | 
            ||
| 103 | */  | 
            ||
| 104 | public function filterProductColumns(array $columns): array  | 
            ||
| 105 |     { | 
            ||
| 106 |         $svg = Svg::get('assets/images/icon.svg', [ | 
            ||
| 107 | 'height' => 24,  | 
            ||
| 108 | 'style' => 'display:flex; flex-shrink:0; margin: -4px 0;',  | 
            ||
| 109 | ]);  | 
            ||
| 110 | $columns[glsr()->prefix.'rating'] = glsr(Builder::class)->div([  | 
            ||
| 111 | 'style' => 'display:flex; align-items:center; justify-content:center;',  | 
            ||
| 112 |             'text' => sprintf('%s<span>%s</span>', $svg, _x('Reviews', 'admin-text', 'site-reviews')), | 
            ||
| 113 | ]);  | 
            ||
| 114 | return $columns;  | 
            ||
| 115 | }  | 
            ||
| 116 | |||
| 117 | /**  | 
            ||
| 118 | * @filter surecart/product/json_schema  | 
            ||
| 119 | */  | 
            ||
| 120 | public function filterProductSchema(array $schema): array  | 
            ||
| 121 |     { | 
            ||
| 122 | $data = glsr(SchemaParser::class)->generate();  | 
            ||
| 123 | $aggregateRatingSchema = Arr::get($data, 'aggregateRating');  | 
            ||
| 124 | $reviewSchema = Arr::get($data, 'review');  | 
            ||
| 125 |         if ($aggregateRatingSchema) { | 
            ||
| 126 | $schema['aggregateRating'] = $aggregateRatingSchema;  | 
            ||
| 127 | }  | 
            ||
| 128 |         if ($reviewSchema) { | 
            ||
| 129 | $schema['review'] = $reviewSchema;  | 
            ||
| 130 | }  | 
            ||
| 131 | // remove Site Reviews generated schema  | 
            ||
| 132 |         add_filter('site-reviews/schema/all', '__return_empty_array'); | 
            ||
| 133 | return $schema;  | 
            ||
| 134 | }  | 
            ||
| 135 | |||
| 136 | /**  | 
            ||
| 137 | * @filter site-reviews/review/value/author  | 
            ||
| 138 | */  | 
            ||
| 139 | public function filterReviewAuthorTagValue(string $value, ReviewTag $tag): string  | 
            ||
| 140 |     { | 
            ||
| 141 |         $ownership = glsr_get_option('integrations.surecart.ownership', [], 'array'); | 
            ||
| 142 |         if (!in_array('labeled', $ownership)) { | 
            ||
| 143 | return $value;  | 
            ||
| 144 | }  | 
            ||
| 145 |         if ($tag->review->hasProductOwner()) { | 
            ||
| 
                                                                                                    
                         1 ignored issue 
                            –
                            show
                         | 
                |||
| 146 |             $text = esc_attr__('verified owner', 'site-reviews'); | 
            ||
| 147 |             $value = sprintf('%s <em data-verified-owner="1">(%s)</em>', $value, $text); | 
            ||
| 148 | }  | 
            ||
| 149 | return $value;  | 
            ||
| 150 | }  | 
            ||
| 151 | |||
| 152 | /**  | 
            ||
| 153 | * @filter site-reviews/review/call/hasProductOwner  | 
            ||
| 154 | */  | 
            ||
| 155 | public function filterReviewCallbackHasProductOwner(Review $review): bool  | 
            ||
| 156 |     { | 
            ||
| 157 | $verified = get_post_meta($review->ID, '_sc_verified', true);  | 
            ||
| 158 |         if ('' !== $verified) { | 
            ||
| 159 | return (bool) $verified;  | 
            ||
| 160 | }  | 
            ||
| 161 | $review->refresh(); // refresh the review first!  | 
            ||
| 162 | $verified = false;  | 
            ||
| 163 |         foreach ($review->assigned_posts as $postId) { | 
            ||
| 164 |             if ('sc_product' === get_post_type($postId)) { | 
            ||
| 165 | $verified = $this->isProductOwner($review->author_id, $postId);  | 
            ||
| 166 | break; // only check the first product  | 
            ||
| 167 | }  | 
            ||
| 168 | }  | 
            ||
| 169 | update_post_meta($review->ID, '_sc_verified', (int) $verified);  | 
            ||
| 170 | return $verified;  | 
            ||
| 171 | }  | 
            ||
| 172 | |||
| 173 | /**  | 
            ||
| 174 | * @filter site-reviews/build/template/reviews-form  | 
            ||
| 175 | */  | 
            ||
| 176 | public function filterReviewFormBuild(string $template, array $data): string  | 
            ||
| 
                                                                                                    
                         1 ignored issue 
                            –
                            show
                         | 
                |||
| 177 |     { | 
            ||
| 178 |         if ('sc_product' !== get_post_type()) { | 
            ||
| 179 | return $template;  | 
            ||
| 180 | }  | 
            ||
| 181 |         $ownership = glsr_get_option('integrations.surecart.ownership', [], 'array'); | 
            ||
| 182 |         if (!in_array('restricted', $ownership)) { | 
            ||
| 183 | return $template;  | 
            ||
| 184 | }  | 
            ||
| 185 |         if ($this->isProductOwner(get_current_user_id(), get_the_ID())) { | 
            ||
| 186 | return $template;  | 
            ||
| 187 | }  | 
            ||
| 188 | return glsr(Builder::class)->p([  | 
            ||
| 189 |             'text' => esc_html__('Only logged in customers who have purchased this product may leave a review.', 'woocommerce'), | 
            ||
| 190 | ]);  | 
            ||
| 191 | }  | 
            ||
| 192 | |||
| 193 | /**  | 
            ||
| 194 | * @param \GeminiLabs\SiteReviews\Modules\Html\ReviewField[] $fields  | 
            ||
| 195 | *  | 
            ||
| 196 | * @return \GeminiLabs\SiteReviews\Modules\Html\ReviewField[]  | 
            ||
| 197 | *  | 
            ||
| 198 | * @filter site-reviews/review-form/fields/visible  | 
            ||
| 199 | */  | 
            ||
| 200 | public function filterReviewFormFields(array $fields, ReviewForm $form): array  | 
            ||
| 201 |     { | 
            ||
| 202 |         if (!is_user_logged_in()) { | 
            ||
| 203 | return $fields;  | 
            ||
| 204 | }  | 
            ||
| 205 |         if ('sc_product' !== get_post_type()) { | 
            ||
| 206 | return $fields;  | 
            ||
| 207 | }  | 
            ||
| 208 | $user = wp_get_current_user();  | 
            ||
| 209 |         array_walk($fields, function ($field) use ($form, $user) { | 
            ||
| 210 |             if (in_array($field->original_name, $form->args()->hide)) { | 
            ||
| 211 | return;  | 
            ||
| 212 | }  | 
            ||
| 213 |             if ('email' === $field->original_name && empty($field->value)) { | 
            ||
| 214 | $field->value = glsr(Sanitizer::class)->sanitizeUserEmail($user->user_email);  | 
            ||
| 215 | return;  | 
            ||
| 216 | }  | 
            ||
| 217 |             if ('name' === $field->original_name && empty($field->value)) { | 
            ||
| 218 | $field->value = glsr(Sanitizer::class)->sanitizeUserName(  | 
            ||
| 219 | $user->display_name,  | 
            ||
| 220 | $user->user_nicename  | 
            ||
| 221 | );  | 
            ||
| 222 | }  | 
            ||
| 223 | });  | 
            ||
| 224 | return $fields;  | 
            ||
| 225 | }  | 
            ||
| 226 | |||
| 227 | /**  | 
            ||
| 228 | * @filter site-reviews/shortcode/site_reviews/attributes  | 
            ||
| 229 | * @filter site-reviews/shortcode/site_reviews_form/attributes  | 
            ||
| 230 | * @filter site-reviews/shortcode/site_reviews_summary/attributes  | 
            ||
| 231 | */  | 
            ||
| 232 | public function filterShortcodeAttributes(array $attributes, ShortcodeContract $shortcode): array  | 
            ||
| 243 | }  | 
            ||
| 244 | |||
| 245 | /**  | 
            ||
| 246 | * @action parse_query  | 
            ||
| 247 | */  | 
            ||
| 248 | public function parseProductQuery(\WP_Query $query): void  | 
            ||
| 249 |     { | 
            ||
| 250 |         if ('sc_product' !== $query->get('post_type')) { | 
            ||
| 251 | return;  | 
            ||
| 252 | }  | 
            ||
| 253 |         if ('rating' !== $query->get('orderby')) { | 
            ||
| 254 | return;  | 
            ||
| 255 | }  | 
            ||
| 256 |         $metaQuery = $query->get('meta_query', []); | 
            ||
| 257 |         $order = $query->get('order', 'desc'); | 
            ||
| 258 |         if ('bayesian' === glsr_get_option('integrations.surecart.sorting')) { | 
            ||
| 259 |             $metaQuery[] = $this->buildMetaQuery('glsr_ranking', CountManager::META_RANKING); | 
            ||
| 260 |             $query->set('meta_query', $metaQuery); | 
            ||
| 261 |             $query->set('orderby', ['glsr_ranking' => $order]); | 
            ||
| 262 |         } else { | 
            ||
| 263 |             $metaQuery[] = $this->buildMetaQuery('glsr_average', CountManager::META_AVERAGE); | 
            ||
| 264 |             $metaQuery[] = $this->buildMetaQuery('glsr_reviews', CountManager::META_REVIEWS); | 
            ||
| 265 |             $query->set('meta_query', $metaQuery); | 
            ||
| 266 |             $query->set('orderby', ['glsr_average' => $order, 'glsr_reviews' => $order]); | 
            ||
| 267 | }  | 
            ||
| 268 | }  | 
            ||
| 269 | |||
| 270 | /**  | 
            ||
| 271 | * @action init  | 
            ||
| 272 | */  | 
            ||
| 273 | public function registerBlockPatterns(): void  | 
            ||
| 274 |     { | 
            ||
| 275 | register_block_pattern(glsr()->id.'/surecart-product-reviews', [  | 
            ||
| 276 |             'title' => _x('Product Reviews', 'admin-text', 'site-reviews'), | 
            ||
| 277 | 'categories' => ['surecart_product_page'],  | 
            ||
| 278 | 'blockTypes' => ['surecart/product-page'],  | 
            ||
| 279 | 'priority' => 2,  | 
            ||
| 280 | 'content' => '  | 
            ||
| 281 |                 <!-- wp:group {"layout":{"type":"constrained"}} --> | 
            ||
| 282 | <div class="wp-block-group">  | 
            ||
| 283 |                     <!-- wp:columns {"align":"wide"} --> | 
            ||
| 284 | <div class="wp-block-columns alignwide">  | 
            ||
| 285 |                         <!-- wp:column {"width":"100%","className":"is-style-default","layout":{"type":"default"}} --> | 
            ||
| 286 | <div class="wp-block-column is-style-default" style="flex-basis:100%">  | 
            ||
| 287 |                             <!-- wp:heading {"className":"is-style-text-subtitle"} --> | 
            ||
| 288 | <h2 class="wp-block-heading is-style-text-subtitle">Reviews</h2>  | 
            ||
| 289 | <!-- /wp:heading -->  | 
            ||
| 290 |                             <!-- wp:site-reviews/reviews {"assigned_posts":["post_id"],"hide":["title"],"id":"reviews","pagination":"ajax","schema":1} /--> | 
            ||
| 291 |                             <!-- wp:heading {"className":"is-style-text-subtitle"} --> | 
            ||
| 292 | <h2 class="wp-block-heading is-style-text-subtitle">Submit a Review</h2>  | 
            ||
| 293 | <!-- /wp:heading -->  | 
            ||
| 294 |                             <!-- wp:site-reviews/form {"assigned_posts":["post_id"],"hide":["name","email","title"],"reviews_id":"reviews"} /--></div> | 
            ||
| 295 | <!-- /wp:column -->  | 
            ||
| 296 | </div>  | 
            ||
| 297 | <!-- /wp:columns -->  | 
            ||
| 298 | </div>  | 
            ||
| 299 | <!-- /wp:group -->',  | 
            ||
| 300 | // 'postTypes' => ['sc_product'],  | 
            ||
| 301 | // 'templateTypes' => ['sc_product'],  | 
            ||
| 302 | ]);  | 
            ||
| 303 | }  | 
            ||
| 304 | |||
| 305 | /**  | 
            ||
| 306 | * @action init:11  | 
            ||
| 307 | */  | 
            ||
| 308 | public function registerBlocks(): void  | 
            ||
| 309 |     { | 
            ||
| 310 |         register_block_type_from_metadata(glsr()->path('assets/blocks/surecart_product_rating')); | 
            ||
| 311 |         register_block_type_from_metadata(glsr()->path('assets/blocks/surecart_product_reviews')); | 
            ||
| 312 | }  | 
            ||
| 313 | |||
| 314 | /**  | 
            ||
| 315 | * @filter surecart/product/attributes_set  | 
            ||
| 316 | */  | 
            ||
| 317 | public function registerProductAttributes(Product $product): void  | 
            ||
| 318 |     { | 
            ||
| 319 | $postId = $product->post->ID ?? 0;  | 
            ||
| 320 |         if (0 === $postId) { | 
            ||
| 321 | return;  | 
            ||
| 322 | }  | 
            ||
| 323 | $ratingInfo = glsr_get_ratings([  | 
            ||
| 324 | 'assigned_posts' => $postId,  | 
            ||
| 325 | ]);  | 
            ||
| 326 |         $product->setAttribute('rating', $ratingInfo->average); | 
            ||
| 327 |         $product->setAttribute('reviews', $ratingInfo->reviews); | 
            ||
| 328 |         $product->setAttribute('ranking', $ratingInfo->ranking); | 
            ||
| 329 | }  | 
            ||
| 330 | |||
| 331 | /**  | 
            ||
| 332 | * @action manage_sc-products_custom_column  | 
            ||
| 333 | */  | 
            ||
| 334 | public function renderProductColumnValues(string $column, $product): void  | 
            ||
| 335 |     { | 
            ||
| 336 |         if (glsr()->prefix.'rating' !== $column) { | 
            ||
| 337 | return;  | 
            ||
| 338 | }  | 
            ||
| 339 | echo glsr(Builder::class)->a([  | 
            ||
| 340 |             'href' => add_query_arg('assigned_post', $product->post->ID, glsr_admin_url()), | 
            ||
| 341 | 'text' => $product->reviews,  | 
            ||
| 342 | ]);  | 
            ||
| 343 | }  | 
            ||
| 344 | |||
| 345 | /**  | 
            ||
| 346 | * @param string $which  | 
            ||
| 347 | *  | 
            ||
| 348 | * @action manage_products_extra_tablenav  | 
            ||
| 349 | */  | 
            ||
| 350 | public function renderProductTableInlineStyles($which): void  | 
            ||
| 351 |     { | 
            ||
| 352 |         if ('top' !== $which) { | 
            ||
| 353 | return;  | 
            ||
| 354 | }  | 
            ||
| 355 | echo '<style>'.  | 
            ||
| 356 |         '@media screen and (min-width: 783px) {'. | 
            ||
| 357 |             '.fixed .column-glsr_rating { width: 5%; }'. | 
            ||
| 358 |             'th.column-glsr_rating span { display: none; }'. | 
            ||
| 359 |             'td.column-glsr_rating { text-align: center; }'. | 
            ||
| 360 | '}'.  | 
            ||
| 361 | '</style>';  | 
            ||
| 362 | }  | 
            ||
| 363 | |||
| 364 | /**  | 
            ||
| 365 | * @action site-reviews/review/created  | 
            ||
| 366 | */  | 
            ||
| 367 | public function verifyProductOwner(Review $review): void  | 
            ||
| 368 |     { | 
            ||
| 369 | $review->hasProductOwner();  | 
            ||
| 370 | }  | 
            ||
| 371 | |||
| 372 | protected function buildMetaQuery(string $orderbyKey, string $metaKey): array  | 
            ||
| 378 | ];  | 
            ||
| 379 | }  | 
            ||
| 380 | |||
| 381 | protected function isProductOwner(int $userId, int $productId): bool  | 
            ||
| 382 |     { | 
            ||
| 383 |         if (!$user = User::getUserBy('id', $userId)) { // @phpstan-ignore-line | 
            ||
| 384 | return false;  | 
            ||
| 385 | }  | 
            ||
| 386 |         if (!$customer = $user->customer()) { // @phpstan-ignore-line | 
            ||
| 387 | return false;  | 
            ||
| 388 | }  | 
            ||
| 389 |         if (!$product = sc_get_product($productId)) { | 
            ||
| 390 | return false;  | 
            ||
| 391 | }  | 
            ||
| 392 | $purchases = Purchase::where([ // @phpstan-ignore-line  | 
            ||
| 393 | 'customer_ids' => [$customer->id],  | 
            ||
| 399 | 
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:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths