WooCommerce: Merge 3 custom shortcodes in one

跟風遠走 提交于 2019-12-06 15:45:38

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"]

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!