PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.7.0
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.7.0
1.7.0 1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 All 50 releases
← All changes | app/Modules/Templating/TemplateActions.php +70 -4 1.6.4 → 1.7.0 View file →
@@ -12,8 +12,9 @@
12 12 use FluentCart\App\Modules\Data\ProductQuery;
13 13 use FluentCart\App\Services\Renderer\ProductListRenderer;
14 14 use FluentCart\App\Services\Renderer\ProductRenderer;
15 15 use FluentCart\App\Services\Renderer\ShopAppRenderer;
16 +use FluentCart\App\Services\Schema\ProductSchema;
16 17 use FluentCart\App\Services\TemplateService;
17 18 use FluentCart\Framework\Support\Arr;
18 19
19 20 class TemplateActions
@@ -22,8 +23,16 @@
22 23 {
23 24 add_action('fluent_cart/template/main_content', [$this, 'renderMainContent']);
24 25 add_action('fluent_cart/template/product_archive', [$this, 'renderProductArchive']);
25 26 add_action('fluent_cart/product/render_product_header', [$this, 'renderProductHeader']);
27 + // Block themes render reviews from the product template, so the
28 + // built-in listener is not attached there — the hook still fires for
29 + // third-party listeners. Safe to decide here: the theme is resolved
30 + // at after_setup_theme, before init runs this.
31 + if (!function_exists('wp_is_block_theme') || !wp_is_block_theme()) {
32 + add_action('fluent_cart/product/after_product_content', [$this, 'renderProductReviews']);
33 + }
34 + add_action('wp_head', [$this, 'renderProductJsonLd']);
26 35
27 36 // Universal hook for after_product_content — fires on both classic and block themes
28 37 // Block themes render <!-- wp:post-content --> which runs the_content filter
29 38 add_filter('the_content', function ($content) {
@@ -46,9 +55,9 @@
46 55 }
47 56
48 57 return $content;
49 58 }, 999);
50 -
59 +
51 60 add_shortcode('fluent_cart_product_header', function ($atts = []) {
52 61 $atts = shortcode_atts([
53 62 'id' => 0,
54 63 ], $atts);
@@ -120,9 +129,10 @@
120 129 ob_start();
121 130 (new ProductListRenderer(
122 131 $products,
123 132 __('Related Products', 'fluent-cart'),
124 - 'fct-similar-product-list-container'
133 + 'fct-similar-product-list-container',
134 + ['rating_context' => 'relevant']
125 135 ))->render();
126 136
127 137 $content = ob_get_clean();
128 138
@@ -211,9 +221,12 @@
211 221 ];
212 222 }
213 223
214 224 $products = $productsQuery->get();
215 - (new ShopAppRenderer($products, [
225 + (new ShopAppRenderer([
226 + 'products' => $products,
227 + 'total' => $products->total(),
228 + ], [
216 229 'default_filters' => $productsQuery->getDefaultFilters(),
217 230 'custom_filters' => [
218 231 'taxonomies' => [
219 232 $taxFilters
@@ -296,9 +309,10 @@
296 309 ob_start();
297 310 (new ProductListRenderer(
298 311 $products,
299 312 __('Related Products', 'fluent-cart'),
300 - 'fct-similar-product-list-container'
313 + 'fct-similar-product-list-container',
314 + ['rating_context' => 'relevant']
301 315 ))->render();
302 316
303 317 $relevantProducts = ob_get_clean();
304 318 $content .= $relevantProducts;
@@ -345,7 +359,59 @@
345 359 ], '', $content);
346 360
347 361 // Remove extra whitespace between tags to clean up the output
348 362 return preg_replace('/>\s+</', '><', $cleaned);
363 + }
364 +
365 + /**
366 + * Whether the product page should carry its review section at all.
367 + *
368 + * Sits beside the relevant-products switch in Product Page settings and
369 + * works the same way, so one control governs the section wherever it is
370 + * rendered from — a review block, or the append below.
371 + *
372 + * @param int $postId
373 + * @return bool
374 + */
375 + public static function shouldShowReviewsOnProductPage($postId): bool
376 + {
377 + $show = (new StoreSettings())->get('show_reviews_in_single_page', 'yes') == 'yes';
378 +
379 + return (bool) apply_filters('fluent_cart/single_product_page/show_reviews', $show, $postId);
380 + }
381 +
382 + private function isReviewModuleAvailable()
383 + {
384 + return \FluentCart\Api\ModuleSettings::isActive('reviews')
385 + && class_exists(\FluentCart\App\Services\ProductReviewService::class);
386 + }
387 +
388 + public function renderProductReviews($postId)
389 + {
390 + if (!$postId || !$this->isReviewModuleAvailable()) {
391 + return;
392 + }
393 +
394 + $renderer = new \FluentCart\App\Services\Renderer\ProductReviewRenderer($postId);
395 + $renderer->render();
396 + $renderer->renderForm();
397 + }
398 +
399 + public function renderProductJsonLd()
400 + {
401 + if (!is_singular(\FluentCart\App\CPT\FluentProducts::CPT_NAME)) {
402 + return;
403 + }
404 +
405 + $postId = get_the_ID();
406 + if (!$postId) {
407 + return;
408 + }
409 +
410 + // The node itself — product, offers, aggregate rating, the reviews
411 + // the page shows — is ProductSchema's; this is only the page gate.
412 + // The reviews module gate lives there too, on the review parts
413 + // alone: a product is for sale whether or not it takes reviews.
414 + ProductSchema::render($postId);
349 415 }
350 416
351 417 }