I am trying to produce a plain output of order data. First step is a WP_QUery (perhaps) so I write this code;
$args = array (
'post_type' =>'shop_order',
'posts_per_page' => -1,
'post_status' => 'any',
//'p' => $post_id,
);
$order_query = new WP_Query( $args );
while ( $order_query->have_posts() ) :
$order_query->the_post();
echo the_ID();
echo ' : ';
the_title();
echo '<br/><br/>';
endwhile;
It obliging products a list of all orders, if I set the 'p' => $post_id where $post_id is a valid post ID, the query returns nothing.
Any idea why, hive mind?
Alternatively is there a Woocommerce way of producing a plain page with a layout like;
Order ID: 836
Order Status: ....
I assumed a WP_Query would be the obvious way but it is appearing like getting woocommerce order data is anything but straightforward.
Update 2
To get the order data for one order, you don't need WP_query. You can use directly:
$order = wc_get_order( $order_id );
$order->id; // order ID
$order->post_title; // order Title
$order->post_status; // order Status
// getting order items
foreach($order->get_items() as $item_id => $item_values){
// Getting the product ID
$product_id = $item_values['product_id'];
// .../...
}
Update 1
You should try this, as with array_keys( wc_get_order_statuses() you will get all order statuses and with 'numberposts' => -1, all existing orders.
Here is an alternative way (without WP_query or you can use thoses args in the WP_query array):
$customer_orders = get_posts( array(
'numberposts' => -1,
'post_type' => 'shop_order',
'post_status' => array_keys( wc_get_order_statuses() )
) );
// Going through each current customer orders
foreach ( $customer_orders as $customer_order ) {
// Getting Order ID, title and status
$order_id = $customer_order->ID;
$order_title = $customer_order->post_title;
$order_status = $customer_order->post_status;
// Displaying Order ID, title and status
echo '<p>Order ID : ' . $order_id . '<br>';
echo 'Order title: ' . $order_title . '<br>';
echo 'Order status: ' . $order_status . '<br>';
// Getting an instance of the order object
$order = wc_get_order( $order_id );
// Going through each current customer order items
foreach($order->get_items() as $item_id => $item_values){
// Getting the product ID
$product_id = $item_values['product_id'];
// displaying the product ID
echo '<p>Product ID: '.$product_id.'</p>';
}
}
The strict answer to this question is change 'p'=>$post_id' to 'post__in' => array($post_id)
...but the real answer, should anyone be treading this path, is that parsing the email is so much easier, woocommerce has done most of the work for you. There are other pratfalls along the way; 1. The post is protected and the order notes are in the excerpt so can't be displayed ( I could move them to meta but...) 2. The order items are within comments and have to be looped then parsed to produce meaningful output, so I might as well parse the email direct.
Related
I have been playing around with the following code in the hope that I will be able to create a button - press it - and then all the descriptions on my website will auto generate. The dream.
My question: Why is my code not updating ALL of the products as intended?
So far I have the following code, which although it works to an extent, doesn't work correctly when the button is located on a product page - It will update that product pages description along with all empty description instances for product with an ID greater than the current product, but doesn't include anything below?!
I have been using the following post to try and help me get to an answer: Woocommerce: function to update all products however I have so far been unsuccessful!
My current code:
// EDIT ALL PRODUCTS //
add_action( 'woocommerce_admin_process_product_object', 'update_products_by_x' );
function update_products_by_x(){
if(isset($_POST['button_all_descriptions']) && $_POST['button_all_descriptions'] == 'All_Descriptions'){
$product_all = get_posts( array(
'post_type' => 'product','product_variation',
'post_status' => 'publish','future','draft','pending','private','trash','auto-draft','inherit',
'fields' => 'ids'
) );
// Loop through product Ids
foreach ( $product_all as $product_id ) {
// Get the WC_Product object
$WCproduct = wc_get_product($product_id);
if($WCproduct->is_type('simple') or $WCproduct->is_type('variable')){
$title = $WCproduct->get_name();
$mpn = $WCproduct->get_meta('sp_wc_barcode_field');
$description = $WCproduct->get_description();
$output = '';
if(!empty($title)){
$output .= "<p>A " . $title;
}if(!empty($mpn)){
$output .= " (MPN: " . $mpn . ").";
}if(empty($description)){
$WCproduct->set_description($output);
$WCproduct->save();
}
}
}
}
}
You might have a default limit of results restricting number of products your get_posts call returns
$product_all = get_posts( array(
'post_type' => 'product','product_variation',
'post_status' => 'publish','future','draft','pending','private','trash','auto-draft','inherit',
'fields' => 'ids',
'numberposts' => -1
) );
I would like to ask how is it possible when i have 3000 variable products in an old woocommerce version eshop, when i am trying to loop throught them that i get only 300 in return instead.
The following code is used by me in order to generate a product feed.
$args = array( 'post_type' => 'product');
$loop = new WP_Query( $args );
while ( $loop->have_posts() ) : $loop->the_post();
Also the variable products are called variable because they support variation as I understand. That means that 3000 variable products are not 3000 "normal" products and instead the 300 that I see is the correct number of them?
May be you are mixing up products (simple or variable) and product variations (that remain to variable products)... You need to display products (simple and variable) in your WP_Query, but not your product variations.
With 'post_type' => 'product', you will get simple and variable products at the same time.
The missing argument is posts_per_page to be set to -1.
So your code will be:
$args = array( 'post_type' => 'product', 'posts_per_page' => -1);
$loop = new WP_Query( $args );
while ( $loop->have_posts() ) :
$loop->the_post();
// set all product IDs in an array
$all_product_ids[] = $loop->post->ID;
endwhile;
// Testing: Output the number of products in that query
echo '<p>Products count: ' . count($all_product_ids) . '</p>';
Then you will get all your products.
May be you can try to use a WPDB query:
## --- THE SQL QUERY --- ##
global $wpdb;
$table_name = $wpdb->prefix . "posts";
$product_ids_array = $wpdb->get_col("
SELECT `ID`
FROM `$table_name`
WHERE `post_status` LIKE 'publish'
AND `post_type` LIKE 'product'
");
## --- TESTING OUTPUT --- ##
// Testing raw output of product IDs
print_r($product_ids_arr);
// Number of products
$products_count = count($product_ids_arr);
echo '<p>Products count: '. $products_count. '</p>';
## --- THE LOOP --- ##
foreach ($product_ids_array as $product_id):
// Get an instance of WP_Product object
$product = new WC_Product($product_id);
// Use the WP_Product methods to get and output the necessary data
endforeach;
So I have done a bunch of looking around the web and couldn't find a solution for this...
Basically what I am trying to do is display a product loop of all the products the user has purchased in the store just like displaying normal products.
If you still don't understand maybe this will help you get what I mean..
Here is the example product loop on the WooCommerce documentation...
<ul class="products">
<?php
$args = array(
'post_type' => 'product',
'posts_per_page' => 12
);
$loop = new WP_Query( $args );
if ( $loop->have_posts() ) {
while ( $loop->have_posts() ) : $loop->the_post();
woocommerce_get_template_part( 'content', 'product' );
endwhile;
} else {
echo __( 'No products found' );
}
wp_reset_postdata();
?>
</ul><!--/.products-->
So what if I wanted to display basically this same exact product loop however filter it out so that it only displays products that the user has already purchased.
I honestly do not know where to go with this one and I am sure there are others that have done research on this in the past so maybe this will help out a bunch of people!
Thanks in advance!
There are at least two different approaches you can take to solve this problem.
The first is to get the product from each post, and then get the product ID from each product and then use an if statement to filter using wc_customer_bought_product or woocommerce_customer_bought_product (if you are using old WooCommerece).
The second is to pass the correct arguments to filter the WP_Query to only include orders purchased by a user and then filter products only in those orders. More information on the second approach is available at Get All User Orders and Products bought by user in WooCommerce based shop (archive.org).
An example of the first approach is something like
<!-- code started -->
<ul class="products">
<?php
$user_id = get_current_user_id();
$current_user= wp_get_current_user();
$customer_email = $current_user->email;
$args = array(
'post_type' => 'product',
'posts_per_page' => 12
);
$loop = new WP_Query( $args );
if ( $loop->have_posts() ) {
while ( $loop->have_posts() ) : $loop->the_post(); $_product = get_product( $loop->post->ID );
if (wc_customer_bought_product($customer_email, $user_id,$_product->id)){
woocommerce_get_template_part( 'content', 'product' );
}
endwhile;
} else {
echo __( 'No products found' );
}
wp_reset_postdata();
?>
</ul><!--/.products-->
Kudos to Appleman1234 for providing two answers, both of which will work.
ApppleMan1234's first answer that he provided an example for is to loop through all products and then filter them by calling wc_customer_bought_product(). This certainly will work. If you have n products then you are going to make n+1 database queries.
His second suggestion is a link to a post written by Brajesh Singh who, on June 2, 2013, published a solution on fusedpress.com. The original post is no longer available. I found a cached copy at Google.
Brajesh Singh's solution queries the user's orders, then queries the order details, and last queries the product id in the order item's metadata. This solution then is always only 3 queries. Unless your shop only has 1 or 2 products, this solution is far better.
Here is a slightly edited version of Brajesh Singh's code.
/**
* Get all Products Successfully Ordered by the user
* #return bool|array false if no products otherwise array of product ids
*/
function so28362162_get_all_products_ordered_by_user() {
$orders = so28362162_get_all_user_orders(get_current_user_id(), 'completed');
if(empty($orders)) {
return false;
}
$order_list = '(' . join(',', $orders) . ')';//let us make a list for query
//so, we have all the orders made by this user that were completed.
//we need to find the products in these orders and make sure they are downloadable.
global $wpdb;
$query_select_order_items = "SELECT order_item_id as id FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id IN {$order_list}";
$query_select_product_ids = "SELECT meta_value as product_id FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE meta_key=%s AND order_item_id IN ($query_select_order_items)";
$products = $wpdb->get_col($wpdb->prepare($query_select_product_ids, '_product_id'));
return $products;
}
/**
* Returns all the orders made by the user
* #param int $user_id
* #param string $status (completed|processing|canceled|on-hold etc)
* #return array of order ids
*/
function so28362162_get_all_user_orders($user_id, $status = 'completed') {
if(!$user_id) {
return false;
}
$args = array(
'numberposts' => -1,
'meta_key' => '_customer_user',
'meta_value' => $user_id,
'post_type' => 'shop_order',
'post_status' => 'publish',
'tax_query' => array(
array(
'taxonomy' => 'shop_order_status',
'field' => 'slug',
'terms' => $status
)
)
);
$posts = get_posts($args);
//get the post ids as order ids
return wp_list_pluck($posts, 'ID');
}
Combining that with a product loop from the question, plus a non-deprecated wc_get_template_part() and an addition of posts_per_page=-1 gives us
<ul class="products">
<?php
$args = array(
'post_type' => 'product',
'post__in' => so28362162_get_all_products_ordered_by_user(),
'posts_per_page' => -1
);
$loop = new WP_Query($args);
if($loop->have_posts()) {
while($loop->have_posts()) : $loop->the_post();
wc_get_template_part('content', 'product');
endwhile;
}
else {
echo __('No products found');
}
wp_reset_postdata();
?>
</ul><!--/.products-->
Not sure if this helps you out at all, but there is a plugin developed by WooThemes to support purchase history.
I'm using WooCommerce 2.0 and I would like to retrieve and display the ordered product name in the myaccount.php page, alongside the order number.
So if this is the default display in myaccount.php:
ORDER DATE STATUS TOTAL ACTIONS LICENSING
#521 August 19, 2014 Completed $99.99 for 1 item VIEW
I'd like to change it to:
ORDER DATE STATUS TOTAL ACTIONS LICENSING
#521-ProductName August 19, 2014 Completed $99.99 for 1 item VIEW
Can anyone offer any suggestions on how to retrieve the order product name? I am confused on how to do this.
Thanks!
You can do it after making some changes in your my-orders.php
you need my-orders.php. Place a copy in your theme folder to make it update proof.
//Add the following code in the customer_order loop
foreach($order->get_items() as $item) {
$product_name = $item['name'];
}
<?php echo $product_name;?> //echo product name
$args = array(
'post_type' => 'shop_order',
'post_status' => 'publish',
'meta_key' => '_customer_user',
'posts_per_page' => '-1'
);
$my_query = new WP_Query($args);
$customer_orders = $my_query->posts;
foreach ($customer_orders as $customer_order) {
$order = new WC_Order();
$order->populate($customer_order);
$orderdata = (array) $order;
// $orderdata Array will have Information. for e.g Shippin firstname, Lastname, Address ... and MUCH more.... Just enjoy!
}
And in order to get more details of the order you can use the below code.
Assuming $post->ID is the id of the product you want to display the orders containing, this is what you need:
$products = array();
foreach (get_posts('post_type=shop_order&numberposts=-1&post_status=publish') as $order) {
$order = new WC_Order($order->ID);
foreach($order->get_items('line_item') as $item) {
$product_id = (!empty($item['variation_id'])) ? $item['variation_id'] : $item['product_id'];
$products[] = $product_id;
}
if (in_array($post->ID,$products)) {
echo 'Status: '.$order->order_status;
echo '<br>Date : '.$order->order_date;
echo '<br>Email : '.$order->billing_email;
}
}
I am creating a custom theme for woocommerce and I need to be able to create a mini product display. I am having problems finding documentation on the woocommerce api. I have a comma delimited list of product IDs that I need to iterate through and display a custom mini product display for each in sequence.
$key_values = get_post_custom_values('rel_products_ids');
//get comma delimited list from product
$rel_product_ids = explode(",", trim($key_values, ","));
// create array of just the product ids
foreach ( $rel_product_ids as $pid ) {
//sequentially get each id and do something with it
$loop = new WP_Query( array( 'post__in' => $pid ) );
// also tried ...
//$loop = new WP_Query( array( 'ID' => $pid ) );
while ( $loop->have_posts() ) : $loop->the_post(); $_product = &new WC_Product( $loop->post->ID );
//do stuff here I have stripped the html in favor of getting to the meat of the issue
woocommerce_show_product_sale_flash( $post, $_product );
if (has_post_thumbnail( $loop->post->ID )) echo get_the_post_thumbnail($loop->post->ID, 'shop_single');
get_permalink( $loop->post->ID );
the_title();
$_product->get_price_html();
endwhile;
}
Any help would be appreciated.
Thank you,
Tim
Use this method:
$_product = wc_get_product( $id );
Official API-docs: wc_get_product
Another easy way is to use the WC_Product_Factory class and then call function get_product(ID)
http://docs.woothemes.com/wc-apidocs/source-class-WC_Product_Factory.html#16-63
sample:
// assuming the list of product IDs is are stored in an array called IDs;
$_pf = new WC_Product_Factory();
foreach ($IDs as $id) {
$_product = $_pf->get_product($id);
// from here $_product will be a fully functional WC Product object,
// you can use all functions as listed in their api
}
You can then use all the function calls as listed in their api:
http://docs.woothemes.com/wc-apidocs/class-WC_Product.html
Alright, I deserve to be throttled. definitely an RTM but not for WooCommerce, for Wordpress.
Solution found due to a JOLT cola (all hail JOLT cola).
TASK:
Field named 'related_product_ids' added to a custom post type. So when that post is displayed mini product displays can be displayed with it.
PROBLEM:
Was having a problem getting the multiple ids returned via WP_Query.
SOLUTION:
$related_id_list = get_post_custom_values('related_product_ids');
// Get comma delimited list from current post
$related_product_ids = explode(",", trim($related_id_list[0],','));
// Return an array of the IDs ensure no empty array elements from extra commas
$related_product_post_ids = array( 'post_type' => 'product',
'post__in' => $related_product_ids,
'meta_query'=> array(
array( 'key' => '_visibility',
'value' => array('catalog', 'visible'),'compare' => 'IN'
)
)
);
// Query to get all product posts matching given IDs provided it is a published post
$loop = new WP_Query( $related_posts );
// Execute query
while ( $loop->have_posts() ) : $loop->the_post(); $_product = get_product( $loop->post->ID );
// Do stuff here to display your products
endwhile;
Thank you for anyone who may have spent some time on this.
Tim
global $woocommerce;
var_dump($woocommerce->customer->get_country());
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$product = new WC_product($cart_item['product_id']);
var_dump($product);
}