PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.0
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.0
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 1.3.19 trunk All 48 releases
fluent-cart / app / Hooks / Handlers / AdvancedVariationHandler.php

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

215 lines 9.1 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;
4
5 use FluentCart\App\Helpers\Helper;
6 use FluentCart\App\Models\AttributeGroup;
7 use FluentCart\App\Services\TemplateService;
8 use FluentCart\App\Vite;
9 use FluentCart\Framework\Support\Arr;
10 use FluentCart\App\Services\AdvancedVariationService;
11 use FluentCart\App\Services\Renderer\AdvancedVariationRenderer;
12
13 class AdvancedVariationHandler
14 {
15 public function register()
16 {
17 add_filter('fluent_cart/variation_types', function ($types) {
18 $types[Helper::PRODUCT_TYPE_ADVANCE_VARIATION] = __('Advanced Variations', 'fluent-cart');
19 return $types;
20 });
21
22 add_filter('fluent_cart/product/get_response_data', function ($payload) {
23 $product = Arr::get($payload, 'product', []);
24 $variationType = Arr::get($product, 'detail.variation_type');
25
26 if ($variationType !== Helper::PRODUCT_TYPE_ADVANCE_VARIATION) {
27 return $payload;
28 }
29
30 $payload['product'] = AdvancedVariationService::hydrateProductData($product);
31 return $payload;
32 });
33
34 // Declared with the 2-arg signature to match the apply_filters site
35 // contract ($variant, $postId). $postId is intentionally unused — this
36 // handler only strips a derived field from the variant payload before
37 // save; the postId is available for future handlers that need scoping.
38 add_filter('fluent_cart/product/variant_save_data', function ($variant, $postId) {
39 // attr_map is hydrated from AttributeRelation — not a column on fc_product_variations.
40 unset($variant['attr_map']);
41 return $variant;
42 }, 10, 2);
43
44 add_filter('fluent_cart/product/variant_option_sync', function ($payload) {
45 $data = Arr::get($payload, 'data', []);
46 $variationType = Arr::get($data, 'variation_type');
47
48 if ($variationType !== Helper::PRODUCT_TYPE_ADVANCE_VARIATION) {
49 return $payload;
50 }
51
52 $productId = (int) Arr::get($payload, 'product_id');
53
54 $payload['handled'] = true;
55 $payload['response'] = AdvancedVariationService::syncVariantOption($productId, $data);
56
57 return $payload;
58 });
59
60 // Build both gallery variation maps for the product renderer.
61 // variant_term_map: variantId → primary-visual-term ID (color/image group).
62 // variant_first_media_map: variantId → { id, url } of the variant's first image.
63 add_filter('fluent_cart/product/gallery_variation_data', function ($data, $product) {
64 $otherInfo = (array) Arr::get($product->detail, 'other_info');
65 $attrConfig = (array) Arr::get($otherInfo, 'attribute_config', []);
66 if (empty($attrConfig)) {
67 return $data;
68 }
69
70 $groupIds = array_filter(array_map(function ($g) {
71 return (int) Arr::get($g, 'group_id');
72 }, $attrConfig));
73 if (empty($groupIds)) {
74 return $data;
75 }
76
77 $groups = AttributeGroup::whereIn('id', $groupIds)->get()->keyBy('id');
78 $primaryGroupId = 0;
79 $primaryTermIds = [];
80 foreach ($attrConfig as $group) {
81 $groupId = (int) Arr::get($group, 'group_id');
82 $attributeGroup = $groups->get($groupId);
83 $type = $attributeGroup ? Arr::get((array) $attributeGroup->settings, 'type', '') : '';
84 if (in_array($type, ['color', 'image'], true)) {
85 $primaryGroupId = $groupId;
86 $primaryTermIds = array_map('intval', (array) Arr::get($group, 'variants', []));
87 break;
88 }
89 }
90 if (!$primaryGroupId) {
91 return $data;
92 }
93
94 $primaryTermSet = array_flip($primaryTermIds);
95 $variantTermMap = [];
96 foreach ($product->variants as $variant) {
97 $identifier = Arr::get($variant, 'variation_identifier', '');
98 if (!$identifier) {
99 continue;
100 }
101 foreach (array_map('intval', explode('_', $identifier)) as $tid) {
102 if (isset($primaryTermSet[$tid])) {
103 $variantTermMap[(int) Arr::get($variant, 'id')] = $tid;
104 break;
105 }
106 }
107 }
108
109 $variantFirstMediaMap = [];
110 foreach ($product->variants as $variant) {
111 $variantId = (int) Arr::get($variant, 'id');
112 $media = Arr::get($variant, 'media.meta_value', []);
113 if (empty($media) || !is_array($media)) {
114 continue;
115 }
116 $firstItem = $media[0] ?? null;
117 if (!$firstItem) {
118 continue;
119 }
120 $firstUrl = Arr::get($firstItem, 'url', '');
121 $firstMediaId = (int) Arr::get($firstItem, 'id', 0);
122 if ($firstUrl) {
123 $variantFirstMediaMap[$variantId] = [
124 'id' => $firstMediaId,
125 'url' => $firstUrl,
126 ];
127 }
128 }
129
130 $data['variant_term_map'] = $variantTermMap;
131 $data['variant_first_media_map'] = $variantFirstMediaMap;
132 return $data;
133 }, 10, 2);
134
135 add_filter('fluent_cart/product/render_advanced_variation', function ($context) {
136 // Storefront server-side render. The renderer echoes HTML
137 // directly into the product template's output buffer (the
138 // apply_filters site captures that output around the call).
139 // render() returns true only when it actually emitted markup —
140 // it no-ops for a product switched to advanced variations before
141 // its attributes are configured. Propagate that bool into
142 // 'rendered' so core skips its default variation block when we
143 // rendered, and falls through to its simple-variation rendering
144 // when we didn't.
145 $product = Arr::get($context, 'product');
146 $selectorStyle = Arr::get($context, 'selector_style', 'auto');
147 if ($product) {
148 $context['rendered'] = (new AdvancedVariationRenderer($product))->render($selectorStyle);
149 }
150 return $context;
151 });
152
153 // Storefront assets for the variation selector. Enqueued on every
154 // page type that can host an advanced-variation product UI: the
155 // single-product page itself, plus shop and product-taxonomy
156 // archives (which render product cards that open the View Options
157 // modal — the same rendered selector markup, just inside a dialog
158 // instead of the main column). Cart / checkout / receipt / login /
159 // registration / customer-dashboard stay excluded so they don't pay
160 // the asset weight. Filterable for custom contexts.
161 $advVariationAssetPages = apply_filters(
162 'fluent_cart/advanced_variation/asset_page_types',
163 ['single_product', 'shop', 'product_taxonomy']
164 );
165
166 add_action('wp_enqueue_scripts', function () use ($advVariationAssetPages) {
167 if (!in_array(TemplateService::getCurrentFcPageType(), $advVariationAssetPages, true)) {
168 return;
169 }
170 Vite::enqueueStyle(
171 'fluent-cart-advanced-variation-style',
172 'public/single-product/advanced-variations.scss'
173 );
174 });
175
176 // Single hook for the advanced-variation selector assets. Core fires
177 // fluent_cart/advanced_variation/enqueue_assets from every product
178 // purchase surface — Shop, Product List, Product Carousel, Related
179 // Products, Product Info, Buy Section, the (single) product shortcodes
180 // and the quick-view modal. It may fire more than once per request, so
181 // a static guard registers the assets a single time.
182 add_action('fluent_cart/advanced_variation/enqueue_assets', function () {
183 static $enqueued = false;
184 if ($enqueued) {
185 return;
186 }
187 $enqueued = true;
188 Vite::enqueueStyle(
189 'fluent-cart-advanced-variation-style',
190 'public/single-product/advanced-variations.scss'
191 );
192 Vite::enqueueScript(
193 'fluent-cart-advanced-variation-public',
194 'public/single-product/advanced-variation-public.js',
195 [],
196 null,
197 true
198 );
199 });
200
201 add_action('wp_enqueue_scripts', function () use ($advVariationAssetPages) {
202 if (!in_array(TemplateService::getCurrentFcPageType(), $advVariationAssetPages, true)) {
203 return;
204 }
205 Vite::enqueueScript(
206 'fluent-cart-advanced-variation-public',
207 'public/single-product/advanced-variation-public.js',
208 [],
209 null,
210 true
211 );
212 });
213 }
214 }
215