brunal2496
brunal2496

Reputation: 33

How to use variation and add-to-cart button outside woocommerce loop

I'm using woocommerce for my website, but as I only have one main product and two accessories (related products), I don't need a classic shop page, neither single-product pages for each of my product. My main product has a color variation.

I want to have an add-to-cart button, with the color variation dropdown and the quantity field in one of my regular post page. Exactly like on a single-product page, but embedded in my own page, and without the parts of the single-product page I don't need (description, ...).

I finally decided to achieve this using two custom shortcodes I created: [my_vc_product_price id="xxx"] and [my_vc_add2cart_variable_product id="xxx"]. So I can put them where I want.

But my problem is that the behavior of the dropdown menu + variation availability + add-to-cart button is not the same that this elements have in the single-product page: - the availability of he variation doesn't show up when I choose the color in the dropdown menu; - the add-to-cart button is not disable when no color is chosen in the dropdown menu (it should be disable and active only when a color is chosen).

Displaying the price was easy, using some code found on internet:

/**
 * Add shortcode to allow to display product price in a page
 */ 
function my_vc_display_product_price( $args ) {
    $product_id = $args['id'];
    $product = wc_get_product( $product_id );
    echo '<p class="price">' . $product->get_price_html() . '</p>';

}
add_shortcode( 'my_vc_product_price', 'my_vc_display_product_price');

To get the same graphical result, I just had to add some CSS classes on the row: "woocommerce" and "product".

The code to display the dropdown menu + the quantity and the add-to-cart button is almost the same than in the variable.php file found in plugins/woocommerce/templates/single-product/add-to-cart/. The only things really change is that you need to get the "variation attributes" of the product, and not the attributes.

$attributes = $product->get_variation_attributes();
$attribute_keys = array_keys( $attributes );

So the full function code is:

/**
 * Add shortcode to allow to display an add to cart button with dropdown menu for variation attributes
 */ 
function my_vc_add_to_cart_button_variable_product( $args ) {
    global $product;
    $product_id = $args['id'];
    $product = wc_get_product( $product_id );
    if( $product->is_type( 'variable' )) {
        $attributes = $product->get_variation_attributes();
        $attribute_keys = array_keys( $attributes );
        $available_variations = array( $product->get_available_variations() );

        do_action( 'woocommerce_before_add_to_cart_form' ); ?>

        <form class="variations_form cart" method="post" enctype='multipart/form-data' data-product_id="<?php echo absint( $product->get_id() ); ?>" data-product_variations="<?php echo htmlspecialchars( wp_json_encode( $available_variations ) ) ?>">
            <?php do_action( 'woocommerce_before_variations_form' ); ?>

            <?php if ( empty( $available_variations ) && false !== $available_variations ) : ?>
                <p class="stock out-of-stock"><?php _e( 'This product is currently out of stock and unavailable.', 'woocommerce' ); ?></p>
            <?php else : ?>
                <table class="variations" cellspacing="0">
                    <tbody>
                        <?php foreach ( $attributes as $attribute_name => $options ) : ?>
                            <tr>
                                <td class="value">
                                    <?php
                                        $selected = isset( $_REQUEST[ 'attribute_' . sanitize_title( $attribute_name ) ] ) ? wc_clean( stripslashes( urldecode( $_REQUEST[ 'attribute_' . sanitize_title( $attribute_name ) ] ) ) ) : $product->get_variation_default_attribute( $attribute_name );
                                        wc_dropdown_variation_attribute_options( array( 'options' => $options, 'attribute' => $attribute_name, 'product' => $product, 'selected' => $selected ) );
                                    ?>
                                </td>
                            </tr>
                        <?php endforeach;?>
                    </tbody>
                </table>

                <?php do_action( 'woocommerce_before_add_to_cart_button' ); ?>

                <div class="single_variation_wrap">
                    <?php
                        /**
                         * woocommerce_before_single_variation Hook.
                         */
                        do_action( 'woocommerce_before_single_variation' );

                        /**
                         * woocommerce_single_variation hook. Used to output the cart button and placeholder for variation data.
                         * @since 2.4.0
                         * @hooked woocommerce_single_variation - 10 Empty div for variation data.
                         * @hooked woocommerce_single_variation_add_to_cart_button - 20 Qty and cart button.
                         */
                        do_action( 'woocommerce_single_variation' );
                        ?>
                        <script type="text/template" id="tmpl-variation-template">
                            <div class="woocommerce-variation-description">{{{ data.variation.variation_description }}}</div>
                            <div class="woocommerce-variation-price">{{{ data.variation.price_html }}}</div>
                            <div class="woocommerce-variation-availability">{{{ data.variation.availability_html }}}</div>
                        </script>
                        <script type="text/template" id="tmpl-unavailable-variation-template">
                            <p><?php _e( 'Sorry, this product is unavailable. Please choose a different combination.', 'woocommerce' ); ?></p>
                        </script>
                        <?php

                        /**
                         * woocommerce_after_single_variation Hook.
                         */
                        do_action( 'woocommerce_after_single_variation' );
                    ?>
                </div>

                <?php do_action( 'woocommerce_after_add_to_cart_button' ); ?>
            <?php endif; ?>

            <?php do_action( 'woocommerce_after_variations_form' ); ?>
        </form>

        <?php
        do_action( 'woocommerce_after_add_to_cart_form' );
    }
}
add_shortcode( 'my_vc_add2cart_variable_product', 'my_vc_add_to_cart_button_variable_product');

Any idea what is going wrong? I don't understand, if the code is the same, why it didn't execute the same. Is there something missing because this code is outside woocommerce pages?

Upvotes: 3

Views: 11237

Answers (2)

brunal2496
brunal2496

Reputation: 33

That's just perfect! Many thanks. Now the hardest to come: I need to understand your code and compare it with mine ;-)

Upvotes: 0

Rajdeep Tayde
Rajdeep Tayde

Reputation: 593

try using following code

function add_to_cart_form_shortcode( $atts ) {
        if ( empty( $atts ) ) {
            return '';
        }

        if ( ! isset( $atts['id'] ) && ! isset( $atts['sku'] ) ) {
            return '';
        }

        $args = array(
            'posts_per_page'      => 1,
            'post_type'           => 'product',
            'post_status'         => 'publish',
            'ignore_sticky_posts' => 1,
            'no_found_rows'       => 1,
        );

        if ( isset( $atts['sku'] ) ) {
            $args['meta_query'][] = array(
                'key'     => '_sku',
                'value'   => sanitize_text_field( $atts['sku'] ),
                'compare' => '=',
            );

            $args['post_type'] = array( 'product', 'product_variation' );
        }

        if ( isset( $atts['id'] ) ) {
            $args['p'] = absint( $atts['id'] );
        }

        $single_product = new WP_Query( $args );

        $preselected_id = '0';


        if ( isset( $atts['sku'] ) && $single_product->have_posts() && 'product_variation' === $single_product->post->post_type ) {

            $variation = new WC_Product_Variation( $single_product->post->ID );
            $attributes = $variation->get_attributes();


            $preselected_id = $single_product->post->ID;


            $args = array(
                'posts_per_page'      => 1,
                'post_type'           => 'product',
                'post_status'         => 'publish',
                'ignore_sticky_posts' => 1,
                'no_found_rows'       => 1,
                'p'                   => $single_product->post->post_parent,
            );

            $single_product = new WP_Query( $args );
        ?>
            <script type="text/javascript">
                jQuery( document ).ready( function( $ ) {
                    var $variations_form = $( '[data-product-page-preselected-id="<?php echo esc_attr( $preselected_id ); ?>"]' ).find( 'form.variations_form' );
                    <?php foreach ( $attributes as $attr => $value ) { ?>
                        $variations_form.find( 'select[name="<?php echo esc_attr( $attr ); ?>"]' ).val( '<?php echo esc_js( $value ); ?>' );
                    <?php } ?>
                });
            </script>
        <?php
        }

        $single_product->is_single = true;
        ob_start();
        global $wp_query;

        $previous_wp_query = $wp_query;

        $wp_query          = $single_product;

        wp_enqueue_script( 'wc-single-product' );
        while ( $single_product->have_posts() ) {
            $single_product->the_post()
            ?>
            <div class="single-product" data-product-page-preselected-id="<?php echo esc_attr( $preselected_id ); ?>">
                <?php woocommerce_template_single_add_to_cart(); ?>
            </div>
            <?php
        }

        $wp_query = $previous_wp_query;

        wp_reset_postdata();
        return '<div class="woocommerce">' . ob_get_clean() . '</div>';
}
add_shortcode( 'add_to_cart_form', 'add_to_cart_form_shortcode' );

/*Example Usage [add_to_cart_form id=147]*/

Upvotes: 6

Related Questions