Search code examples
phpwordpresswoocommerceshortcodeproduct-variations

Custom shortcode displaying a loop of WooCommerce product variations


Im trying to make a custom shortcode in order to display some product variations that have no stock quantity with stock <= 0.

Here's what I did so far:

if( ! function_exists('preorable_products') ) {

    // Add Shortcode
    function preorable_products( $atts ) {
        global $woocommerce_loop;

        // Attributes 
        $atts = shortcode_atts(
            array(
                'columns'   => '4',
                'limit'     => '20',
                'preordable'     => "yes",
                'stock'       => 0,
            ),
            $atts, 'preorable_products'
        );


        $woocommerce_loop['columns'] = $atts['columns'];
        
        // The WP_Query
        $products_variation = new WP_Query( array (
            'post_type'         => 'product_variation',
            'post_status'       => 'publish',
            'fields'         => 'id=>parent',
            'posts_per_page'    => $atts['limit'],
            'meta_query'        => array(
                'relation'      => 'AND',
                'preordable'  => array(
                    'key'       =>'_ab_preorder_checkbox',
                    'value'     => "yes",
                    'compare'   => '=='
                ),
                'stock'  => array(
                    'key'       =>'_stock',
                    'value'     => 0,
                    'compare'   => '<='
                ),
            )
        ));
        $products = $products_variation;
        ob_start();
        
        if ( $products->have_posts() ) { ?>

            <?php woocommerce_product_loop_start(); ?>

                <?php while ( $products->have_posts() ) : $products->the_post(); ?>

                    <?php wc_get_template_part( 'content', 'product' ); ?>

                <?php endwhile; // end of the loop. ?>

            <?php woocommerce_product_loop_end(); ?>

            <?php
        } else {
            do_action( "woocommerce_shortcode_products_loop_no_results", $atts );
            echo "<p>Aucun article disponible à la précommande.</p>";
        }

        woocommerce_reset_loop();
        wp_reset_postdata();

        return '<div class="woocommerce columns-' . $atts['columns'] . '">' . ob_get_clean() . '</div>';
    }

    add_shortcode( 'preorable_products', 'preorable_products' );
}

The WP Query part seems to work perfectly, but the code part to display the product seems wrong. I feel like $product variable doesn't have have_post method :

if ( $products->have_posts() ) { ?>
    <?php woocommerce_product_loop_start(); ?>

        <?php while ( $products->have_posts() ) : $products->the_post(); ?>

            <?php wc_get_template_part( 'content', 'product' ); ?>

        <?php endwhile; // end of the loop. ?>

    <?php woocommerce_product_loop_end(); ?>

    <?php
} else {
    do_action( "woocommerce_shortcode_products_loop_no_results", $atts );
    echo "<p>Aucun article disponible à la précommande.</p>";
}

woocommerce_reset_loop();
wp_reset_postdata();

return '<div class="woocommerce columns-' . $atts['columns'] . '">' . ob_get_clean() . '</div>';     

Any help is welcome.


Solution

  • There are some errors and missing things in your code, use instead the following revisited code:

    if( ! function_exists('get_preordable_products') ) {
    
        function get_preordable_products( $atts ) {
            // Shortcode Attributes
            extract( shortcode_atts( array(
                'columns'    => '4',
                'limit'      => '20',
                'preordable' => "yes",
                'stock'      => 0,
            ), $atts, 'preordable_products' ) );
    
            // The WP_Query
            $query = new WP_Query( array (
                'post_type'         => 'product_variation',
                'post_status'       => 'publish',
                'posts_per_page'    => $limit,
                'meta_query'        => array(
                    'relation'      => 'AND',
                    'preordable'  => array(
                        'key'       =>'_ab_preorder_checkbox',
                        'value'     => "yes",
                        'compare'   => '=='
                    ),
                    array(
                        'key'       =>'_stock',
                        'value'     => 0,
                        'compare'   => '<='
                    ),
                )
            ) );
    
            global $woocommerce_loop;
    
            $woocommerce_loop['columns']      = $columns;
            $woocommerce_loop['is_shortcode'] = 1;
            $woocommerce_loop['name']         = 'preordable_products';
            $woocommerce_loop['total']        = $query->post_count;
            $woocommerce_loop['total_pages']  = $query->max_num_pages;
            $woocommerce_loop['per_page']     = $limit;
    
            ob_start();
    
            if ( $query->have_posts() ) {
                woocommerce_product_loop_start();
    
                while ( $query->have_posts() ) {
                    $query->the_post();
                    wc_get_template_part( 'content', 'product' );
                }
                woocommerce_product_loop_end();
                woocommerce_reset_loop();
                wp_reset_postdata();
            } else {
                do_action( "woocommerce_shortcode_products_loop_no_results", $atts );
    
                echo '<p>'. __("Aucun article disponible à la précommande.") . '</p>';
            }
    
            $content = ob_get_clean();
    
            return '<div class="woocommerce columns-' . $columns . '">' . $content . '</div>';
        }
    
        add_shortcode( 'preordable_products', 'get_preordable_products' );
    }
    

    Code goes in functions.php file of the active child theme (or active theme). Tested and works.

    Usage: [preordable_products] or in Php code echo do_shortcode('[preordable_products]');