PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.7.1
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.7.1
1.7.1 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 All 51 releases
fluent-cart / app / Hooks / Handlers / BlockEditors / ProductRatingBlockEditor.php

ProductRatingBlockEditor.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.7.1, at app/Hooks/Handlers/BlockEditors/ProductRatingBlockEditor.php

169 lines 5.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Hooks\Handlers\BlockEditors;
4
5 use FluentCart\Api\ModuleSettings;
6 use FluentCart\App\Services\Renderer\ProductCardRender;
7 use FluentCart\App\Services\Translations\TransStrings;
8 use FluentCart\Framework\Support\Arr;
9
10 class ProductRatingBlockEditor extends BlockEditor
11 {
12 protected static string $editorName = 'product-rating';
13
14 public function supports(): array
15 {
16 return [
17 // Out of the inserter while the reviews module is off; the block
18 // is still registered so the card templates that carry it keep
19 // parsing (see actions.php). Mirrored in the JS registration.
20 'inserter' => ModuleSettings::isActive('reviews'),
21 'html' => false,
22 'align' => ['left', 'center', 'right'],
23 'typography' => [
24 'fontSize' => true,
25 'lineHeight' => true,
26 '__experimentalDefaultControls' => [
27 'fontSize' => true,
28 ],
29 ],
30 'color' => [
31 'text' => true,
32 ],
33 'spacing' => [
34 'margin' => true,
35 'padding' => true,
36 ],
37 ];
38 }
39
40 public function blockAttributes(): array
41 {
42 return [
43 // Must mirror the JS registration — WordPress prepares dynamic
44 // block attributes against this server-side schema, so an
45 // attribute missing here never reaches the render callback.
46 'query_type' => ['type' => 'string', 'default' => 'default'],
47 'product_id' => ['type' => ['string', 'number'], 'default' => ''],
48 // How many reviews a product needs before its rating is worth
49 // showing. 0 shows it always, including the empty five stars a
50 // product with no reviews has.
51 'minReviewCount' => ['type' => 'number', 'default' => 0],
52 // And how good that rating has to be. 0 shows every rating; 4
53 // shows only products averaging four stars or better.
54 'minAverageRating' => ['type' => 'number', 'default' => 0],
55 ];
56 }
57
58 public function getScripts(): array
59 {
60 return [
61 [
62 'source' => 'admin/BlockEditor/ProductRating/ProductRatingBlockEditor.jsx',
63 'dependencies' => ['wp-blocks', 'wp-components', 'wp-data', 'wp-block-editor', 'wp-element']
64 ]
65 ];
66 }
67
68 public function getStyles(): array
69 {
70 return [
71 'admin/BlockEditor/ProductRating/style/product-rating-block-editor.scss'
72 ];
73 }
74
75 public function localizeData(): array
76 {
77 return [
78 $this->getLocalizationKey() => [
79 'slug' => $this->slugPrefix,
80 'name' => static::getEditorName(),
81 'title' => __('Product Rating', 'fluent-cart'),
82 'description' => __('Display the star rating for a product.', 'fluent-cart'),
83 // The editor draws nothing while the module is off.
84 'reviews_active' => ModuleSettings::isActive('reviews'),
85 ],
86 'fluent_cart_block_translation' => TransStrings::blockStrings(),
87 ];
88 }
89
90 public function useContext()
91 {
92 // Only Related Products provides this key — its presence tells the
93 // render which store toggle governs this placement.
94 return ['fluent-cart/related_product_ids'];
95 }
96
97 public function render(array $shortCodeAttribute, $block = null)
98 {
99 // No reviews module, no rating — whatever template carries the block.
100 if (!ModuleSettings::isActive('reviews')) {
101 return '';
102 }
103
104 // Store-level kill switch: Settings → Store Settings → Product Page
105 // → Product Rating. The block controls per-layout presence (it ships
106 // in the loop templates and editors remove it freely); these toggles
107 // turn ratings off store-wide without editing every page.
108 $isRelevantContext = $block instanceof \WP_Block
109 && isset($block->context['fluent-cart/related_product_ids']);
110
111 $ratingVisibilitySettingKey = $isRelevantContext
112 ? 'show_rating_in_relevant'
113 : 'show_rating_in_shop';
114
115 if ((new \FluentCart\Api\StoreSettings())->get($ratingVisibilitySettingKey, 'yes') !== 'yes') {
116 return '';
117 }
118
119 $product = $this->resolveProduct($shortCodeAttribute);
120
121 if (!$product) {
122 return '';
123 }
124
125 $wrapper_attributes = get_block_wrapper_attributes([
126 'class' => 'fct-product-card-rating',
127 ]);
128
129 ob_start();
130 (new ProductCardRender($product))->renderStarRatingBlock($wrapper_attributes, $shortCodeAttribute, $block);
131 return ob_get_clean();
132 }
133
134 /**
135 * Reviews counted for this product, from the aggregate the stars are
136 * drawn from. A product whose detail row has not been created yet counts
137 * as none rather than as an error.
138 *
139 * @param \FluentCart\App\Models\Product $product
140 * @return int
141 */
142 protected function reviewCount($product): int
143 {
144 return (int) Arr::get($this->ratingInfo($product), 'review_count', 0);
145 }
146
147 /**
148 * The average the stars are drawn from, on the same terms.
149 *
150 * @param \FluentCart\App\Models\Product $product
151 * @return float
152 */
153 protected function averageRating($product): float
154 {
155 return (float) Arr::get($this->ratingInfo($product), 'average_rating', 0);
156 }
157
158 /**
159 * @param \FluentCart\App\Models\Product $product
160 * @return array
161 */
162 protected function ratingInfo($product): array
163 {
164 $detail = $product->detail;
165
166 return $detail ? ($detail->other_info ?: []) : [];
167 }
168 }
169