| Total Complexity | 75 |
| Total Lines | 429 |
| Duplicated Lines | 0 % |
| Coverage | 25.84% |
| Changes | 3 | ||
| Bugs | 1 | Features | 0 |
Complex classes like ReviewController 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 ReviewController, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 29 | class ReviewController extends AbstractController |
||
| 30 | { |
||
| 31 | /** |
||
| 32 | * @param \WP_Post[] $posts |
||
| 33 | * |
||
| 34 | * @return \WP_Post[] |
||
| 35 | * |
||
| 36 | * @filter the_posts |
||
| 37 | */ |
||
| 38 | public function filterPostsToCacheReviews(array $posts): array |
||
| 39 | { |
||
| 40 | $reviews = array_filter($posts, fn ($post) => glsr()->post_type === $post->post_type); |
||
| 41 | if ($postIds = wp_list_pluck($reviews, 'ID')) { |
||
| 42 | glsr(Query::class)->reviews([], $postIds); // this caches the associated Review objects |
||
| 43 | } |
||
| 44 | return $posts; |
||
| 45 | } |
||
| 46 | |||
| 47 | /** |
||
| 48 | * @filter wp_insert_post_data |
||
| 49 | */ |
||
| 50 | 28 | public function filterReviewPostData(array $data, array $sanitized): array |
|
| 51 | { |
||
| 52 | 28 | if (empty($sanitized['ID']) || empty($sanitized['action']) || glsr()->post_type !== Arr::get($sanitized, 'post_type')) { |
|
| 53 | 28 | return $data; |
|
| 54 | } |
||
| 55 | if (!empty(filter_input(INPUT_GET, 'bulk_edit'))) { |
||
| 56 | if (is_numeric(filter_input(INPUT_GET, 'post_author'))) { |
||
| 57 | $data['post_author'] = filter_input(INPUT_GET, 'post_author'); |
||
| 58 | } else { |
||
| 59 | unset($data['post_author']); |
||
| 60 | } |
||
| 61 | } |
||
| 62 | if (is_numeric(filter_input(INPUT_POST, 'post_author_override'))) { |
||
| 63 | // use the value from the author meta box |
||
| 64 | $data['post_author'] = filter_input(INPUT_POST, 'post_author_override'); |
||
| 65 | } |
||
| 66 | return $data; |
||
| 67 | } |
||
| 68 | |||
| 69 | /** |
||
| 70 | * @filter site-reviews/rendered/template/review |
||
| 71 | */ |
||
| 72 | 8 | public function filterReviewTemplate(string $template, array $data): string |
|
| 73 | { |
||
| 74 | 8 | $search = 'id="review-'; |
|
| 75 | 8 | $dataType = Arr::get($data, 'review.type', 'local'); |
|
| 76 | 8 | $replace = sprintf('data-type="%s" %s', $dataType, $search); |
|
| 77 | 8 | if (Arr::get($data, 'review.is_pinned')) { |
|
| 78 | $replace = 'data-pinned="1" '.$replace; |
||
| 79 | } |
||
| 80 | 8 | if (Arr::get($data, 'review.is_verified')) { |
|
| 81 | $replace = 'data-verified="1" '.$replace; |
||
| 82 | } |
||
| 83 | 8 | return str_replace($search, $replace, $template); |
|
| 84 | } |
||
| 85 | |||
| 86 | /** |
||
| 87 | * @filter site-reviews/query/sql/clause/operator |
||
| 88 | */ |
||
| 89 | 8 | public function filterSqlClauseOperator(string $operator): string |
|
| 90 | { |
||
| 91 | 8 | $operators = ['loose' => 'OR', 'strict' => 'AND']; |
|
| 92 | 8 | return Arr::get($operators, glsr_get_option('reviews.assignment', 'strict', 'string'), $operator); |
|
| 93 | } |
||
| 94 | |||
| 95 | /** |
||
| 96 | * @filter site-reviews/review/build/after |
||
| 97 | */ |
||
| 98 | 8 | public function filterTemplateTags(array $tags, Review $review, ReviewHtml $reviewHtml): array |
|
| 99 | { |
||
| 100 | 8 | $tags['assigned_links'] = $reviewHtml->buildTemplateTag($review, 'assigned_links', $review->assigned_posts); |
|
| 101 | 8 | return $tags; |
|
| 102 | } |
||
| 103 | |||
| 104 | /** |
||
| 105 | * Triggered after one or more categories are added or removed from a review. |
||
| 106 | * |
||
| 107 | * @action set_object_terms |
||
| 108 | */ |
||
| 109 | 28 | public function onAfterChangeAssignedTerms( |
|
| 110 | int $postId, |
||
| 111 | array $terms, |
||
| 112 | array $newTTIds, |
||
| 113 | string $taxonomy, |
||
| 114 | bool $append, |
||
| 115 | array $oldTTIds |
||
| 116 | ): void { |
||
| 117 | 28 | if (Review::isReview($postId)) { |
|
| 118 | 24 | $review = glsr(ReviewManager::class)->get($postId); |
|
| 119 | 24 | $diff = $this->getAssignedDiffs($oldTTIds, $newTTIds); |
|
| 120 | 24 | $this->execute(new UnassignTerms($review, $diff['old'])); |
|
| 121 | 24 | $this->execute(new AssignTerms($review, $diff['new'])); |
|
| 122 | } |
||
| 123 | } |
||
| 124 | |||
| 125 | /** |
||
| 126 | * Triggered when a post status changes or when a review is approved|unapproved|trashed. |
||
| 127 | * |
||
| 128 | * @action transition_post_status |
||
| 129 | */ |
||
| 130 | 28 | public function onAfterChangeStatus(string $new, string $old, ?\WP_Post $post): void |
|
| 131 | { |
||
| 132 | 28 | if (is_null($post)) { |
|
| 133 | 28 | return; // This should never happen, but some plugins are bad actors so... |
|
| 134 | } |
||
| 135 | if (in_array($old, ['new', $new])) { |
||
| 136 | return; |
||
| 137 | } |
||
| 138 | if (Review::isReview($post)) { |
||
| 139 | $isAutoDraft = 'auto-draft' === $old && 'auto-draft' !== $new; |
||
| 140 | if ($isAutoDraft) { |
||
| 141 | glsr(ReviewManager::class)->createFromPost($post->ID); |
||
| 142 | } |
||
| 143 | $isPublished = 'publish' === $new; |
||
| 144 | glsr(ReviewManager::class)->updateRating($post->ID, ['is_approved' => $isPublished]); |
||
| 145 | glsr(Cache::class)->delete($post->ID, 'reviews'); |
||
| 146 | glsr(CountManager::class)->recalculate(); |
||
| 147 | if ($isAutoDraft) { |
||
| 148 | return; |
||
| 149 | } |
||
| 150 | $review = glsr_get_review($post->ID); |
||
| 151 | if ('publish' === $new) { |
||
| 152 | glsr()->action('review/approved', $review, $old, $new); |
||
| 153 | } elseif ('pending' === $new) { |
||
| 154 | glsr()->action('review/unapproved', $review, $old, $new); |
||
| 155 | } elseif ('trash' === $new) { |
||
| 156 | glsr()->action('review/trashed', $review, $old, $new); |
||
| 157 | } |
||
| 158 | glsr()->action('review/transitioned', $review, $new, $old); |
||
| 159 | } else { |
||
| 160 | glsr(ReviewManager::class)->updateAssignedPost($post->ID); |
||
| 161 | } |
||
| 162 | } |
||
| 163 | |||
| 164 | /** |
||
| 165 | * Fallback action if ajax is not working for any reason. |
||
| 166 | * |
||
| 167 | * @action admin_action_approve |
||
| 168 | */ |
||
| 169 | public function onApprove(): void |
||
| 170 | { |
||
| 171 | if (glsr()->id === filter_input(INPUT_GET, 'plugin')) { |
||
| 172 | check_admin_referer('approve-review_'.($postId = $this->getPostId())); |
||
| 173 | $this->execute(new ToggleStatus(new Request([ |
||
| 174 | 'post_id' => $postId, |
||
| 175 | 'status' => 'publish', |
||
| 176 | ]))); |
||
| 177 | wp_safe_redirect(wp_get_referer()); |
||
| 178 | exit; |
||
| 179 | } |
||
| 180 | } |
||
| 181 | |||
| 182 | /** |
||
| 183 | * Triggered when a review's assigned post IDs are updated. |
||
| 184 | * |
||
| 185 | * @action site-reviews/review/updated/post_ids |
||
| 186 | */ |
||
| 187 | public function onChangeAssignedPosts(Review $review, array $postIds = []): void |
||
| 188 | { |
||
| 189 | $diff = $this->getAssignedDiffs($review->assigned_posts, $postIds); |
||
| 190 | $this->execute(new UnassignPosts($review, $diff['old'])); |
||
| 191 | $this->execute(new AssignPosts($review, $diff['new'])); |
||
| 192 | } |
||
| 193 | |||
| 194 | /** |
||
| 195 | * Triggered when a review's assigned users IDs are updated. |
||
| 196 | * |
||
| 197 | * @action site-reviews/review/updated/user_ids |
||
| 198 | */ |
||
| 199 | public function onChangeAssignedUsers(Review $review, array $userIds = []): void |
||
| 200 | { |
||
| 201 | $diff = $this->getAssignedDiffs($review->assigned_users, $userIds); |
||
| 202 | $this->execute(new UnassignUsers($review, $diff['old'])); |
||
| 203 | $this->execute(new AssignUsers($review, $diff['new'])); |
||
| 204 | } |
||
| 205 | |||
| 206 | /** |
||
| 207 | * Triggered after a review is created. |
||
| 208 | 24 | * |
|
| 209 | * @action site-reviews/review/created |
||
| 210 | 24 | */ |
|
| 211 | 24 | public function onCreatedReview(Review $review, CreateReview $command): void |
|
| 212 | { |
||
| 213 | $this->execute(new AssignPosts($review, $command->assigned_posts)); |
||
| 214 | $this->execute(new AssignUsers($review, $command->assigned_users)); |
||
| 215 | } |
||
| 216 | |||
| 217 | /** |
||
| 218 | * Triggered when a review is created. |
||
| 219 | 24 | * |
|
| 220 | * @action site-reviews/review/create |
||
| 221 | 24 | */ |
|
| 222 | 24 | public function onCreateReview(int $postId, CreateReview $command): void |
|
| 223 | 24 | { |
|
| 224 | 24 | $values = glsr()->args($command->toArray()); // this filters the values |
|
| 225 | 24 | $data = glsr(RatingDefaults::class)->restrict($values->toArray()); |
|
| 226 | $data['review_id'] = $postId; |
||
| 227 | $data['is_approved'] = 'publish' === get_post_status($postId); |
||
| 228 | if (false === glsr(Database::class)->insert('ratings', $data)) { |
||
| 229 | glsr_log()->error('A review could not be created. Here are some things to try which may fix the problem:'. |
||
| 230 | PHP_EOL.'1. First, deactivate Site Reviews and then reactivate it (this should fix any broken database table indexes).'. |
||
| 231 | PHP_EOL.'2. Next, hold down the ALT key (Option key if using a Mac) and run the Migrate Plugin tool.'. |
||
| 232 | PHP_EOL.'3. Finally, run the "Repair Review Relations" tool.'. |
||
| 233 | PHP_EOL.'4. If the problem persists, please use the "Contact Support" section on the Help page.' |
||
| 234 | ); |
||
| 235 | glsr_log()->debug($data); |
||
| 236 | 24 | wp_delete_post($postId, true); // remove post as review was not created |
|
| 237 | 24 | return; |
|
| 238 | } |
||
| 239 | $termIds = wp_set_object_terms($postId, $values->assigned_terms, glsr()->taxonomy); |
||
| 240 | 24 | if (is_wp_error($termIds)) { |
|
| 241 | glsr_log()->error($termIds->get_error_message()); |
||
| 242 | } |
||
| 243 | $excluded = Cast::toArray($command->request()->excluded); |
||
|
1 ignored issue
–
show
|
|||
| 244 | 24 | if (!empty($excluded)) { // save the fields hidden in the review form |
|
| 245 | glsr(Database::class)->metaSet($postId, 'excluded', $excluded); |
||
| 246 | } |
||
| 247 | if (!empty($values->response)) { // save the response if one is provided |
||
| 248 | glsr(Database::class)->metaSet($postId, 'response', $values->response); |
||
| 249 | glsr(Database::class)->metaSet($postId, 'response_by', $values->response_by); // @phpstan-ignore-line |
||
| 250 | } |
||
| 251 | foreach ($values->custom as $key => $value) { |
||
| 252 | glsr(Database::class)->metaSet($postId, "custom_{$key}", $value); |
||
| 253 | } |
||
| 254 | } |
||
| 255 | |||
| 256 | /** |
||
| 257 | * Triggered when a review or other post type is deleted and the posts table uses the MyISAM engine. |
||
| 258 | * |
||
| 259 | * @action deleted_post |
||
| 260 | */ |
||
| 261 | public function onDeletePost(int $postId, \WP_Post $post): void |
||
| 275 | }); |
||
| 276 | } |
||
| 277 | } |
||
| 278 | |||
| 279 | /** |
||
| 280 | * Triggered when a review is deleted and the posts table uses the MyISAM engine. |
||
| 281 | * |
||
| 282 | * @see $this->onDeletePost() |
||
| 283 | */ |
||
| 284 | public function onDeleteReview(int $reviewId): void |
||
| 285 | { |
||
| 286 | glsr(ReviewManager::class)->deleteRating($reviewId); |
||
| 287 | } |
||
| 288 | |||
| 289 | /** |
||
| 290 | * Triggered when a user is deleted and the users table uses the MyISAM engine. |
||
| 291 | * |
||
| 292 | * @action deleted_user |
||
| 293 | */ |
||
| 294 | public function onDeleteUser(int $userId = 0): void |
||
| 295 | { |
||
| 296 | $reviewIds = glsr(Query::class)->reviewIds([ |
||
| 297 | 'assigned_users' => $userId, |
||
| 298 | 'per_page' => -1, |
||
| 299 | 'status' => 'all', |
||
| 300 | ]); |
||
| 301 | if (glsr(Database::class)->delete('assigned_users', ['user_id' => $userId])) { |
||
| 302 | array_walk($reviewIds, function ($reviewId) { |
||
| 303 | glsr(Cache::class)->delete($reviewId, 'reviews'); |
||
| 304 | }); |
||
| 305 | } |
||
| 306 | } |
||
| 307 | |||
| 308 | /** |
||
| 309 | * Triggered when a review is edited or trashed. |
||
| 310 | * It's unnecessary to trigger a term recount as this is done by the set_object_terms hook |
||
| 311 | * We need to use "post_updated" to support revisions (vs "save_post"). |
||
| 312 | * |
||
| 313 | * @action post_updated |
||
| 314 | */ |
||
| 315 | public function onEditReview(int $postId, ?\WP_Post $post, ?\WP_Post $oldPost): void |
||
| 334 | } |
||
| 335 | } |
||
| 336 | |||
| 337 | /** |
||
| 338 | * Fallback action if ajax is not working for any reason. |
||
| 339 | * |
||
| 340 | * @action admin_action_unapprove |
||
| 341 | */ |
||
| 342 | public function onUnapprove(): void |
||
| 343 | { |
||
| 344 | if (glsr()->id === filter_input(INPUT_GET, 'plugin')) { |
||
| 345 | $postId = $this->getPostId(); |
||
| 346 | check_admin_referer("unapprove-review_{$postId}"); |
||
| 347 | $this->execute(new ToggleStatus(new Request([ |
||
| 348 | 'post_id' => $postId, |
||
| 349 | 24 | 'status' => 'publish', |
|
| 350 | ]))); |
||
| 351 | 24 | wp_safe_redirect(wp_get_referer()); |
|
| 352 | exit; |
||
| 353 | } |
||
| 354 | 24 | } |
|
| 355 | 24 | ||
| 356 | /** |
||
| 357 | * @action site-reviews/review/created |
||
| 358 | */ |
||
| 359 | public function sendNotification(Review $review): void |
||
| 360 | { |
||
| 361 | if (defined('WP_IMPORTING')) { |
||
| 362 | return; |
||
| 363 | } |
||
| 364 | if (empty(glsr_get_option('general.notifications'))) { |
||
| 365 | return; |
||
| 366 | } |
||
| 367 | if (!in_array($review->status, ['pending', 'publish'])) { |
||
| 368 | return; // this review is likely a draft made in the wp-admin |
||
| 369 | } |
||
| 370 | glsr(Queue::class)->async('queue/notification', ['review_id' => $review->ID]); |
||
| 371 | } |
||
| 372 | |||
| 373 | protected function bulkUpdateReview(Review $review, \WP_Post $oldPost): void |
||
| 374 | { |
||
| 375 | 24 | if ($assignedPostIds = filter_input(INPUT_GET, 'post_ids', FILTER_SANITIZE_NUMBER_INT, FILTER_FORCE_ARRAY)) { |
|
| 376 | glsr()->action('review/updated/post_ids', $review, Cast::toArray($assignedPostIds)); // trigger a recount of assigned posts |
||
| 377 | 24 | } |
|
| 378 | 24 | if ($assignedUserIds = filter_input(INPUT_GET, 'user_ids', FILTER_SANITIZE_NUMBER_INT, FILTER_FORCE_ARRAY)) { |
|
| 379 | 24 | glsr()->action('review/updated/user_ids', $review, Cast::toArray($assignedUserIds)); // trigger a recount of assigned users |
|
| 380 | 24 | } |
|
| 381 | 2 | $review = glsr(ReviewManager::class)->get($review->ID); // get a fresh copy of the review |
|
| 382 | 2 | glsr()->action('review/updated', $review, [], $oldPost); // pass an empty array since review values are unchanged |
|
| 383 | 2 | } |
|
| 384 | |||
| 385 | 24 | protected function getAssignedDiffs(array $existing, array $replacements): array |
|
| 386 | 24 | { |
|
| 387 | 24 | sort($existing); |
|
| 388 | 24 | sort($replacements); |
|
| 389 | $new = $old = []; |
||
| 390 | if ($existing !== $replacements) { |
||
| 391 | $ignored = array_intersect($existing, $replacements); |
||
| 392 | $new = array_diff($replacements, $ignored); |
||
| 393 | $old = array_diff($existing, $ignored); |
||
| 394 | } |
||
| 395 | return [ |
||
| 396 | 'new' => $new, |
||
| 397 | 'old' => $old, |
||
| 398 | ]; |
||
| 399 | } |
||
| 400 | |||
| 401 | protected function isEditedReview(\WP_Post $post, \WP_Post $oldPost): bool |
||
| 411 | } |
||
| 412 | |||
| 413 | protected function refreshAvatar(array $data, Review $review): string |
||
| 414 | { |
||
| 415 | $avatarUrl = Cast::toString($data['avatar'] ?? ''); |
||
| 416 | if ($review->author === ($data['name'] ?? false)) { |
||
| 425 | } |
||
| 426 | |||
| 427 | /** |
||
| 428 | * This is run after editing a review in the admin. |
||
| 429 | */ |
||
| 430 | protected function updateReview(Review $review, \WP_Post $oldPost): void |
||
| 460 |