cart_data` is empty). Used only * to decide whether to swap in the "When empty" Global Block; never * parsed for any other value, and never a second live-cart lookup of our * own (no `fluent_cart` query reimplementation). */ private const EMPTY_MARKER = 'fluent-cart-cart-empty-content'; public static function render(array $properties): string { if (!Requirements::hasFluentCart()) { return FluentCartAdapter::notice( __('FluentCart is unavailable. Activate FluentCart to render this element.', 'sparkcore-commerce'), 'broken' ); } $settings = self::section($properties, 'cart'); if (ProductContext::isBuilderRequest() && empty($settings['live_preview'])) { return FluentCartAdapter::notice( __('This Cart renders live on the frontend and works correctly there. Enable Live editor preview here to inspect the real output in the builder.', 'sparkcore-commerce'), 'runtime' ); } try { return self::renderLive($settings); } catch (\Throwable $error) { if (defined('WP_DEBUG') && WP_DEBUG && function_exists('error_log')) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Diagnostic logging, only reached when WP_DEBUG is enabled. error_log('SparkCore Commerce FC_Cart render error: ' . $error->getMessage()); } return FluentCartAdapter::notice( __('This FluentCart element could not be rendered. Check plugin compatibility.', 'sparkcore-commerce'), 'broken' ); } } private static function renderLive(array $settings): string { // Full customer-state view: same route-no-cache protection // Commerce_View (cart mode) already uses, unlike Mini Cart. CacheGuard::protectCustomerState('cart'); if (!function_exists('shortcode_exists') || !shortcode_exists(self::SHORTCODE)) { return FluentCartAdapter::notice( sprintf( /* translators: %s shortcode tag. */ __('FluentCart shortcode %s is not registered in this version.', 'sparkcore-commerce'), self::SHORTCODE ), 'broken' ); } $html = (string) do_shortcode('[' . self::SHORTCODE . ']'); $html = self::maybeSwapEmptyBlock($html, $settings['when_empty'] ?? null); return '