%s', $label_id, esc_html( $label_text ) ); return $html; } /** * Render variation selector dropdown. * * @param WC_Product $product The product object. * @param string $attribute_name Name of the attribute. * @param array $options Available options for this attribute. * @return string Rendered dropdown HTML. */ protected function render_variation_selector( $product, $attribute_name, $options ): string { $selected = $this->get_selected_attribute_value( $product, $attribute_name ); $select_id = esc_attr( 'attribute_' . sanitize_title( $attribute_name ) ); $html = sprintf( ''; return $html; } /** * Get selected attribute value. * * @param WC_Product $product The product object. * @param string $attribute_name Name of the attribute. * @return string Selected value */ private function get_selected_attribute_value( $product, $attribute_name ): string { return $product->get_variation_default_attribute( $attribute_name ); } /** * Get HTML for variation options. * * @param WC_Product $product The product object. * @param string $attribute_name Name of the attribute. * @param array $options Available options. * @param string $selected Selected value. * @param bool $is_taxonomy Whether this is a taxonomy-based attribute. * @return string Options HTML */ private function get_variation_options_html( $product, $attribute_name, $options, $selected, $is_taxonomy ): string { if ( empty( $options ) ) { return ''; } $html = ''; $items = $is_taxonomy ? wc_get_product_terms( $product->get_id(), $attribute_name, array( 'fields' => 'all' ) ) : $options; foreach ( $items as $item ) { $option_value = $is_taxonomy ? $item->slug : $item; $option_label = $is_taxonomy ? $item->name : $item; if ( ! $is_taxonomy || in_array( $option_value, $options, true ) ) { $selected_attr = $is_taxonomy ? selected( sanitize_title( $selected ), $option_value, false ) : selected( $selected, $option_value, false ); /** * Filter the variation option name. * * @since 9.7.0 * * @param string $option_label The option label. * @param WP_Term|string|null $item Term object for taxonomies, option string for custom attributes. * @param string $attribute_name Name of the attribute. * @param WC_Product $product Product object. */ $filtered_label = apply_filters( 'woocommerce_variation_option_name', $option_label, $is_taxonomy ? $item : null, $attribute_name, $product ); $html .= sprintf( '', esc_attr( $option_value ), $selected_attr, esc_html( $filtered_label ) ); } } return $html; } /** * Render variation form. * * @param WC_Product $product Product instance. * @param array $attributes Block attributes. * @return string Rendered form HTML */ private function render_variation_form( $product, $attributes ): string { $variation_attributes = $product->get_variation_attributes(); if ( empty( $variation_attributes ) ) { return ''; } $variations = $this->get_variations_data( $product ); if ( empty( $variations ) ) { return ''; } wp_enqueue_script( 'wc-add-to-cart-variation' ); return $this->get_form_html( $product, $variations, $variation_attributes ); } /** * Get variations data. * * @param WC_Product $product Product instance. * @return array|false */ private function get_variations_data( $product ) { /** * Filter the number of variations threshold. * * @since 9.7.0 * * @param int $threshold Maximum number of variations to load upfront. * @param WC_Product $product Product object. */ $get_variations = count( $product->get_children() ) <= apply_filters( 'woocommerce_ajax_variation_threshold', 30, $product ); return $get_variations ? $product->get_available_variations() : false; } /** * Get form HTML. * * @param WC_Product $product Product instance. * @param array $variations Variations data. * @param array $variation_attributes Variation attributes. * @return string Form HTML */ private function get_form_html( $product, $variations, $variation_attributes ): string { $variations_json = wp_json_encode( $variations ); $variations_attr = function_exists( 'wc_esc_json' ) ? wc_esc_json( $variations_json ) : _wp_specialchars( $variations_json, ENT_QUOTES, 'UTF-8', true ); $form = $this->get_form_opening( $product, $variations_attr ); $form .= $this->get_variations_table( $product, $variation_attributes ); $form .= ''; return $form; } /** * Get form opening HTML. * * @param WC_Product $product Product instance. * @param string $variations_attr Variations JSON. * @return string Form opening HTML */ private function get_form_opening( $product, $variations_attr ): string { return sprintf( '
', absint( $product->get_id() ), $variations_attr ); } /** * Get variations table HTML. * * @param WC_Product $product Product instance. * @param array $variation_attributes Variation attributes. * @return string Table HTML */ private function get_variations_table( $product, $variation_attributes ): string { ob_start(); /** * Action hook to add content before the variations table. * * @since 9.7.0 */ do_action( 'woocommerce_before_variations_table' ); $before_table = ob_get_clean(); $table = ''; foreach ( $variation_attributes as $attribute_name => $options ) { $table .= $this->get_variation_row( $product, $attribute_name, $options ); } $table .= ''; ob_start(); /** * Action hook to add content after the variations table. * * @since 9.7.0 */ do_action( 'woocommerce_after_variations_table' ); $after_table = ob_get_clean(); return $before_table . $table . $after_table; } /** * Get variation row HTML. * * @param WC_Product $product Product instance. * @param string $attribute_name Attribute name. * @param array $options Attribute options. * @return string Row HTML */ private function get_variation_row( $product, $attribute_name, $options ): string { $html = ''; $html .= '' . $this->render_variation_label( $attribute_name ) . ''; $html .= ''; $html .= $this->render_variation_selector( $product, $attribute_name, $options ); $html .= ''; $html .= ''; $variation_attributes = $product->get_variation_attributes(); $attribute_keys = array_keys( $variation_attributes ); if ( end( $attribute_keys ) === $attribute_name ) { $html .= $this->get_reset_button_row(); } return $html; } /** * Get reset button row HTML. * * @return string Row HTML */ private function get_reset_button_row(): string { return sprintf( '%s', wp_kses_post( /** * Filter the reset variation button. * * @since 9.7.0 */ apply_filters( 'woocommerce_reset_variations_link', sprintf( '', esc_html__( 'Clear options', 'woocommerce' ), esc_html__( 'Clear', 'woocommerce' ) ) ) ) ); } /** * Render the block. * * @param array $attributes Block attributes. * @param string $content Block content. * @param WP_Block $block Block instance. * @return string Rendered block output. */ protected function render( $attributes, $content, $block ): string { global $product; if ( $product instanceof \WC_Product && $product->is_type( 'variable' ) ) { return $this->render_variation_form( $product, $attributes ); } return ''; } }