product = $product; $this->variants = $product->variants; $this->storeSettings = new StoreSettings(); $this->viewType = $this->storeSettings->get('variation_view', 'both'); $this->columnType = $this->storeSettings->get('variation_columns', 'masonry'); $defaultVariationId = $config['default_variation_id'] ?? ''; // 'image', 'text','both' $this->viewType = apply_filters('fluent_cart/single_product/variation_view_type', $this->viewType, [ 'product' => $product, 'variants' => $this->variants, 'defaultVariationId' => $defaultVariationId, ]); // 'one', 'two','three', 'four', 'masonry' $this->columnType = apply_filters('fluent_cart/single_product/variation_column_type', $this->columnType, [ 'product' => $product, 'variants' => $this->variants, 'defaultVariationId' => $defaultVariationId, ]); $hasExplicitDefault = true; if (!$defaultVariationId) { $variationIds = $product->variants->pluck('id')->toArray(); $defaultVariationId = $product->detail->default_variation_id; if (!$defaultVariationId || !in_array($defaultVariationId, $variationIds)) { $defaultVariationId = Arr::get($variationIds, '0'); $hasExplicitDefault = false; } } // Always set resolved default variation id $this->defaultVariationId = $defaultVariationId; // Gallery defaults to featured image (key 0) when no explicit default variation is set $this->defaultGalleryImageId = $hasExplicitDefault ? $defaultVariationId : 0; $this->product->variants->load('bundleChildren.product'); foreach ($this->product->variants as $variant) { if ($variant->id == $this->defaultVariationId) { $this->defaultVariant = $variant; } $paymentType = Arr::get($variant->other_info, 'payment_type'); if ($paymentType === 'onetime') { $this->hasOnetime = true; } else if ($paymentType === 'subscription') { $this->hasSubscription = true; } } $this->buildProductGroups(); } public function buildProductGroups() { $groupKey = 'repeat_interval'; $otherInfo = (array)Arr::get($this->product->detail, 'other_info'); $groupBy = Arr::get($otherInfo, 'group_pricing_by', 'repeat_interval'); //repeat_interval,payment_type,none if ($groupBy !== 'none') { if ($groupBy === 'payment_type') { $groupKey = 'payment_type'; } $paymentTypes = []; if ($groupBy === 'repeat_interval') { foreach ($this->variants as $key => $variant) { $paymentType = 'onetime'; $type = Arr::get($variant, 'payment_type'); if ($type === 'subscription') { $isInstallment = Arr::get($variant, 'other_info.installment', 'no'); if ($isInstallment === 'yes' && App::isProActive()) { $paymentType = 'installment'; } else { $paymentType = Arr::get($variant, 'other_info.repeat_interval', 'onetime');; } } $paymentTypes[] = $paymentType; if (!isset($this->variantsByPaymentTypes[$paymentType])) { $this->variantsByPaymentTypes[$paymentType] = []; } $this->variantsByPaymentTypes[$paymentType][] = $variant; if ($this->defaultVariationId == $variant['id']) { $this->activeTab = $paymentType; } } } else { foreach ($this->variants as $key => $variant) { $paymentType = 'onetime'; $type = Arr::get($variant, 'payment_type'); if ($type === 'subscription') { $isInstallment = Arr::get($variant, 'other_info.installment'); if ($isInstallment === 'yes' && App::isProActive()) { $paymentType = 'installment'; } else { $paymentType = 'subscription'; } } $paymentTypes[] = $paymentType; if (!isset($this->variantsByPaymentTypes[$paymentType])) { $this->variantsByPaymentTypes[$paymentType] = []; } $this->variantsByPaymentTypes[$paymentType][] = $variant; if ($this->defaultVariationId == $variant['id']) { $this->activeTab = $paymentType; } } } $paymentTypes = array_unique($paymentTypes); $intervalOptions = Helper::getAvailableSubscriptionIntervalOptions(); $groupLanguageMap = [ 'onetime' => __('One Time', 'fluent-cart'), 'subscription' => __('Subscription', 'fluent-cart'), 'installment' => __('Installment', 'fluent-cart'), ]; foreach ($intervalOptions as $interval) { $groupLanguageMap[$interval['value']] = $interval['label']; } foreach ($paymentTypes as $paymentType) { $this->paymentTypes[$paymentType ?: 'onetime'] = Arr::get($groupLanguageMap, $paymentType ?: 'onetime'); } } } public function render() { ?>
renderGallery(); ?>
renderTitle(); $this->renderProductMeta(); $this->renderExcerpt(); $this->renderPrices(); if ($this->product->detail->variation_type === 'simple' && !$this->hasSubscription) { foreach ($this->product->variants as $variant) { $this->renderVariationsBundleProduct($variant); } } $this->renderBuySection(); ?>
renderStockAvailability(); ?> renderSku(); ?>
product->detail, 'other_info'); $groupBy = Arr::get($otherInfo, 'group_pricing_by', 'repeat_interval'); //repeat_interval,payment_type,none $this->renderBuySectionWrapperStart(); $this->renderVariationDisplay($atts); $this->renderItemPrice(); $this->renderQuantity(); ?>
renderPurchaseButtons(Arr::get($atts, 'button_atts', [])); ?>
renderBuySectionWrapperEnd(); } public function renderVariationDisplay($atts = []) { $otherInfo = (array)Arr::get($this->product->detail, 'other_info'); $groupBy = Arr::get($otherInfo, 'group_pricing_by', 'repeat_interval'); //repeat_interval,payment_type,none if (count($this->paymentTypes) === 1 || $groupBy === 'none') { $this->renderVariants(Arr::get($atts, 'variation_atts', [])); } else { $this->renderTab(Arr::get($atts, 'variation_atts', [])); } } public function renderGalleryThumb() { $thumbnails = []; $featuredMedia = $this->product->thumbnail ?? Vite::getAssetUrl('images/placeholder.svg'); if (!$featuredMedia) { $featuredMedia = []; } $galleryImage = get_post_meta($this->product->ID, 'fluent-products-gallery-image', true); if (!empty($galleryImage)) { $thumbnails[0] = [ 'media' => $galleryImage, ]; } foreach ($this->variants as $variant) { if (!empty($variant['media']['meta_value'])) { $thumbnails[$variant['id']] = [ 'media' => $variant['media']['meta_value'], ]; } else { $this->defaultImageUrl = $featuredMedia; $this->defaultImageAlt = Arr::get($variant, 'variation_title', ''); } } $images = empty($thumbnails) ? [] : $thumbnails; $this->images = $images; if (!empty($images)) { $imageId = $this->defaultGalleryImageId; if (isset($images[$imageId])) { $imageMetaValue = $images[$imageId]; $this->defaultImageUrl = Arr::get($imageMetaValue, 'media.0.url', ''); $this->defaultImageAlt = Arr::get($imageMetaValue, 'media.0.title', ''); } } ?> images, 'media.*.url'); if(count($totalThumbImages) == 1 && is_countable($totalThumbImages[0]) && count($totalThumbImages[0]) == 1){ return ''; } // Collect ALL gallery images as JSON for lightbox (even when max thumbnails limits visible thumbs) $allGalleryImages = []; foreach ($this->images as $imageId => $image) { if (empty($image['media']) || !is_array($image['media'])) { continue; } foreach ($image['media'] as $item) { $url = Arr::get($item, 'url', ''); if (empty($url)) { continue; } $allGalleryImages[] = [ 'url' => $url, 'title' => Arr::get($item, 'title', ''), 'variation_id' => (string) $imageId, ]; } } ?> images as $imageId => $image) { if (empty($image['media']) || !is_array($image['media'])) { continue; } foreach ($image['media'] as $item) { if (!empty(Arr::get($item, 'url', ''))) { $totalImages++; } } } // Then render up to max foreach ($this->images as $imageId => $image) { if (empty($image['media']) || !is_array($image['media'])) { continue; } foreach ($image['media'] as $item) { if (empty(Arr::get($item, 'url', ''))) { continue; } if ($maxThumbnails !== null && $count >= (int) $maxThumbnails) { $this->renderGallerySeeMoreButton($totalImages - (int) $maxThumbnails); return; } $this->renderGalleryThumbControlButton($item, $imageId); $count++; } } } public function renderGallerySeeMoreButton($remainingCount) { ?> defaultVariationId ? 'is-hidden' : ''; $itemUrl = Arr::get($item, 'url', ''); $itemTitle = Arr::get($item, 'title', ''); $isSelected = !$this->galleryActiveSet && $imageId == $this->defaultGalleryImageId; if ($isSelected) { $this->galleryActiveSet = true; } ?> 'all', // horizontal, vertical 'thumb_position' => 'bottom', // bottom, left, right, top 'scrollable_thumbs' => 'no', // yes / no 'max_thumbnails' => null, // null = no limit, integer = max visible ]; $atts = wp_parse_args($args, $defaults); $thumbnailMode = $atts['thumbnail_mode']; $wrapperAtts = [ 'class' => 'fct-product-gallery-wrapper ' . 'thumb-pos-' . $atts['thumb_position'] . ' thumb-mode-' . $thumbnailMode, 'data-fct-product-gallery' => '', 'data-fluent-cart-product-gallery-wrapper' => '', 'data-thumbnail-mode' => $thumbnailMode, 'data-product-id' => $this->product->ID, 'data-scrollable-thumbs' => $atts['scrollable_thumbs'], ]; ?>
> renderGalleryThumb(); $this->renderGalleryThumbControls($atts['max_thumbnails']); ?>

product->post_title); ?>

product->detail->getStockAvailability(); if (!Arr::get($stockAvailability, 'manage_stock')) { return ''; } $isStock = $this->product->isStock(); // Check default variant stock for both simple and variable products if ($this->defaultVariant) { $isStock = $isStock && $this->defaultVariant->isStock(); } $stockLabel = $isStock ? $stockAvailability['availability'] : __('Out of Stock', 'fluent-cart'); $statusClass = $isStock ? ($stockAvailability['class'] ?? '') : 'out-of-stock'; echo sprintf( '
%3$s %4$s
', esc_attr($statusClass), $wrapper_attributes, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped esc_html__('Availability:', 'fluent-cart'), esc_html($stockLabel) ); } public function renderSku($wrapper_attributes = '', $showLabel = true, $label = '', $variant = null) { if (!$variant) { $variant = $this->defaultVariant ?: $this->product->variants->first(); } if (!$variant || empty($variant->sku)) { return; } if (!$label) { $label = __('SKU:', 'fluent-cart'); } $labelHtml = ''; if ($showLabel && $label) { $labelHtml = sprintf('%s ', esc_html($label)); } echo sprintf( '
%s%s
', $wrapper_attributes, // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped $labelHtml, esc_html($variant->sku) ); } public function renderExcerpt() { $excerpt = $this->product->post_excerpt; if (!$excerpt) { return; } ?>

product->ID); if (!$post || empty($post->post_content)) { return; } ?>
post_content); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
product->detail->variation_type === 'simple') { // we have to render for the simple product $first_price = $this->product->variants()->first(); $itemPrice = $first_price ? $first_price->item_price : 0; $itemPrice = apply_filters('fluent_cart/product/display_price', $itemPrice, [ 'product' => $this->product, 'variation' => $first_price, ]); $itemPrice = (int)$itemPrice; $comparePrice = $first_price ? (int)$first_price->compare_price : 0; if ($comparePrice <= $itemPrice) { $comparePrice = 0; } do_action('fluent_cart/product/single/before_price_block', [ 'product' => $this->product, 'current_price' => $itemPrice, 'scope' => 'price_range' ]); ?>
$this->product, 'current_price' => $itemPrice, 'scope' => 'price_range' ]); ?>
$this->product, 'current_price' => $itemPrice, 'scope' => 'price_range' ]); return; } $min_price = $this->product->detail->min_price; $max_price = $this->product->detail->max_price; do_action('fluent_cart/product/single/before_price_range_block', [ 'product' => $this->product, 'current_price' => $min_price, 'scope' => 'price_range' ]); ?>
$min_price): ?> $this->product, 'current_price' => $min_price, 'scope' => 'price_range' ]); ?>
$this->product, 'current_price' => $min_price, 'scope' => 'price_range' ]); } public function renderVariants($atts = []) { if ($this->product->detail->variation_type === 'simple') { return; } $variants = $this->product->variants; if (!$variants || $variants->isEmpty()) { return; } // Sort by serial_index ascending $variants = $variants->sortBy('serial_index')->values(); $classes = array_filter([ 'fct-product-variants', 'column-type-' . $this->columnType, Arr::get($atts, 'wrapper_class', ''), ]); ?>
$this->product, 'variant' => $variant, 'scope' => 'product_variant_item' ]); $this->renderVariationItem($variant, $this->defaultVariationId); do_action('fluent_cart/product/single/after_variant_item', [ 'product' => $this->product, 'variant' => $variant, 'scope' => 'product_variant_item' ]); } ?>
product->detail->variation_type === 'simple' && !$this->hasSubscription) { return; // for simple product we already rendered the price } do_action('fluent_cart/product/single/before_price_block', [ 'product' => $this->product, 'current_price' => $this->defaultVariant ? $this->defaultVariant->item_price : 0, 'scope' => 'product_variant_price' ]); foreach ($this->product->variants as $variant) { if ($this->shouldRenderPriceInPriceSection()) { $this->renderVariantPricingWrapperStart($variant); $paymentType = Arr::get($variant->other_info, 'payment_type', 'onetime'); if (!$this->hasSubscription) { $this->renderVariationComparePrice($variant); $this->applyVariationPriceFilter($variant, $paymentType); } else { $atts = [ 'class' => 'fct-product-payment-type fluent-cart-product-variation-content' . ($this->defaultVariant->id != $variant->id ? ' is-hidden' : ''), 'data-fluent-cart-product-payment-type' => '', 'data-variation-id' => $variant->id ]; $this->renderComparePriceWrapperStart($atts); $this->renderVariationComparePrice($variant); if ($paymentType === 'onetime') { echo esc_html(Helper::toDecimal($variant->item_price)); } else { $this->applyVariationPriceFilter($variant, $paymentType); } $this->renderComparePriceWrapperEnd(); } $this->renderVariantPricingWrapperEnd(); } } foreach ($this->product->variants as $variant) { $this->renderVariationsBundleProduct($variant); } do_action('fluent_cart/product/single/after_price_block', [ 'product' => $this->product, 'current_price' => $this->defaultVariant ? $this->defaultVariant->item_price : 0, 'scope' => 'product_variant_price' ]); } public function shouldRenderPriceInPriceSection(): bool { return !($this->viewType === 'text' && $this->columnType === 'one'); } public function applyVariationPriceFilter($variant, $paymentType = 'onetime') { $priceText = $paymentType === 'onetime' ? Helper::toDecimal($variant->item_price) : $variant->getSubscriptionTermsText(true); echo wp_kses_post(apply_filters('fluent_cart/single_product/variation_price', esc_html($priceText), [ 'product' => $this->product, 'variant' => $variant, 'scope' => 'product_variant_price' ])); do_action('fluent_cart/product/after_price', [ 'product' => $this->product, 'current_price' => $variant->item_price, 'scope' => 'product_variant_price' ]); } public function renderComparePriceWrapperStart($atts = []) { ?>
renderAttributes($atts); ?> >
compare_price) { return; } ?> compare_price)); ?>
bundleChildren) == 0) { return; } if(is_object($variant->bundleChildren)) { $bundleProducts = $variant->bundleChildren->toArray(); }else{ $bundleProducts = $variant->bundleChildren; } $total = count($bundleProducts); ?>

-

2): ?>

-

2) : ?>
product->soldIndividually(); if (!$this->hasOnetime || $soldIndividually) { return; } $attributes = [ 'data-fluent-cart-product-quantity-container' => '', 'data-cart-id' => $this->defaultVariant ? $this->defaultVariant->id : '', 'data-variation-type' => $this->product->detail->variation_type, 'data-payment-type' => 'onetime', 'class' => 'fct-product-quantity-container' ]; $defaultVariantData = $this->getDefaultVariantData(); if ($this->hasSubscription && Arr::get($defaultVariantData, 'payment_type') !== 'onetime') { $attributes['class'] .= ' is-hidden'; } do_action('fluent_cart/product/single/before_quantity_block', [ 'product' => $this->product, 'scope' => 'product_quantity_block' ]); ?>
renderAttributes($attributes); ?>>
class="fct-quantity-input" data-fluent-cart-single-product-page-product-quantity-input type="number" inputmode="numeric" pattern="[0-9]*" placeholder="" value="1" aria-label="" />
$this->product, 'scope' => 'product_quantity_block' ]); } public function renderPurchaseButtons($atts = []) { $buyNowButtonAtts = $atts; $this->renderBuyNowButton($buyNowButtonAtts); $this->renderAddToCartButton($atts); } public function renderBuyNowButton($atts = []) { // Stock management check using isStock() method // if (ModuleSettings::isActive('stock_management')) { // if ($this->product->detail->variation_type === 'simple' && $this->defaultVariant) { // if (!$this->defaultVariant->isStock()) { // echo '' . esc_html__('Out of stock', 'fluent-cart') . ''; // return; // } // } // } $defaults = [ 'buy_now_text' => __('Buy Now', 'fluent-cart'), 'add_to_cart_text' => __('Add To Cart', 'fluent-cart'), ]; $atts = wp_parse_args($atts, $defaults); $enableModalCheckout = Helper::isModalCheckoutEnabled(); $isInStock = true; if (ModuleSettings::isActive('stock_management')) { $isInStock = $this->product->isStock() && ($this->defaultVariant && $this->defaultVariant->isStock()); } $stockStatus = $isInStock ? 'in-stock' : 'out-of-stock'; $variationClass = 'fluent-cart-direct-checkout-button'; if (!$isInStock) { $variationClass .= ' is-hidden'; } $buyNowAttributes = [ 'data-fluent-cart-direct-checkout-button' => '', 'data-variation-type' => $this->product->detail->variation_type, 'class' => $variationClass, 'data-stock-availability' => $stockStatus, 'data-quantity' => '1', 'data-cart-id' => $this->defaultVariant ? $this->defaultVariant->id : '', 'data-url' => site_url('?fluent-cart=instant_checkout&item_id='), ]; if ($isInStock) { $buyNowAttributes['href'] = site_url('?fluent-cart=instant_checkout&item_id=') . ($this->defaultVariant ? $this->defaultVariant->id : '') . '&quantity=1'; } if ($enableModalCheckout) { $buyNowAttributes['data-fct-instant-checkout-button'] = ''; $buyNowAttributes['data-enable-modal-checkout'] = 'yes'; } $buyButtonText = apply_filters('fluent_cart/product/buy_now_button_text', $atts['buy_now_text'], [ 'product' => $this->product ]); ?> defaultVariant ? $this->defaultVariant->variation_title : ''; $buyNowAriaLabel = $variantTitle ? sprintf( /* translators: 1: Button text (e.g. "Buy Now"), 2: Variant name */ __('%1$s - %2$s', 'fluent-cart'), $buyButtonText, $variantTitle ) : $buyButtonText; ?> renderAttributes($buyNowAttributes); ?> aria-label=""> $text, 'class' => '', 'target' => '', 'rel' => '', 'is_shortcode' => false, ]; $atts = wp_parse_args($atts, $defaults); $enableModalCheckout = Arr::get($atts, 'enable_modal_checkout', false); $isInStock = true; if (ModuleSettings::isActive('stock_management')) { $isInStock = $this->product->isStock() && ($this->defaultVariant && $this->defaultVariant->isStock()); } $stockStatus = $isInStock ? 'in-stock' : 'out-of-stock'; $checkoutUrl = add_query_arg([ 'fluent-cart' => $enableModalCheckout ? 'modal_checkout' : 'instant_checkout', 'item_id' => $variantId ?? '', 'quantity' => 1 ], site_url()); $buyNowClass = trim('wp-block-button__link wp-element-button ' . Arr::get($atts, 'class', '')); if ($stockStatus === 'out-of-stock') { $buyNowClass .= ' out-of-stock'; } $buyNowAttributes = [ 'data-fluent-cart-direct-checkout-button' => '', 'data-variation-type' => $this->product->detail->variation_type, 'class' => $buyNowClass, 'data-stock-availability' => $stockStatus, 'data-quantity' => '1', 'data-cart-id' => $variantId ?? '', 'data-url' => $checkoutUrl, ]; if ($stockStatus === 'out-of-stock') { $buyNowAttributes['aria-disabled'] = 'true'; } else { $buyNowAttributes['href'] = $checkoutUrl; } $target = Arr::get($atts, 'target'); if ($target) { $buyNowAttributes['target'] = $target; if (strtolower($target) === '_blank') { $buyNowAttributes['rel'] = Arr::get($atts, 'rel', 'noopener noreferrer'); } } if ($enableModalCheckout) { $buyNowAttributes['data-fct-instant-checkout-button'] = ''; $buyNowAttributes['data-enable-modal-checkout'] = 'yes'; } $wrapperAttributes = ''; $isShortcode = !empty($atts['is_shortcode']); if ($isShortcode) { foreach ($buyNowAttributes as $attr => $value) { if ($value === '') { $wrapperAttributes .= esc_attr($attr) . ' '; } else { $wrapperAttributes .= sprintf('%s="%s" ', esc_attr($attr), esc_attr((string)$value)); } } $wrapperAttributes = trim($wrapperAttributes); } else { $wrapperAttributes = get_block_wrapper_attributes($buyNowAttributes); } $buyButtonText = apply_filters('fluent_cart/product/buy_now_button_text', $atts['buy_now_text'], [ 'product' => $this->product ]); ?> aria-label=""> __('Buy Now', 'fluent-cart'), 'add_to_cart_text' => __('Add To Cart', 'fluent-cart'), ]; $atts = wp_parse_args($atts, $defaults); $cartAttributes = [ 'data-fluent-cart-add-to-cart-button' => '', 'data-cart-id' => $this->defaultVariant ? $this->defaultVariant->id : '', 'data-product-id' => $this->product->ID, 'class' => 'fluent-cart-add-to-cart-button', 'data-variation-type' => $this->product->detail->variation_type, ]; $defaultVariantData = $this->getDefaultVariantData(); // If product is subscription-only, hide add-to-cart if ($this->hasSubscription && Arr::get($defaultVariantData, 'payment_type') !== 'onetime') { $cartAttributes['class'] .= ' is-hidden'; } // Check stock availability using both product-level and variant-level $isOutOfStock = false; if (ModuleSettings::isActive('stock_management')) { if (!$this->product->isStock() || ($this->defaultVariant && !$this->defaultVariant->isStock())) { $isOutOfStock = true; $cartAttributes['disabled'] = 'disabled'; $cartAttributes['class'] .= ' out-of-stock'; $cartAttributes['aria-disabled'] = 'true'; $atts['add_to_cart_text'] = __('Not Available', 'fluent-cart'); } } $addToCartText = apply_filters('fluent_cart/product/add_to_cart_text', $atts['add_to_cart_text'], [ 'product' => $this->product ]); // Render add to cart if product supports onetime, or if out of stock (to show "Not Available") if ($this->hasOnetime || $isOutOfStock) : ?> defaultVariant ? $this->defaultVariant->variation_title : ''; $addToCartAriaLabel = $variantTitle ? sprintf( /* translators: 1: Button text (e.g. "Add To Cart"), 2: Variant name */ __('%1$s - %2$s', 'fluent-cart'), $addToCartText, $variantTitle ) : $addToCartText; ?> $text, ]; $atts = wp_parse_args($atts, $defaults); $cartAttributes = [ 'data-fluent-cart-add-to-cart-button' => '', 'class' => 'wp-block-button__link wp-element-button fct-loader', 'data-cart-id' => $this->defaultVariant ? $this->defaultVariant->id : '', 'data-product-id' => $this->product->ID, 'data-variation-type' => $this->product->detail->variation_type, ]; if ($extraClass) { $cartAttributes['class'] .= ' ' . $extraClass; } // If the product does NOT support one-time purchase if (!$this->hasOnetime) { if (Helper::isAdminUser()) { $view = '

' . esc_html__('Add to Cart is not supported for subscription product', 'fluent-cart') . '

'; FrontendView::make('', $view); return; } return; } // Check stock availability using both product-level and variant-level if (ModuleSettings::isActive('stock_management')) { if (!$this->product->isStock() || ($this->defaultVariant && !$this->defaultVariant->isStock())) { $cartAttributes['disabled'] = 'disabled'; $cartAttributes['class'] .= ' out-of-stock'; $cartAttributes['aria-disabled'] = 'true'; $atts['add_to_cart_text'] = __('Not Available', 'fluent-cart'); } } $addToCartText = apply_filters('fluent_cart/product/add_to_cart_text', $atts['add_to_cart_text'], [ 'product' => $this->product ]); $wrapperAttributes = ''; $isShortcode = !empty($atts['is_shortcode']); if ($isShortcode) { foreach ($cartAttributes as $attr => $value) { if ($value === '') { $wrapperAttributes .= esc_attr($attr) . ' '; } else { $wrapperAttributes .= sprintf('%s="%s" ', esc_attr($attr), esc_attr((string)$value)); } } $wrapperAttributes = trim($wrapperAttributes); } else { $wrapperAttributes = get_block_wrapper_attributes($cartAttributes); } ?>

available; if (!$variant->manage_stock) { $availableStocks = 'unlimited'; } $comparePrice = $variant->compare_price; if ($comparePrice <= $variant->item_price) { $comparePrice = ''; } if ($comparePrice) { $comparePrice = Helper::toDecimal($comparePrice); } $paymentType = Arr::get($variant->other_info, 'payment_type'); $itemClasses = [ 'fct-product-variant-item', 'fct_price_type_' . $paymentType, 'fct_variation_view_type_' . $this->viewType, ]; if ($variant->media_id) { $itemClasses[] = 'fct-item-has-image'; } if ($variant->id == $defaultId) { $itemClasses[] = 'selected'; } $priceSuffix = apply_filters('fluent_cart/product/price_suffix_atts', '', [ 'product' => $this->product, 'variant' => $variant, 'scope' => 'variant_item' ]); $renderingAttributes = [ 'data-fluent-cart-product-variant' => '', 'data-cart-id' => $variant->id, 'data-item-stock' => $variant->isStock() ? 'in-stock' : 'out-of-stock', 'data-default-variation-id' => $defaultId, 'data-payment-type' => $paymentType, 'data-available-stock' => $availableStocks, 'data-item-price' => Helper::toDecimal($variant->item_price), 'data-compare-price' => $comparePrice, 'data-price-suffix' => $priceSuffix, 'data-stock-management' => ModuleSettings::isActive('stock_management') ? 'yes' : 'no', 'data-sku' => $variant->sku ?? '', ]; if ($paymentType === 'subscription') { $renderingAttributes['data-subscription-terms'] = $variant->getSubscriptionTermsText(true); $repeatInterval = Arr::get($variant->other_info, 'repeat_interval', ''); $hasInstallment = Arr::get($variant->other_info, 'has_installment') === 'yes'; $itemClasses[] = 'fct_sub_interval_' . $repeatInterval; if ($hasInstallment) { $itemClasses[] = 'fct_sub_has_installment'; } } if ($extraClasses) { $itemClasses = array_merge($itemClasses, $extraClasses); } $itemClasses = array_filter($itemClasses); $renderingAttributes['class'] = implode(' ', $itemClasses); $itemPrice = $variant->item_price; $comparePrice = $variant->compare_price; if (!$comparePrice || $comparePrice <= $itemPrice) { $comparePrice = 0; } ?>
renderAttributes($renderingAttributes); ?> role="radio" tabindex="id == $defaultId ? '0' : '-1'; ?>" aria-checked="id == $defaultId ? 'true' : 'false'; ?>" aria-label="variation_title); ?>" > viewType === 'image'): ?> renderTooltip($variant); ?>
viewType === 'both' || $this->viewType === 'image') { $this->renderVariantImage($variant); } ?> viewType === 'both' || $this->viewType === 'text') { echo '
' . esc_html($variant->variation_title) . '
'; } ?>
viewType === 'text' && $paymentType === 'subscription' && $this->columnType === 'one'): ?> renderSubscriptionInfo($variant); ?> viewType === 'text' && $this->columnType === 'one'): ?>
thumbnail; if (!$image) { $image = Vite::getAssetUrl('images/placeholder.svg'); } ?>
<?php echo esc_attr($variant->variation_title); ?>
getSubscriptionTermsText(true); if (!$info) { return ''; } ?>
$value) { if ($value !== '') { echo esc_attr($attr) . '="' . esc_attr((string)$value) . '" '; } else { echo esc_attr($attr) . ' '; } } } protected function renderTab($atts = []) { ?>
renderTabNav(); ?>
renderTabPane($atts); ?>
paymentTypes as $typeKey => $typeLabel) : ?>
columnType, Arr::get($atts, 'wrapper_class', ''), ]; foreach ($this->variantsByPaymentTypes as $variantKey => $variants): ?>
sortBy('serial_index')->values(); foreach ($variants as $variant) { do_action('fluent_cart/product/single/before_variant_item', [ 'product' => $this->product, 'variant' => $variant, 'scope' => 'product_variant_item' ]); $this->renderVariationItem($variant, $this->defaultVariationId); do_action('fluent_cart/product/single/after_variant_item', [ 'product' => $this->product, 'variant' => $variant, 'scope' => 'product_variant_item' ]); } ?>
variants) || !$this->defaultVariationId) { return null; } foreach ($this->variants as $variant) { if ($variant['id'] == $this->defaultVariationId) { return $variant; } } return null; } }