detail, 'other_info'); $attrConfig = (array) Arr::get($otherInfo, 'attribute_config', []); if (empty($attrConfig)) { return $data; } $groupIds = array_filter(array_map(function ($g) { return (int) Arr::get($g, 'group_id'); }, $attrConfig)); if (empty($groupIds)) { return $data; } $groups = AttributeGroup::whereIn('id', $groupIds)->get()->keyBy('id'); $primaryGroupId = 0; $primaryTermIds = []; foreach ($attrConfig as $group) { $groupId = (int) Arr::get($group, 'group_id'); $attributeGroup = $groups->get($groupId); $type = $attributeGroup ? Arr::get((array) $attributeGroup->settings, 'type', '') : ''; if (in_array($type, ['color', 'image'], true)) { $primaryGroupId = $groupId; $primaryTermIds = array_map('intval', (array) Arr::get($group, 'variants', [])); break; } } if (!$primaryGroupId) { return $data; } $primaryTermSet = array_flip($primaryTermIds); $variantTermMap = []; foreach ($product->variants as $variant) { $identifier = Arr::get($variant, 'variation_identifier', ''); if (!$identifier) { continue; } foreach (array_map('intval', explode('_', $identifier)) as $tid) { if (isset($primaryTermSet[$tid])) { $variantTermMap[(int) Arr::get($variant, 'id')] = $tid; break; } } } $variantFirstMediaMap = []; foreach ($product->variants as $variant) { $variantId = (int) Arr::get($variant, 'id'); $media = Arr::get($variant, 'media.meta_value', []); if (empty($media) || !is_array($media)) { continue; } $firstItem = $media[0] ?? null; if (!$firstItem) { continue; } $firstUrl = Arr::get($firstItem, 'url', ''); $firstMediaId = (int) Arr::get($firstItem, 'id', 0); if ($firstUrl) { $variantFirstMediaMap[$variantId] = [ 'id' => $firstMediaId, 'url' => $firstUrl, ]; } } $data['variant_term_map'] = $variantTermMap; $data['variant_first_media_map'] = $variantFirstMediaMap; return $data; }, 10, 2); add_filter('fluent_cart/product/render_advanced_variation', function ($context) { // Storefront server-side render. The renderer echoes HTML // directly into the product template's output buffer (the // apply_filters site captures that output around the call). // render() returns true only when it actually emitted markup — // it no-ops for a product switched to advanced variations before // its attributes are configured. Propagate that bool into // 'rendered' so core skips its default variation block when we // rendered, and falls through to its simple-variation rendering // when we didn't. $product = Arr::get($context, 'product'); $selectorStyle = Arr::get($context, 'selector_style', 'auto'); if ($product) { $context['rendered'] = (new AdvancedVariationRenderer($product))->render($selectorStyle); } return $context; }); // Storefront assets for the variation selector. Enqueued on every // page type that can host an advanced-variation product UI: the // single-product page itself, plus shop and product-taxonomy // archives (which render product cards that open the View Options // modal — the same rendered selector markup, just inside a dialog // instead of the main column). Cart / checkout / receipt / login / // registration / customer-dashboard stay excluded so they don't pay // the asset weight. Filterable for custom contexts. $advVariationAssetPages = apply_filters( 'fluent_cart/advanced_variation/asset_page_types', ['single_product', 'shop', 'product_taxonomy'] ); add_action('wp_enqueue_scripts', function () use ($advVariationAssetPages) { if (!in_array(TemplateService::getCurrentFcPageType(), $advVariationAssetPages, true)) { return; } Vite::enqueueStyle( 'fluent-cart-advanced-variation-style', 'public/single-product/advanced-variations.scss' ); }); // Single hook for the advanced-variation selector assets. Core fires // fluent_cart/advanced_variation/enqueue_assets from every product // purchase surface — Shop, Product List, Product Carousel, Related // Products, Product Info, Buy Section, the (single) product shortcodes // and the quick-view modal. It may fire more than once per request, so // a static guard registers the assets a single time. add_action('fluent_cart/advanced_variation/enqueue_assets', function () { static $enqueued = false; if ($enqueued) { return; } $enqueued = true; Vite::enqueueStyle( 'fluent-cart-advanced-variation-style', 'public/single-product/advanced-variations.scss' ); Vite::enqueueScript( 'fluent-cart-advanced-variation-public', 'public/single-product/advanced-variation-public.js', [], null, true ); }); add_action('wp_enqueue_scripts', function () use ($advVariationAssetPages) { if (!in_array(TemplateService::getCurrentFcPageType(), $advVariationAssetPages, true)) { return; } Vite::enqueueScript( 'fluent-cart-advanced-variation-public', 'public/single-product/advanced-variation-public.js', [], null, true ); }); } }