Get user total purchased items count in Woocmmmerce - php

I'm trying to figure out a function which get current user total number of purchased items (not total sum but items) across as all placed orders. So far I have found this (which doesn't work) - but again this function should get total sum and not items. Been trying to edit it to work but no success so far.
public function get_customer_total_order() {
$customer_orders = get_posts( array(
'numberposts' => - 1,
'meta_key' => '_customer_user',
'meta_value' => get_current_user_id(),
'post_type' => array( 'shop_order' ),
'post_status' => array( 'wc-completed' )
) );
$total = 0;
foreach ( $customer_orders as $customer_order ) {
$order = wc_get_order( $customer_order );
$total += $order->get_total();
}
return $total;
}
Any ideas?

Updated (Taking in account the item quantity)
The following very lightweight function will get the total purchased items count by a customer:
function get_user_total_purchased_items( $user_id = 0 ){
global $wpdb;
$customer_id = $user_id === 0 ? get_current_user_id() : (int) $user_id;
return (int) $wpdb->get_var( "
SELECT SUM(woim.meta_value)
FROM {$wpdb->prefix}woocommerce_order_items AS woi
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS woim ON woi.order_item_id = woim.order_item_id
INNER JOIN {$wpdb->prefix}posts as p ON woi.order_id = p.ID
INNER JOIN {$wpdb->prefix}postmeta as pm ON woi.order_id = pm.post_id
WHERE woi.order_item_type = 'line_item'
AND p.post_type LIKE 'shop_order'
AND p.post_status IN ('wc-completed')
AND pm.meta_key LIKE '_customer_user'
AND pm.meta_value LIKE '$customer_id'
AND woim.meta_key LIKE '_qty'
" );
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
USAGE Example
1) Display the current user total purchased items count:
<?php echo '<p>Total purchased items: ' . get_user_total_purchased_items() . '</p>'; ?>
2) Display the total purchased items count for a given user ID:
// Here the user ID is 105
<?php echo '<p>Total purchased items: ' . get_user_total_purchased_items(105) . '</p>'; ?>

function get_user_total_purchased_items_by_email( $email ){
global $wpdb;
if (empty($email)) {
return;
}
return (int) $wpdb->get_var( "
SELECT SUM(woim.meta_value)
FROM {$wpdb->prefix}woocommerce_order_items AS woi
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS woim ON woi.order_item_id = woim.order_item_id
INNER JOIN {$wpdb->prefix}posts as p ON woi.order_id = p.ID
INNER JOIN {$wpdb->prefix}postmeta as pm ON woi.order_id = pm.post_id
WHERE woi.order_item_type = 'line_item'
AND p.post_type LIKE 'shop_order'
AND p.post_status IN ('wc-completed')
AND pm.meta_key LIKE '_customer_user'
AND pm.meta_value LIKE '$email'
AND woim.meta_key LIKE '_qty'
" );
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Usage Example
<?php echo '<p>Total purchased items: ' . get_user_total_purchased_items_by_email('youremail#mail.com') . '</p>'; ?>

Try this below code
global $wpdb;
$customer_orders = get_posts( array(
'numberposts' => - 1,
'meta_key' => '_customer_user',
'meta_value' => get_current_user_id(),
'post_type' => array( 'shop_order' ),
'post_status' => array( 'wc-completed' )
) );
$customer_orders = json_decode(json_encode($customer_orders),true);
$total_product_count = 0;
foreach ( $customer_orders as $customer_order_data ) {
$Order_ID=$customer_order_data['ID'];
$Select_Order_Details = $wpdb->get_results( "SELECT COUNT(order_item_id) AS total_product_count FROM {$wpdb->prefix}woocommerce_order_items WHERE order_item_type='line_item' and order_id = $Order_ID");
$Select_Order_Details = json_decode(json_encode($Select_Order_Details),true);
foreach($Select_Order_Details as $Product_Count)
{
$total_product_count=$total_product_count+$Product_Count['total_product_count'];
}
}
echo ($total_product_count); exit;

Related

Display total revenue of a product id in WooCommerce shortcode

I am trying to display the total revenue of a given product ID from WooCommerce via a shortcode. I have code this far with the code however I cannot seem to get it to work correctly by selecting the individual ID, at the moment it is showing it for the whole store.
function get_total_sales( $atts ) {
$atts = shortcode_atts( array(
'id' => ''), $atts );
global $wpdb;
$order_totals = apply_filters( 'woocommerce_reports_sales_overview_order_totals', $wpdb->get_row( "
SELECT SUM(meta.meta_value), ['id'], AS total_sales, COUNT(posts.ID) AS total_orders FROM {$wpdb->posts} AS posts
LEFT JOIN {$wpdb->postmeta} AS meta ON posts.ID = meta.post_id
LEFT JOIN {$wpdb->term_relationships} AS rel ON posts.ID=rel.object_ID
LEFT JOIN {$wpdb->term_taxonomy} AS tax USING( term_taxonomy_id )
LEFT JOIN {$wpdb->terms} AS term USING( term_id )
WHERE meta.meta_key = '_order_total'
AND posts.post_type = 'shop_order'
AND posts.post_status IN ( 'wc-" . implode( "','wc-", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed','on-hold', 'processing' ) ) ) . "' )
" ) );
return $order_totals->total_sales;
}
add_shortcode('sales', 'get_total_sales');
I am struggling to find how I can made it specific to the ID and have tried to implement an ID attribute, but it seems to be ignored.
Thank you in advance!
If you just want to get all total sales there is a meta that you can use. This method doesnt check by order statuses.
function get_total_sales_by_product_id( $atts ) {
return get_post_meta($atts['id'], 'total_sales', true);
}
add_shortcode('sales', 'get_total_sales_by_product_id');
To get total sales by list of order statuses try this
function get_total_sales_by_product_id( $atts ){
$atts = shortcode_atts( array(
'id' => ''), $atts );
$product_id = $atts['id'];
if(empty($product_id)) return;
//Add remove order statuses
$order_status = array( 'wc-completed', 'wc-processing' );
global $wpdb;
$order_ids = $wpdb->get_col("
SELECT order_items.order_id
FROM {$wpdb->prefix}woocommerce_order_items as order_items
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
LEFT JOIN {$wpdb->posts} AS posts ON order_items.order_id = posts.ID
WHERE posts.post_type = 'shop_order'
AND posts.post_status IN ( '" . implode( "','", $order_status ) . "' )
AND order_items.order_item_type = 'line_item'
AND order_item_meta.meta_key = '_product_id'
AND order_item_meta.meta_value = '$product_id'
");
$unique_order_ids = array_unique($order_ids);
$total_sales = 0;
foreach ($unique_order_ids as $order_id) {
$order = wc_get_order($order_id);
foreach ($order->get_items() as $item_key => $item ) {
if ($item->get_product()->get_id() == $product_id) {
$total_sales = $total_sales + $item->get_quantity();
}
}
}
return $total_sales;
}
add_shortcode('sales', 'get_total_sales_by_product_id');

How to get coupons from email restrictions with efficiency in WooCommerce

I have the following loop for getting Woocommerce coupons on a page within the my account section of a customers dashboard.
Currently we have 10k+ coupons and just by performing this loop, it's a huge drain on resources and not very efficient causing time outs. Are there any obvious ways in which I can improve the efficiency of it?
Is there a way I can limit the loop to the only search for emails in the "Allowed emails" field (as each coupon is tied to an email address)?
<?php $smart_coupons = get_posts( array(
'posts_per_page' => -1,
'orderby' => 'name',
'order' => 'desc',
'post_type' => 'shop_coupon',
'post_status' => 'publish'
) );
if ( $smart_coupons ) {
foreach( $smart_coupons as $smart_coupon) {
$strcode = strtolower($smart_coupon->post_title);
$full_coupon = new WC_Coupon( $strcode ); ?>
<?php if($full_coupon->discount_type == "smart_coupon"){
$emails = $full_coupon->get_email_restrictions();
if (in_array($current_email, $emails)) {
if($full_coupon->usage_count < $full_coupon->usage_limit){ ?>
coupon content
<?php }
}
}
}
}
As email restrictions are in an array (so an indexed array in the database) it is not possible to get that from a meta query in your WP_Query for many technical reasons.
Now instead you can do a custom very light and effective SQL query to get the "smart" coupons that belong to an email, using the WPDB Class.
I have embedded this SQL query in the function below (where the $discount_type argument is already set buy default to "smart_coupon"):
function get_coupons_from_email( $current_email, $discount_type = 'smart_coupon' ) {
global $wpdb;
return $wpdb->get_col( $wpdb->prepare("
SELECT p.post_name
FROM {$wpdb->prefix}posts p
INNER JOIN {$wpdb->prefix}postmeta pm
ON p.ID = pm.post_id
INNER JOIN {$wpdb->prefix}postmeta pm2
ON p.ID = pm2.post_id
WHERE p.post_type = 'shop_coupon'
AND p.post_status = 'publish'
AND pm.meta_key = 'discount_type'
AND pm.meta_value = '%s'
AND pm2.meta_key = 'customer_email'
AND pm2.meta_value LIKE '%s'
ORDER BY p.post_name DESC
", $discount_type, '%'.$current_email.'%' ) );
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Now you can use it in your code as follows:
// Get smart coupons from email
$smart_coupon_codes = get_coupons_from_email( $current_email );
if ( count($smart_coupon_codes) > 0 ) {
// Loop through smart coupons code
foreach ( $smart_coupon_codes as $coupon_code ) {
$coupon = new WC_Coupon( $coupon_code ); // Get the WC_Coupon Object
if( $coupon->get_usage_count() < $coupon->get_usage_limit() ){
?>
<p>coupon content</p>
<?php
}
}
}
It should work smoothly now.

Total count for each order item in a loop on Woocommerce

Hi I'm learning PHP and Woocommerce at the same time!
I'm trying to get all the orders which have status processing and the total count for each and display this on a page.
Thus far I can loop through all the orders and get the name and quantity of each.
but as I don't know which product is going to be in the list, I'm not sure how I would compare the name and then add the quantity.
My current output is like this:
prod1 - v1 x 1
Prod2 - v3 x 1
prod2 - v3 x 1
prod3 - v2 x 11
prod3 - v2 x 1
What I would like is:
prod1 - v1 x 1
Prod2 - v3 x 2
prod3 - v2 x 12
The code currently is:
<?php
/*
Template Name: Print Supplier Order
*/
if (!is_user_logged_in() || !current_user_can('manage_options')) wp_die('This page is private.');
?>
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title><?php _e('Processing Orders'); ?></title>
<style>
body { background:white; color:black; width: 95%; margin: 0 auto; }
</style>
</head>
<body>
<header>
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
<h1 class="title"><?php the_title(); ?></h1>
<?php the_content(); ?>
<?php endwhile; endif; ?>
</header>
<section>
<?php
global $woocommerce;
$args = array( 'post_type' => 'shop_order', 'post_status' => 'wc-processing', 'posts_per_page' => -1 );
$loop = new WP_Query( $args );
while ( $loop->have_posts() ) : $loop->the_post();
$order_id = $loop->post->ID;
$order = new WC_Order($order_id);
$product_details = array();
$order_items = $order->get_items();
foreach( $order_items as $product ) {
echo $product['name']." x ".$product['qty'];
echo '<br>';
}
?>
<?php endwhile; ?>
</section>
</body>
</html>
Updated (Now the SQL query gives also the product name, make it even lighter)
Instead of using a WP_Query and some heavy code to get your calculations, you should better use this much more lighter and effective version code, using WPDB Class (a SQL query):
global $wpdb;
$results = $wpdb->get_results( "
SELECT DISTINCT woim2.meta_value as id, SUM(woim.meta_value) as count, woi.order_item_name as name
FROM {$wpdb->prefix}woocommerce_order_itemmeta as woim
INNER JOIN {$wpdb->prefix}woocommerce_order_items as woi ON woi.order_item_id = woim.order_item_id
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta as woim2 ON woi.order_item_id = woim2.order_item_id
INNER JOIN {$wpdb->prefix}posts as p ON p.ID = woi.order_id
WHERE p.post_status IN ('wc-processing','wc-on-hold')
AND woim.meta_key LIKE '_qty'
AND woim2.meta_key LIKE '_product_id'
GROUP BY woim2.meta_value
" );
foreach( $results as $result ){
echo $result->name . ' (' . $result->id . ') x ' . $result->count . '<br>';
}
The product count will be based only on the processing oder status and not on product total sales.
Tested and work.
Get the product variations instead (as asked in your comment)
As asked in your comment to get the product variations that are in the order, you will replace the line:
AND woim2.meta_key LIKE '_product_id'
by the following line:
AND woim2.meta_key LIKE '_variation_id'
Get all products and variations (Excluding Variable Products)
To get all products including Product Variations but excluding Variable Products use:
global $wpdb;
$results = $wpdb->get_results( "
SELECT DISTINCT woim2.meta_value as id, SUM(woim.meta_value) as count, woi.order_item_name as name
FROM {$wpdb->prefix}woocommerce_order_itemmeta as woim
INNER JOIN {$wpdb->prefix}woocommerce_order_items as woi ON woi.order_item_id = woim.order_item_id
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta as woim2 ON woi.order_item_id = woim2.order_item_id
INNER JOIN {$wpdb->prefix}posts as p ON p.ID = woi.order_id
WHERE p.post_status IN ('wc-processing','wc-on-hold')
AND woim.meta_key LIKE '_qty'
AND ((woim2.meta_key LIKE '_variation_id' AND woim2.meta_value > 0)
OR (woim2.meta_key LIKE '_product_id'
AND woim2.meta_value NOT IN (SELECT DISTINCT post_parent FROM {$wpdb->prefix}posts WHERE post_type LIKE 'product_variation')))
GROUP BY woim2.meta_value
" );
foreach( $results as $result ){
echo $result->name . ' (' . $result->id . ') x ' . $result->count . '<br>';
}
Tested and work.
To get them all (even product variations and variable products):
global $wpdb;
$results = $wpdb->get_results( "
SELECT DISTINCT woim2.meta_value as id, SUM(woim.meta_value) as count, woi.order_item_name as name
FROM {$wpdb->prefix}woocommerce_order_itemmeta as woim
INNER JOIN {$wpdb->prefix}woocommerce_order_items as woi ON woi.order_item_id = woim.order_item_id
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta as woim2 ON woi.order_item_id = woim2.order_item_id
INNER JOIN {$wpdb->prefix}posts as p ON p.ID = woi.order_id
WHERE p.post_status IN ('wc-processing','wc-on-hold')
AND woim.meta_key LIKE '_qty'
AND ((woim2.meta_key LIKE '_variation_id' AND woim2.meta_value > 0)
OR woim2.meta_key LIKE '_product_id' )
GROUP BY woim2.meta_value
" );
foreach( $results as $result ){
echo $result->name . ' (' . $result->id . ') x ' . $result->count . '<br>';
}
Tested and work.
Method 1
There are a few different ways you could tackle this, but if we want to adjust your code only slightly, then we can achieve this by adding your products in the product loop to a custom array, using the product name as a key, and then working through that array.
Here's an example:
global $woocommerce;
//custom array to list products:
$products_array = array();
//start wp query
$args = array(
'post_type' => 'shop_order',
'post_status' => 'wc-processing',
'posts_per_page'=> -1
);
$loop = new WP_Query( $args );
//loop through orders
while ( $loop->have_posts() ) {
$loop->the_post();
//vars
$order_id = $loop->post->ID;
$order = new WC_Order($order_id);
$product_details = array();
$order_items = $order->get_items();
//loop through products
foreach( $order_items as $product ) {
//if product has been captured to custom array before, increment value, else add it.
if (array_key_exists($product['name'], $products_array)) {
$products_array[$product['name']] += $product['qty'];
} else {
$products_array[$product['name']] = $product['qty'];
}
}
}
//end wp_query
wp_reset_postdata();
//display list:
foreach ($products_array as $title => $quantity) {
echo $title.' x '.$quantity;
}
Method 2
Loop through all products, instead of all your orders (after a while, assuming the store is successful, there will probably be a lot more orders than products, so this method might be faster in the long run). Each product stores it's total sales as a meta value, so accessing it is quite simple:
//WP Query args
$args = array(
'post_type' => 'product',
'posts_per_page'=> -1,
'meta_key' => 'total_sales',
'orderby' => 'title',
'order' => 'ASC',
'meta_query' => array( //make sure we only grab items that have actually sold
array(
'key' => 'total_sales',
'value' => 0,
'compare' => '>'
)
)
);
$query = new WP_Query($args);
//loop through products
if($query->have_posts()) {
while($query->have_posts()) {
$query->the_post();
//use built in Wordpress functions for get_the_title() and get_post_meta()
echo get_the_title().' x '.get_post_meta(get_the_ID(), 'total_sales', true);
}
}
//end wp query
wp_reset_postdata();

Total purchase count by product for current user in Woocommerce

In Woocommerce, I would like to show the total purchase product count for the current user in single product pages. Like for example if John bought a Pen 2 times then it displays the count ("2") in this product page for John user and if Jack bought it 5 times then it will show 5 in this product page for Jack user.
I don't want print total sold count, I want to show as per current logged in user.
My actual code in function.php file:
add_action( 'woocommerce_single_product_summary', 'wc_product_sold_count', 11 );
function wc_product_sold_count() {
$get_current_pro_id = $_SESSION["iddd"];
global $product;
$current_user = wp_get_current_user();
if ( wc_customer_bought_product( $current_user->user_email, $current_user->ID, $product->get_id() ) )
{
$units_sold = get_post_meta( $product->id, 'total_sales', true );
//echo '<p>' . sprintf( __( 'Units Sold: %s', 'woocommerce' ), $units_sold ) . '</p>';
return $units_sold;
}
}
That can be done easily with a very light SQL query in your hooked function:
add_action( 'woocommerce_single_product_summary', 'wc_product_sold_count', 11 );
function wc_product_sold_count() {
// Only for logged in users
if ( ! is_user_logged_in() ) return; // Exit for non logged users
global $wpdb, $product;
$user_id = get_current_user_id(); // Current User ID
$product_id = $product->get_id(); // Current Product ID
// The SQL request
$units_bought = $wpdb->get_var( "
SELECT SUM(woim2.meta_value)
FROM {$wpdb->prefix}woocommerce_order_items AS woi
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta woim ON woi.order_item_id = woim.order_item_id
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta woim2 ON woi.order_item_id = woim2.order_item_id
INNER JOIN {$wpdb->prefix}postmeta pm ON woi.order_id = pm.post_id
INNER JOIN {$wpdb->prefix}posts AS p ON woi.order_id = p.ID
WHERE woi.order_item_type LIKE 'line_item'
AND p.post_type LIKE 'shop_order'
AND p.post_status IN ('wc-completed','wc-processing')
AND pm.meta_key = '_customer_user'
AND pm.meta_value = '$user_id'
AND woim.meta_key = '_product_id'
AND woim.meta_value = '$product_id'
AND woim2.meta_key = '_qty'
");
// Display count if is greater than zero
if( $units_bought > 0 ){
$label = __( 'Units bought' , 'woocommerce' ); // Label
// Output
echo '<p class="units-bought"><strong>' . $label . ': </strong>' . $units_bought . '</p>';
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Related: Display the total purchase count of a specific product for customer in Woocommerce

WooCommerce my-accounts Page - get the current user for orders query

I'm trying to sum specific items for each user, but it seems it doesn't recognize the current user and it sums all the orders for all customers.
How can I solve this? What I am missing?
Here's the code I am using:
$order_items = apply_filters( 'woocommerce_reports_top_earners_order_items', $wpdb->get_results( "
SELECT order_item_meta_2.meta_value as product_id, SUM( order_item_meta.meta_value ) as line_total FROM {$wpdb->prefix}woocommerce_order_items as order_items
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta_2 ON order_items.order_item_id = order_item_meta_2.order_item_id
LEFT JOIN {$wpdb->posts} AS posts ON order_items.order_id = posts.ID
WHERE posts.post_type = 'shop_order'
AND posts.post_status IN ( '" . implode( "','", array( 'wc-completed', 'wc-processing', 'wc-on-hold' ) ) . "' )
AND order_items.order_item_type = 'line_item'
AND order_item_meta.meta_key = '_line_total'
AND order_item_meta_2.meta_key = '_product_id'
GROUP BY order_item_meta_2.meta_value
" ));
$totalPR = 0;
$Products = array(1507, 1406, 1506);
foreach ($order_items as $item) {
if (in_array($item->product_id, $Products)) {
$totalPR = $item->line_total + $totalPR;
echo $totalPR;
}
}
Update 2 - It's working making this:
Get the list of the customers
Go through each customer in a foreach loop
Get the customer ID
Add a LEFT JOIN for wp_postmeta table on the post_id:
LEFT JOIN {$wpdb->postmeta} AS postmeta ON order_items.order_id = postmeta.post_id
insert this 2 lines into your WHERE:
AND postmeta.meta_key = '_customer_user'
AND postmeta.meta_value = '$customer_id'
Then comes your loop inside the customers loop, to get the sum by customer
This is the code:
global $wpdb;
// Set here your product ids
$products = array( 1507, 1406, 1506 );
$all_customers = get_users( 'role=customer' );
foreach ( $all_customers as $customer ) {
$customer_id = $customer->ID;
$total_pr = 0;
$query = $wpdb->get_results("
SELECT order_item_meta_2.meta_value as product_id,
SUM( order_item_meta.meta_value ) as line_total
FROM {$wpdb->prefix}woocommerce_order_items as order_items
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta_2 ON order_items.order_item_id = order_item_meta_2.order_item_id
LEFT JOIN {$wpdb->postmeta} AS postmeta ON order_items.order_id = postmeta.post_id
LEFT JOIN {$wpdb->posts} AS posts ON order_items.order_id = posts.ID
WHERE posts.post_type = 'shop_order'
AND posts.post_status IN ( '" . implode( "','", array( 'wc-completed', 'wc-processing', 'wc-on-hold' ) ) . "' )
AND postmeta.meta_key = '_customer_user'
AND postmeta.meta_value = '$customer_id'
AND order_items.order_item_type = 'line_item'
AND order_item_meta.meta_key = '_line_total'
AND order_item_meta_2.meta_key = '_product_id'
GROUP BY order_item_meta_2.meta_value
");
$results = apply_filters( 'woocommerce_reports_top_earners_order_items', $query );
foreach ( $results as $values ) {
if ( in_array( $values->product_id, $products ) ) {
$total_pr += $values->line_total;
}
}
echo 'customer ID: ' . $customer->ID . ' | Total PR: ' . number_format( $total_pr, 2 ) . '<br>';
}
Tested and works. It will output the list with the Customer IDs and the corresponding Total PR.
References:
WordPress Code Reference - get_users()
php variable in sql query
$current_user = wp_get_current_user();
$current_user_id = $current_user->ID;
$args = array(
'customer_id' => $current_user_id
);
$orders = wc_get_orders($args);
You Can Use wp_get_current_user(); for Get Current User And Get Order Data With Args May be It's Work For You.$current_user_id = $current_user->ID;echo $current_user_id;//check current user id get or notecho $current_user_id;//check current user id get or not

Categories