← All changes
|
app/Http/Controllers/FrontendControllers/ProductReviewFrontendController.php
+104
-14
1.7.0
→
1.8.0
View file →
| @@ -37,8 +37,10 @@ | ||
| 37 | 37 | * a trim: a review that long was going to print in full anyway. |
| 38 | 38 | */ |
| 39 | 39 | const MAX_REVIEW_WORDS = 500; |
| 40 | 40 | |
| 41 | + const MAX_ALL_PRODUCTS_REVIEWS_PER_PAGE = ProductReviewService::MAX_ALL_PRODUCTS_REVIEWS_PER_PAGE; | |
| 42 | + | |
| 41 | 43 | public function getReviews(ReviewRequest $request, $postId) |
| 42 | 44 | { |
| 43 | 45 | $postId = intval($postId); |
| 44 | 46 | |
| @@ -78,19 +80,24 @@ | ||
| 78 | 80 | 'page' => max(1, intval($request->get('page', 1))), |
| 79 | 81 | 'sort_by' => $data['sort_by'] ?? 'created_at', |
| 80 | 82 | 'sort_order' => $data['sort_order'] ?? 'DESC', |
| 81 | 83 | 'per_page' => $perPage, |
| 82 | - // The list's floor, read out of the composition this request names | |
| 83 | - // rather than off the query string. It is the editor's setting, | |
| 84 | - // not the reader's: taken from the request, anyone could ask for | |
| 85 | - // the one-star reviews a shop had chosen not to publish here. | |
| 84 | + // A signed per-instance floor takes precedence over the legacy | |
| 85 | + // composition default. Neither changes public-review permissions. | |
| 86 | 86 | 'min_rating' => ProductReviewListBlockEditor::minRatingOfComposition( |
| 87 | - $request->get('client_id', '') | |
| 87 | + $request->get('client_id', ''), | |
| 88 | + (int) $postId, | |
| 89 | + (string) $request->get('rating_token', '') | |
| 88 | 90 | ), |
| 89 | 91 | ]; |
| 90 | 92 | |
| 93 | + // ratings=5,4 from the star chips; the single rating param still works. | |
| 94 | + $ratings = ProductReviewService::ratingList($request->get('ratings', '')); | |
| 91 | 95 | $rating = !empty($data['rating']) ? (int) $data['rating'] : null; |
| 92 | - if ($rating && $rating >= 1 && $rating <= 5) { | |
| 96 | + | |
| 97 | + if ($ratings) { | |
| 98 | + $params['ratings'] = $ratings; | |
| 99 | + } elseif ($rating && $rating >= 1 && $rating <= 5) { | |
| 93 | 100 | $params['rating'] = $rating; |
| 94 | 101 | } |
| 95 | 102 | |
| 96 | 103 | if (!empty($data['has_media'])) { |
| @@ -106,9 +113,35 @@ | ||
| 106 | 113 | // The rows render on the server, after the filter so PRO's media and |
| 107 | 114 | // vote fields are part of them; the JS swaps this markup in. Display |
| 108 | 115 | // flags travel with the request since they are block attributes the |
| 109 | 116 | // server cannot otherwise know. |
| 110 | - $listRenderer = new ReviewListRenderer([ | |
| 117 | + $listRenderer = $this->buildListRendererFromRequest($request, [ | |
| 118 | + // The composed row again, rebuilt from the post the block is saved | |
| 119 | + // in. Without this the first sort, filter or page swaps the | |
| 120 | + // editor's row for the fixed one and never gives it back — the | |
| 121 | + // saved composition lasting exactly one page load. | |
| 122 | + 'rows_renderer' => ProductReviewListBlockEditor::savedRowRenderer( | |
| 123 | + $request->get('client_id', '') | |
| 124 | + ), | |
| 125 | + ]); | |
| 126 | + | |
| 127 | + $responseData['reviews_html'] = $listRenderer->renderReviewItems(Arr::get($responseData, 'reviews.data', [])); | |
| 128 | + $responseData['pagination_html'] = $listRenderer->paginationHtml(Arr::get($responseData, 'reviews', [])); | |
| 129 | + | |
| 130 | + return $this->sendSuccess($responseData); | |
| 131 | + } | |
| 132 | + | |
| 133 | + /** | |
| 134 | + * Rows and pager for a list request. Display flags are block attributes the server | |
| 135 | + * cannot otherwise know, so they travel with the request and are bounded here. | |
| 136 | + * | |
| 137 | + * @param Request $request | |
| 138 | + * @param array $extraOptions Options the caller adds or overrides. | |
| 139 | + * @return ReviewListRenderer | |
| 140 | + */ | |
| 141 | + protected function buildListRendererFromRequest($request, array $extraOptions = []): ReviewListRenderer | |
| 142 | + { | |
| 143 | + return new ReviewListRenderer(array_merge([ | |
| 111 | 144 | 'show_reviewer' => $request->get('show_reviewer', '1') !== '0', |
| 112 | 145 | 'show_date' => $request->get('show_date', '1') !== '0', |
| 113 | 146 | 'show_verified' => $request->get('show_verified', '1') !== '0', |
| 114 | 147 | 'show_view_reply' => $request->get('show_view_reply', '1') !== '0', |
| @@ -143,15 +176,72 @@ | ||
| 143 | 176 | $request->get('pagination_type', 'numbers'), |
| 144 | 177 | ReviewListRenderer::paginationTypes(), |
| 145 | 178 | true |
| 146 | 179 | ) ? $request->get('pagination_type', 'numbers') : 'numbers', |
| 147 | - // The composed row again, rebuilt from the post the block is saved | |
| 148 | - // in. Without this the first sort, filter or page swaps the | |
| 149 | - // editor's row for the fixed one and never gives it back — the | |
| 150 | - // saved composition lasting exactly one page load. | |
| 151 | - 'rows_renderer' => ProductReviewListBlockEditor::savedRowRenderer( | |
| 152 | - $request->get('client_id', '') | |
| 153 | - ), | |
| 180 | + ], $extraOptions)); | |
| 181 | + } | |
| 182 | + | |
| 183 | + /** | |
| 184 | + * Approved reviews across the store, for [fluent_cart_product_reviews scope="all"]. | |
| 185 | + * Same list contract as getReviews(), narrowed by product and category ids. | |
| 186 | + */ | |
| 187 | + public function getAllProductsReviews(Request $request) | |
| 188 | + { | |
| 189 | + $settings = ProductReviewService::getReviewSettings(); | |
| 190 | + | |
| 191 | + if (Arr::get($settings, 'reviews_enabled') !== 'yes') { | |
| 192 | + return $this->sendError([ | |
| 193 | + 'message' => __('Reviews are not available', 'fluent-cart'), | |
| 194 | + ], 404); | |
| 195 | + } | |
| 196 | + | |
| 197 | + $perPage = (int) $request->get('per_page', 0); | |
| 198 | + $perPage = $perPage > 0 ? $perPage : (int) Arr::get($settings, 'reviews_per_page', 10); | |
| 199 | + $perPage = max(1, min($perPage, static::MAX_ALL_PRODUCTS_REVIEWS_PER_PAGE)); | |
| 200 | + | |
| 201 | + $params = array_merge(ProductReviewService::sanitizeProductFilters([ | |
| 202 | + 'product_ids' => $request->get('product_ids', ''), | |
| 203 | + 'category_ids' => $request->get('category_ids', ''), | |
| 204 | + ]), [ | |
| 205 | + 'page' => max(1, intval($request->get('page', 1))), | |
| 206 | + 'sort_by' => sanitize_text_field($request->get('sort_by', 'created_at')), | |
| 207 | + 'sort_order' => sanitize_text_field($request->get('sort_order', 'DESC')), | |
| 208 | + 'per_page' => $perPage, | |
| 209 | + // A floor the renderer signed for post id 0. Without a valid token there is | |
| 210 | + // no floor; every approved review is public, so that only widens the list. | |
| 211 | + 'min_rating' => (int) ProductReviewService::minRatingFromToken(0, (string) $request->get('rating_token', '')), | |
| 212 | + ]); | |
| 213 | + | |
| 214 | + $ratings = ProductReviewService::ratingList($request->get('ratings', '')); | |
| 215 | + $rating = intval($request->get('rating', 0)); | |
| 216 | + | |
| 217 | + if ($ratings) { | |
| 218 | + $params['ratings'] = $ratings; | |
| 219 | + } elseif ($rating >= 1 && $rating <= 5) { | |
| 220 | + $params['rating'] = $rating; | |
| 221 | + } | |
| 222 | + | |
| 223 | + if (intval($request->get('has_media', 0))) { | |
| 224 | + $params['has_media'] = true; | |
| 225 | + } | |
| 226 | + | |
| 227 | + if (intval($request->get('verified_only', 0))) { | |
| 228 | + $params['verified_only'] = true; | |
| 229 | + } | |
| 230 | + | |
| 231 | + $responseData = ProductReviewService::getAllProductsReviewsPayload($params); | |
| 232 | + | |
| 233 | + if ($request->get('include_summary', '0') === '1') { | |
| 234 | + $summary = ProductReviewService::getProductsRatingSummary($params); | |
| 235 | + $responseData['summary'] = $summary; | |
| 236 | + $responseData['stars_html'] = (new ProductReviewRenderer(0))->starsHtml(Arr::get($summary, 'average', 0)); | |
| 237 | + /* translators: 1: the average star rating, e.g. "Rated 4.5 out of 5" */ | |
| 238 | + $responseData['stars_label'] = sprintf(__('Rated %1$s out of 5', 'fluent-cart'), Arr::get($summary, 'average', 0)); | |
| 239 | + } | |
| 240 | + | |
| 241 | + $listRenderer = $this->buildListRendererFromRequest($request, [ | |
| 242 | + // A Review List block's custom row; an unknown token keeps the fixed row. | |
| 243 | + 'rows_renderer' => ProductReviewListBlockEditor::savedRowRenderer($request->get('client_id', '')), | |
| 154 | 244 | ]); |
| 155 | 245 | |
| 156 | 246 | $responseData['reviews_html'] = $listRenderer->renderReviewItems(Arr::get($responseData, 'reviews.data', [])); |
| 157 | 247 | $responseData['pagination_html'] = $listRenderer->paginationHtml(Arr::get($responseData, 'reviews', [])); |