PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / trunk
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler vtrunk
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
fluent-cart / app / Hooks / Handlers / BlockEditors / ProductRatingBlockEditor.php

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

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