WooCommerce: Merge 3 custom shortcodes in one

2019-05-30 19:29发布

I have three separate woocommerce snippets where each one have one function. I'm trying to combine them together in one script but can't seems to return more than one value.

function display_woocommerce_order_count2( $atts, $content = null ) {
$args = shortcode_atts( array(
    'status' => 'completed',
), $atts );
$statuses    = array_map( 'trim', explode( ',', $args['status'] ) );
$order_count = 0;
foreach ( $statuses as $status ) {
    // if we didn't get a wc- prefix, add one
    if ( 0 !== strpos( $status, 'wc-' ) ) {
        $status = 'wc-' . $status;
    }
    $order_count += wp_count_posts( 'shop_order' )->$status;
}
ob_start();
return '<span style="color:#fff;text-align:center;font-size:12px">Deals:' . 
$order_count;
$user->total;
return ob_get_clean();
}
add_shortcode( 'wc_order_count3', 'display_woocommerce_order_count2' );

function get_instock_products_count(){
global $wpdb;

// The SQL query
$result = $wpdb->get_col( "
    SELECT COUNT(p.ID)
    FROM {$wpdb->prefix}posts as p
    INNER JOIN {$wpdb->prefix}postmeta as pm ON p.ID = pm.post_id
    WHERE p.post_type LIKE '%product%'
    AND p.post_status LIKE 'publish'
    AND pm.meta_key LIKE '_stock_status'
    AND pm.meta_value LIKE 'instock'
" );

return '<span style="color:#fff;text-align:center;font-size:12px">Proposals 
Left: ' . reset($result);
}
add_shortcode('fp7', 'get_instock_products_count');

function new_proposals2(){
global $wpdb;

// 24 hours ago
$is_24h_ago = date("Y-m-d H:i:s", strtotime(date("Y-m-d H:i:s")." -1day"));

// The SQL query
$result = $wpdb->get_col( "
    SELECT COUNT(p.ID)
    FROM {$wpdb->prefix}posts as p
    WHERE p.post_type LIKE '%product%'
    AND p.post_status LIKE 'publish'
    AND p.post_date > '$is_24h_ago'
" );

return '<span style="color:#fff;text-align:center;font-size:12px">New 
Proposals: ' . reset($result);
}
add_shortcode( 'new_proposals', 'new_proposals2' );

i tried combining all scripts and put function after one another and tried to return those 3 values at the end of the 3 functions. But only the first one is returned.

or return value at the end of each function, no luck.

My goal is to have something similar to:

Proposals: Taken(x) | New(x) | Left(x)

2条回答
冷血范
2楼-- · 2019-05-30 19:53

To merge that 3 shortcodes in one is very easy and can be done this way:

function order_multi_count( $atts, $content = null ) {
    global $wpdb;

    $args = shortcode_atts( array(
        'status' => 'completed',
    ), $atts );

    ## ---- ---- ---- ---- ---- ---- TAKEN ---- ---- ---- ---- ---- ---- ---- ##

    $statuses    = array_map( 'trim', explode( ',', $args['status'] ) );
    $taken = 0;

    foreach ( $statuses as $status ) {
        // if we didn't get a wc- prefix, add one
        if ( 0 !== strpos( $status, 'wc-' ) ) {
            $status = 'wc-' . $status;
        }
        $taken += wp_count_posts( 'shop_order' )->$status;
    }

    ## ---- ---- ---- ---- ---- ----  LEFT ---- ---- ---- ---- ---- ---- ---- ##

    // The SQL query 
    $result = $wpdb->get_col( "
        SELECT COUNT(p.ID)
        FROM {$wpdb->prefix}posts as p
        INNER JOIN {$wpdb->prefix}postmeta as pm ON p.ID = pm.post_id
        WHERE p.post_type LIKE '%product%'
        AND p.post_status LIKE 'publish'
        AND pm.meta_key LIKE '_stock_status'
        AND pm.meta_value LIKE 'instock'
    " );

    $left = reset($result);

    ## ---- ---- ---- ---- ---- ----  NEW  ---- ---- ---- ---- ---- ---- ---- ##

    // 24 hours ago
    $is_24h_ago = date("Y-m-d H:i:s", strtotime(date("Y-m-d H:i:s")." -1day"));

    // The SQL query
    $result2 = $wpdb->get_col( "
        SELECT COUNT(p.ID)
        FROM {$wpdb->prefix}posts as p
        WHERE p.post_type LIKE '%product%'
        AND p.post_status LIKE 'publish'
        AND p.post_date > '$is_24h_ago'
    " );

    $new = reset($result2);

    ## ---- ---- ---- ---- ---- RETURNING VALUE ---- ---- ---- ---- ---- ---- ##

    $style = 'style="color:#fff;text-align:center;font-size:12px"';
    return "<span $style><strong>Proposals:</strong> Taken ($taken) | New ($new) | Left ($left)</span>"; 
}
add_shortcode( 'order_multi_count', 'order_multi_count' );

This should work


USAGE: [order_multi_count] or [order_multi_count status="processing"]

查看更多
霸刀☆藐视天下
3楼-- · 2019-05-30 19:56

While accepted answer is perfectly valid one here is more performant and scalable code.

The issue with your code is that for each and every page load you are fetching & calculating same non-changing data.

This is an overhead which won't affect the site for few thousand visitors but when the products, orders or concurrent traffic goes up above code will start becoming a bottleneck.

In this code I have extensively used Transient API (caching) and instead of updating counts on each page load it will use cached counts. Best part is that it will automatically update the cached counts when any of the data used in each counts changes.

For example, as soon as a product is ordered (or order cancelled) the code will update Taken and Left.


This code is fully tested

Main Shortcode

add_shortcode( 'wp1707_proposal_stats', 'wp1707_proposal_stats_func' );
/**
 * Shortcode to display In-Stock, New in last 24hours and Ordered products.
 *
 * @param      array   $atts   The atts passed via shortcode
 *
 * @return     string  Outputs Shortcode HTML
 */
function wp1707_proposal_stats_func( $atts ){

    $args = shortcode_atts( array(
        'status' => 'completed',
    ), $atts );

    /**
     * Taken
     */
    $requested_statuses = array_map( 'trim', explode( ',', $args['status'] ) );
    $order_count_all_statuses = wp1707_get_count_posts();
    $taken = 0;

    foreach ( $requested_statuses as $key => $status ) {
        // if we didn't get a wc- prefix, add one
        if ( 0 !== strpos( $status, 'wc-' ) ) {
            $status = 'wc-' . $status;
        }
        $taken += $order_count_all_statuses->$status;
    }

    /**
     * Left
     */
    $left = wp1707_instock_products_count( false );

   /**
    * New
    */
   $new = wp1707_new_products_count();

   return sprintf("<span style='proposal-stats'>Proposals:Taken (%d)| Left (%d) | New (%d)</span>", $taken, $left, $new );
}

Utility Functions

add_action('transition_post_status', 'wp1707_update_transients', 10, 3 );
/**
 * Updates transients for Orders Count and In-Stock Products Count
 *
 * @param      string   $new_status  New status passed by WordPress Hook
 * @param      string   $old_status  Old status passed by WordPress Hook
 * @param      WP_Post  $post       WP Post instance
 */
function wp1707_update_transients( $new_status, $old_status, $post ){
    if( 'publish' === $new_status || 'publish' === $old_status ) {
        if ( $post->post_type === 'shop_order' ) {
            wp1707_get_count_posts( true );
        } elseif ( in_array( $post->post_type, array( 'product', 'product_variation' ) ) ) {
            wp1707_new_products_count( true );
        }
    }
}

/**
 * Gets Shop Order Counts for each of the Statuses available
 *
 * @param      boolean  $update  Force update the transient. Pass true to use
 *                               data in transient api. Default false
 *
 * @return     array    Array containing Status as keys and order counts as values.
 */
function wp1707_get_count_posts ( $update = false ){
    $shop_order_count = get_transient( 'wp1707_shop_order_count' );

    if ( !$shop_order_count || !$update) {
        $shop_order_count = wp_count_posts( 'shop_order' );
        set_transient( 'wp1707_shop_order_count', $shop_order_count, 12 * HOUR_IN_SECONDS );
    }

    return reset( $shop_order_count );
}

/**
 * Counts New Products published in last 24hours
 *
 * @param      boolean  $update  Force update the transient. Pass true to use
 *                               data in transient api. Default false
 *
 * @return     int      Count of new products pubplished in last 24hours
 */
function wp1707_new_products_count( $update = false ){

    $new_products_count = get_transient( 'wp1707_new_products_count' );

    if ( !$new_products_count || $update === true ) {
        /**
         * It wasn't there or a product was just published, so regenerate the
         * data and save the transient */
        global $wpdb;

        // 24 hours ago
        $is_24h_ago = date("Y-m-d H:i:s", strtotime(date("Y-m-d H:i:s")." -1day"));

        // The SQL query
        $new_products_count = $wpdb->get_col( "
                                             SELECT COUNT(p.ID)
                                             FROM {$wpdb->posts} as p
                                             WHERE p.post_type LIKE '%product%'
                                             AND p.post_status LIKE 'publish'
                                             AND p.post_date > '$is_24h_ago'
                                             " );

       set_transient( 'wp1707_new_products_count', $new_products_count, 24 * HOUR_IN_SECONDS ); // Tweak the time here per your need
   }

   return reset( $new_products_count );
}


add_action('woocommerce_product_set_stock', 'wp1707_instock_products_count');
add_action('woocommerce_variation_set_stock', 'wp1707_instock_products_count');
/**
 * Counts In-Stock Products
 *
 * @param      boolean  $update  Force update the transient. Pass false to use
 *                               data from transient api. Default true
 *
 * @return     int      Count of instock products
 */
function wp1707_instock_products_count( $update = true ){

    // Get any existing copy of our transient data
    $instock_products_count = get_transient( 'wp1707_instock_products_count' );

    if ( !$instock_products_count || $update === true ) {
        /**
         * It wasn't there or stock was updated for some product, so regenerate
         * the data and save the transient */
        global $wpdb;
        // The SQL query
        $instock_products_count = $wpdb->get_col( "
                                SELECT COUNT(p.ID)
                                FROM {$wpdb->posts} as p
                                INNER JOIN {$wpdb->postmeta} as pm ON p.ID = pm.post_id
                                WHERE p.post_type LIKE '%product%'
                                AND p.post_status LIKE 'publish'
                                AND pm.meta_key LIKE '_stock_status'
                                AND pm.meta_value LIKE 'instock'
                                " );

       set_transient( 'wp1707_instock_products_count', $instock_products_count, 12 * HOUR_IN_SECONDS ); // Tweak the time here per your usual sales traffic
    }

   return reset( $instock_products_count );
}

Usage

[wp1707_proposal_stats] or [wp1707_proposal_stats status="processing,on-hold,completed"]

You can style the output by using .proposal-stats class in your CSS.

查看更多
登录 后发表回答