I have this table (wp_woocommerce_order_itemmeta):
And this table (wp_woocommerce_order_items):
I'm using a function (functions.php) which returns these item id's in an array:
function retrieve_orders_ids_from_a_product_id( $product_id, $order_date ) {
global $wpdb;
$table_posts = $wpdb->prefix . "posts";
$table_items = $wpdb->prefix . "woocommerce_order_items";
$table_itemmeta = $wpdb->prefix . "woocommerce_order_itemmeta";
// Define HERE the orders status to include in <== <== <== <== <== <== <==
$orders_statuses = "'wc-processing'";
# Requesting All defined statuses Orders IDs for a defined product ID
$orders_ids = $wpdb->get_col( "
SELECT DISTINCT $table_items.order_id
FROM $table_itemmeta, $table_items, $table_posts
WHERE $table_items.order_item_id = $table_itemmeta.order_item_id
AND $table_items.order_id = $table_posts.ID
AND $table_posts.post_status IN ( $orders_statuses )
AND $table_itemmeta.meta_key LIKE '_product_id'
AND $table_itemmeta.meta_value LIKE '$product_id'
AND $table_itemmeta.meta_key LIKE 'order_date'
AND $table_itemmeta.meta_value LIKE '$order_date'
ORDER BY $table_items.order_item_id DESC"
);
// return an array of Orders IDs for the given product ID
return $orders_ids;
}
And I call it inside my page:
<?php $orders_ids_array = retrieve_orders_ids_from_a_product_id($getid, $order_date); ?>
I'm 100% sure that $getid ($getid = product_id) and $order_date has the exact value which I from my db.
But if I print_r the array it returns with:
Array ( )
instead of any value.
Finally I need to count these id-s, but if doesn't return anything it looks impossible to me.
It looks like
AND $table_itemmeta.meta_value LIKE 'order_date'
should be
AND $table_itemmeta.meta_key LIKE 'order_date'
Okay, it has 2 problems. First:
AND $table_itemmeta.meta_value LIKE 'order_date'
should be
AND $table_itemmeta.meta_key LIKE 'order_date'
and the second is the $order_date is always refreshed because my foreach() loop.
Related
I want to show the last 10 products a user has ordered in the past.
The problem is, that I don't know how many products where in the last order.
So I maybe need to get more than one order.
I want to display everything in the cart. To let users buy products again.
Later I want to exclude procuts which are in the current cart.
For the last order I found a snippet here: https://stackoverflow.com/a/71501798/1788961
// For logged in users only
if ( is_user_logged_in() ) {
// The current user ID
$user_id = get_current_user_id();
// Get the last WC_Order Object instance from current customer
$last_order = wc_get_customer_last_order( $user_id );
// NOT empty
if ( ! empty( $last_order ) ) {
// Initalize
$product_ids = array();
// Loop
foreach ( $last_order->get_items() as $item ) {
// Get product ID
$product_id = $item->get_product_id();
$product_ids[] = $product_id;
}
echo '<pre>';
var_dump( $product_ids );
echo '</pre>';
}
}
Is there a way to extend the function to more orders?
You can indeed use wc_get_orders(), where you will retrieve the orders based on arguments, such as the user ID and order by date.
Also note that a limit of 10 is used. This is because we can assume that every order contains at least 1 product. So if you want to collect 10 products the limit will never be more than 10 orders:
// Args
$args = array(
'customer_id' => get_current_user_id(),
'limit' => 10,
'orderby' => 'date',
'order' => 'DESC',
'status' => array( 'wc-on-hold','wc-processing','wc-completed' ),
);
// Get orders
$orders = wc_get_orders( $args );
// NOT empty
if ( ! empty ( $orders ) ) {
// Initalize
$product_ids = array();
foreach ( $orders as $order ) {
// Loop through order items
foreach ( $order->get_items() as $item ) {
// Get product ID
$product_id = $item->get_product_id();
// Limit of 10 products
if ( count( $product_ids ) < 10 ) {
// Push to array
$product_ids[] = $product_id;
} else {
break;
}
}
}
// The output
echo '<pre>', print_r( $product_ids, 1 ), '</pre>';
}
A much lighter and faster solution is to use a custom SQL query:
global $wpdb;
$current_user = wp_get_current_user();
$customer_email = $current_user->user_email;
$result = $wpdb->get_col( "
SELECT oim.meta_value FROM {$wpdb->prefix}posts AS p
INNER JOIN {$wpdb->prefix}postmeta AS pm ON p.ID = pm.post_id
INNER JOIN {$wpdb->prefix}woocommerce_order_items AS oi ON p.ID = oi.order_id
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS oim ON oi.order_item_id = oim.order_item_id
WHERE p.post_status IN ( 'wc-on-hold','wc-processing','wc-completed' )
AND pm.meta_key = '_billing_email'
AND pm.meta_value = '$customer_email'
AND oim.meta_key = '_product_id'
AND oim.meta_value != 0
ORDER BY p.post_date DESC LIMIT 0, 10
" );
// The raw output
echo '<pre>', print_r( $result, 1 ), '</pre>';
Whichever option you prefer. These can in any case be expanded with, for example, the variants, of variable products or on the basis of multiple order statuses. Excluding duplicate products, etc.. So it depends on your needs
I am using this to get the data from the latest completed order:
<?php
function get_last_order_id(){
global $wpdb;
$statuses = "wc-completed";
// Getting last Order ID (max value)
$results = $wpdb->get_col( "
SELECT MAX(ID) FROM {$wpdb->prefix}posts
WHERE post_type LIKE 'shop_order'
AND post_status IN ('$statuses')
" );
return reset($results);
}
$latest_order_id = get_last_order_id(); // Last order ID
echo ($latest_order_id);
$order = wc_get_order( $latest_order_id );
$order_data = $order->get_data();
print_r($order);
However, the data that I need to get is showing as this:
[data:protected] => Array ( [id] => 3767 [key] => billing_name [value] => David) )
I have searched the questions on the StackOverflow on this but unfortunately, I still can't access this. Does anyone have an idea of how I can get it?
Thanks in Advance
<?php
function get_last_order_id(){
global $wpdb;
$statuses = "wc-completed";
// Getting last Order ID (max value)
$results = $wpdb->get_row( "SELECT MAX(P.ID) ,PM.meta_value as firstname
FROM {$wpdb->prefix}posts AS P
INNER JOIN {$wpdb->prefix}postmeta AS PM
ON P.ID = PM.post_id
WHERE P.post_type LIKE 'shop_order'
AND P.post_status IN ('$statuses')
AND PM.meta_key='_billing_first_name'" );
return $results;
}
$latest_order_id = get_last_order_id(); // Last order ID
$firstname = $latest_order_id->firstname;
echo $firstname;
I have the products ids as an array and I would like to get the list of order ids if the customer has purchased that product.
I have the customer purchased product ids with me. Somehow, I have to get the linked order id and cancel that order if customer purchases new product.
To check if a customer has purchased a product I am using the function has_bought_items() from this answer thread: Check if a customer has purchased a specific products in WooCommerce
May be it can be tweaked to get the desired output?
The following custom function made with a very light unique SQL query, will get all the Orders IDs from an array of products IDs (or a unique product ID) for a given customer.
Based on code from: Check if a customer has purchased a specific products in WooCommerce
function get_order_ids_from_bought_items( $product_ids = 0, $customer_id = 0 ) {
global $wpdb;
$customer_id = $customer_id == 0 || $customer_id == '' ? get_current_user_id() : $customer_id;
$statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() );
if ( is_array( $product_ids ) )
$product_ids = implode(',', $product_ids);
if ( $product_ids != ( 0 || '' ) )
$meta_query_line = "AND woim.meta_value IN ($product_ids)";
else
$meta_query_line = "AND woim.meta_value != 0";
// Get Orders IDs
$results = $wpdb->get_col( "
SELECT DISTINCT p.ID FROM {$wpdb->prefix}posts AS p
INNER JOIN {$wpdb->prefix}postmeta AS pm ON p.ID = pm.post_id
INNER JOIN {$wpdb->prefix}woocommerce_order_items AS woi ON p.ID = woi.order_id
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS woim ON woi.order_item_id = woim.order_item_id
WHERE p.post_status IN ( 'wc-" . implode( "','wc-", $statuses ) . "' )
AND pm.meta_key = '_customer_user'
AND pm.meta_value = $customer_id
AND woim.meta_key IN ( '_product_id', '_variation_id' )
$meta_query_line
" );
// Return an array of Order IDs or an empty array
return sizeof($results) > 0 ? $results : array();
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
USAGE Examples:
1) For the current logged in customer (and 2 product Ids in an array):
$product_ids = array(37,53);
$order_ids = get_order_ids_from_bought_items( $product_ids );
2) For a defined User ID and one product ID:
$product_id = 53;
$user_id = 72;
$order_ids = get_order_ids_from_bought_items( $product_id, $user_id );
In WooCommerce, I would like to:
Get the unique values for a certain product attribute (From all my products), assuming all products have that particular attribute set as something.
Get the list of unique attribute values with the highest product price.
For Example:
Product 1:
Product-Attribute = Green
Price = $100
Product 2:
Product-Attribute = Red
Price = $50
Product 3:
Product-Attribute = Green
Price = $50
Expected result (an array):
Green : $100
Red : $50
Any ideas on how to code this so that it returns an array?
For Variable products and its variation attributes name and term value name with the highest variation price in an array, you will try the following custom function:
function get_variations_attributes_values_highest_price(){
global $wpdb;
// SQL query
$all_attributes = $wpdb->get_results("SELECT * FROM {$wpdb->prefix}woocommerce_attribute_taxonomies");
$product_attributes = array();
foreach( $all_attributes as $value ){
$product_attributes = 'pa_'.$value->attribute_name;
}
//$pa_taxonomies = "'".implode("','", $product_attributes)."'";
// The 2nd SQL query
$query = $wpdb->get_results( "
SELECT p.ID, pm.meta_key as attr, pm.meta_value as term, pm2.meta_value as price
FROM {$wpdb->prefix}postmeta as pm
INNER JOIN {$wpdb->prefix}posts as p ON pm.post_id = p.ID
INNER JOIN {$wpdb->prefix}postmeta as pm2 ON pm.post_id = pm2.post_id
WHERE p.post_type IN ('product_variation','product')
AND p.post_status LIKE 'publish'
AND pm.meta_key LIKE 'attribute_pa_%'
AND pm2.meta_key LIKE '_price'
AND pm2.meta_value != ''
ORDER BY pm.meta_key ASC, pm.meta_value ASC, CAST(replace(pm2.meta_value, 'MDT ', '') AS UNSIGNED) ASC
" );
//print_pr($query);
$ordered_results = $results = array();
// Loop 1: Filtering and removing duplicate terms keeping highest price
foreach( $query as $values ){
if( !empty($values->price)){
$filter_key = $values->attr .' '.$values->term;
$ordered_results[$filter_key] = (object) array( 'attr' => $values->attr,
'term' => $values->term, 'price' => $values->price );
}
}
// Loop 2: Get the real name values, formatting data
foreach( $ordered_results as $result ){
$taxonomy = str_replace('attribute_' , '', $result->attr );
$attr_name = get_taxonomy( $taxonomy )->labels->singular_name; // Attribute name
$value_name = get_term_by( 'slug', $result->term, $taxonomy )->name; // Attribute value term name
$results[$attr_name.' - '.$value_name] = wc_price($result->price);
}
return $results;
}
Code goes in function.php file of the active child theme (or active theme).
Tested and works.
## --- USAGE --- ##
// RAW ARRAY OUTPUT
echo '<pre>'; print_r(get_variations_attributes_values_highest_price()); echo '</pre>';
You can make little changes easily to match your needs (the honey for TheBear)…
How can I get an array with Order IDs by Product ID?
I mean receive all orders where specific product is presented.
I know how to do this by MySQL, but is there a way to do this by WP_Query function?
Updates:
2017 - SQL query changed to "SELECT DISTINCT" instead of "SELECT" to avoid duplicated Order IDs in the array (then no need of array_unique() to filter duplicates…).
2019 - Enabled product variation type support in the SQL Query
Then you can embed this in a custom function with $product_id as argument. You will have to set inside it, the order statuses that you are targeting.
So here is the function that will do the job:
function get_orders_ids_by_product_id( $product_id ) {
global $wpdb;
// Define HERE the orders status to include in <== <== <== <== <== <== <==
$orders_statuses = "'wc-completed', 'wc-processing', 'wc-on-hold'";
# Get All defined statuses Orders IDs for a defined product ID (or variation ID)
return $wpdb->get_col( "
SELECT DISTINCT woi.order_id
FROM {$wpdb->prefix}woocommerce_order_itemmeta as woim,
{$wpdb->prefix}woocommerce_order_items as woi,
{$wpdb->prefix}posts as p
WHERE woi.order_item_id = woim.order_item_id
AND woi.order_id = p.ID
AND p.post_status IN ( $orders_statuses )
AND woim.meta_key IN ( '_product_id', '_variation_id' )
AND woim.meta_value LIKE '$product_id'
ORDER BY woi.order_item_id DESC"
);
}
This code goes in any php file.
This code is tested and works for WooCommerce version 2.5+, 2.6+ and 3+
USAGE EXAMPLES:
## This will display all orders containing this product ID in a coma separated string ##
// A defined product ID: 40
$product_id = 40;
// We get all the Orders for the given product ID in an arrray
$orders_ids_array = get_orders_ids_by_product_id( $product_id );
// We display the orders in a coma separated list
echo '<p>' . implode( ', ', $orders_ids_array ) . '</p>';
If you want your code to work in future WC updates, it is better to use functions provided by WC to get details from the DB, since WC often change the DB structure.
I'd try something like:
function get_orders_id_from_product_id($product_id, $args = array() ) {
//first get all the order ids
$query = new WC_Order_Query( $args );
$order_ids = $query->get_orders();
//iterate through order
$filtered_order_ids = array();
foreach ($order_ids as $order_id) {
$order = wc_get_order($order_id);
$order_items = $order->get_items();
//iterate through an order's items
foreach ($order_items as $item) {
//if one item has the product id, add it to the array and exit the loop
if ($item->get_product_id() == $product_id) {
array_push($filtered_order_ids, $order_id);
break;
}
}
}
return $filtered_order_ids;
}
Usage example:
$product_id = '2094';
// NOTE: With 'limit' => 10 you only search in the last 10 orders
$args = array(
'limit' => 10,
'orderby' => 'date',
'order' => 'DESC',
'return' => 'ids',
);
$filtered_order_ids = get_orders_id_from_product_id($product_id, $args);
print_r($filtered_order_ids);
I'd like to note that the above answer will return a duplicate of the order_id if the order has multiple items in it.
E.g.
If there was a product called "apples" with product_id=>1036
Customer puts "apples" 3 times in their cart and purchases it, creating order_id=>555
If I query product_id->1036, I will get array(555,555,555).
There's probably an SQL way of doing this which may be faster, (would appreciate anyone that could add to this), otherwise I used: array_unqiue() to merge the duplicates.
function retrieve_orders_ids_from_a_product_id( $product_id )
{
global $wpdb;
$table_posts = $wpdb->prefix . "posts";
$table_items = $wpdb->prefix . "woocommerce_order_items";
$table_itemmeta = $wpdb->prefix . "woocommerce_order_itemmeta";
// Define HERE the orders status to include in <== <== <== <== <== <== <==
$orders_statuses = "'wc-completed', 'wc-processing', 'wc-on-hold'";
# Requesting All defined statuses Orders IDs for a defined product ID
$orders_ids = $wpdb->get_col( "
SELECT $table_items.order_id
FROM $table_itemmeta, $table_items, $table_posts
WHERE $table_items.order_item_id = $table_itemmeta.order_item_id
AND $table_items.order_id = $table_posts.ID
AND $table_posts.post_status IN ( $orders_statuses )
AND $table_itemmeta.meta_key LIKE '_product_id'
AND $table_itemmeta.meta_value LIKE '$product_id'
ORDER BY $table_items.order_item_id DESC"
);
// return an array of Orders IDs for the given product ID
$orders_ids = array_unique($orders_ids);
return $orders_ids;
}
Modified function to get specific user product ids
function retrieve_orders_ids_from_a_product_id( $product_id,$user_id )
{
global $wpdb;
$table_posts = $wpdb->prefix . "posts";
$table_postmeta = $wpdb->prefix . "postmeta";
$table_items = $wpdb->prefix . "woocommerce_order_items";
$table_itemmeta = $wpdb->prefix . "woocommerce_order_itemmeta";
// Define HERE the orders status to include in <== <== <== <== <== <== <==
$orders_statuses = "'wc-completed', 'wc-processing', 'wc-on-hold'";
# Requesting All defined statuses Orders IDs for a defined product ID
$orders_ids = $wpdb->get_col( "
SELECT DISTINCT $table_items.order_id
FROM $table_itemmeta, $table_items, $table_posts , $table_postmeta
WHERE $table_items.order_item_id = $table_itemmeta.order_item_id
AND $table_items.order_id = $table_posts.ID
AND $table_posts.post_status IN ( $orders_statuses )
AND $table_postmeta.meta_key LIKE '_customer_user'
AND $table_postmeta.meta_value LIKE '$user_id '
AND $table_itemmeta.meta_key LIKE '_product_id'
AND $table_itemmeta.meta_value LIKE '$product_id'
ORDER BY $table_items.order_item_id DESC"
);
// return an array of Orders IDs for the given product ID
return $orders_ids;
}
Usage Example
## This will display all orders containing this product ID in a coma separated string ##
// A defined product ID: 40
$product_id = 40;
// Current User
$current_user = wp_get_current_user();
// We get all the Orders for the given product ID of current user in an arrray
$orders_ids_array = retrieve_orders_ids_from_a_product_id( $product_id, $current_user->ID );
// We display the orders in a coma separated list
echo '<p>' . implode( ', ', $orders_ids_array ) . '</p>';