Extending search in WooCommerce admin orders list - wordpress

I'm looking for a way to extend the search field in WooCommerce admin orders list for a custom meta key. Currently i'm using the woocommerce_shop_order_search_fields filter hook.
Resulting in this code, which allows me to search by the user id, order total and order number.
add_filter( 'woocommerce_shop_order_search_fields', 'woocommerce_shop_order_search_order_total' );
function woocommerce_shop_order_search_order_total( $search_fields ) {
$search_fields[] = '_order_total';
$search_fields[] = '_user_id';
$search_fields[] = '_order_number';
return $search_fields;
}
However, these are all existing meta keys, what if I want to search for meta data that doesn't yet exist? Any adivce?

Expanding the search in WooCommerce admin orders list can be done very easily by using the woocommerce_shop_order_search_fields filter hook, you just need to add some post_meta fields for order (item(s)). As long as this data exists of course!
By default, the following metakeys are present:
_billing_address_index
_shipping_address_index
_billing_last_name
_billing_email
So, for example, if you want to extend the search by the billing first name, you can do this by simply adding the metakey _billing_first_name, and then you get:
function filter_woocommerce_shop_order_search_fields( $search_fields ) {
// Metakey
$search_fields[] = '_billing_first_name';
return $search_fields;
}
add_filter( 'woocommerce_shop_order_search_fields', 'filter_woocommerce_shop_order_search_fields', 10, 1 );
Now, what if this metadata is not present for the orders? You could add that specific metadata for future orders, when the order is created. But wait! what about the existing orders? for these orders this data would not exist!
Wouldn't it be useful if we could add this data to existing orders, or even newer orders. Well this is possible, namely by using wc_get_orders(), to get (and update) the existing orders before running the search.
So you get: (where we in this example add the user's nickname as meta data to orders)
function filter_woocommerce_shop_order_search_fields( $search_fields ) {
// The desired meta key
$meta_key = '_user_nickname';
// Get ALL orders where a certain meta key not exists
$orders = wc_get_orders( array(
'limit' => -1, // Query all orders
'meta_key' => $meta_key, // Post meta_key
'meta_compare' => 'NOT EXISTS', // Comparison argument
));
// NOT empty
if ( ! empty ( $orders ) ) {
// Loop through the orders
foreach ( $orders as $order ) {
// Get the desired information via the order object
// Get user
$user = $order->get_user();
// User is NOT empty
if ( ! empty ( $user ) ) {
// Get nickname from user
$meta_value = $user->nickname;
// Meta value is NOT empty
if ( ! empty ( $meta_value ) ) {
// Add the meta data
$order->update_meta_data( $meta_key, $meta_value );
$order->save();
}
}
}
}
// Metakey
$search_fields[] = $meta_key;
return $search_fields;
}
add_filter( 'woocommerce_shop_order_search_fields', 'filter_woocommerce_shop_order_search_fields', 10, 1 );

Related

How to display the last ordered product in WooCommerce via a shortcode

I'm looking for a way to display the last ordered product on another page.
I think it would be possible to maybe create a shortcode in the functions that takes the order details and displays them wherever I add the shortcode.
But I can't seem to figure out how to get it to work. So far I got this information to work with:
add_shortcode( 'displaylast', 'last' );
function last(){
$customer_id = get_current_user_id();
$order = wc_get_customer_last_order( $customer_id );
return $order->get_order();
}
[displaylast] is currently showing me noting. It does work when I change get_order() to get_billing_first_name().
That displays the order name. But I can't seem to get the item that was bought. Maybe there is a get_() that I'm not seeing?
You are close, however you must obtain the last product from the order object.
So you get:
function last() {
// Not available
$na = __( 'N/A', 'woocommerce' );
// For logged in users only
if ( ! is_user_logged_in() ) return $na;
// The current user ID
$user_id = get_current_user_id();
// Get the WC_Customer instance Object for the current user
$customer = new WC_Customer( $user_id );
// Get the last WC_Order Object instance from current customer
$last_order = $customer->get_last_order();
// When empty
if ( empty ( $last_order ) ) return $na;
// Get order items
$order_items = $last_order->get_items();
// Latest WC_Order_Item_Product Object instance
$last_item = end( $order_items );
// Get product ID
$product_id = $last_item->get_variation_id() > 0 ? $last_item->get_variation_id() : $last_item->get_product_id();
// Pass product ID to products shortcode
return do_shortcode("[product id='$product_id']");
}
// Register shortcode
add_shortcode( 'display_last', 'last' );
SHORTCODE USAGE
In an existing page:
[display_last]
Or in PHP:
echo do_shortcode('[display_last]');

WordPress - Gravity Forms - Create field dynamically

Trying to create new fields for my form dynamically, as I'm getting json from 3rd party API. Based on this json, I need a number of fields added to my form - not fixed number. So, I'm doing this, hooking it onto gform_pre_render:
add_filter( 'gform_pre_process', 'create_products_gforms' );
add_filter( 'gform_admin_pre_render', 'create_products_gforms' );
add_filter( 'gform_pre_render', 'create_products_gforms' );
function create_products_gforms( $form ) {
$helper = new NSHelper();
$state_name = $_POST['input_7'] ?? '';
$code_value = $helper->get_state_code_by_name( $state_name );
// Only fetch products if current form id is 33, state code is defined
// and if there are products for this state.
if ( $form['id'] != 33 || !$code_value ) {
return $form;
}
// Get product list from NetSuit API based on state code
$products_json_data = get_products_data( $code_value );
$products = json_decode( json_decode( $products_json_data ) );
$new_field_id = 0;
foreach( $form['fields'] as $field ) {
if( $field->id > $new_field_id ) {
$new_field_id = $field->id;
}
}
$new_field_id++;
foreach ( $products as $product_object ) {
// Prepare field properties
$props = array(
'id' => $new_field_id,
'type' => 'singleproduct',
'label' => $product_object->onlinedisplayname,
'basePrice' => floatval( $product_object->price ),
'enableCalculation' => true
);
// Create new gravity forms field and add it to the form object
$nf = GF_Fields::create( $props );
// Hack - insert into array at specific position
// Needed to display product fields before other fields
// in the form
array_splice( $form['fields'], 11, 0, array($nf) );
$new_field_id++;
}
GFAPI::update_form( $form );
$form['dynamic_fields_ids'] = $added_fields_ids;
return $form;
}
This works, ie, it shows fields properly on the frontend. Now, the issue with this is that, once form is submitted, all the fields except these dynamically added ones are in the submission. But these are not. I assumed this has to do that these fields aren't registered in the form, so I also tried GFAPI::update_form( $form );, but that didn't help with submission part, though it udpates my form with new fields in the backend too.
Any ideas?
Based on your use-case, Milos, I would suggest using the gform_form_post_get_meta filter:
https://docs.gravityforms.com/gform_form_post_get_meta/
This will fire every time the form is fetched from the database and the most sure-fire way of guaranteeing your fields will be present.
If you prefer to be more surgical and stick with the gform_pre_render approach, you'll want to apply the same function on a couple other filters:
gform_pre_process
gform_admin_pre_render

How to add a Woocommerce Subscription order note programmatically

With the below code, I can successfully add a custom order note to a subscription renewal order when the order is completed.
However, I would like to modify this to add the note to the Subscription Notes instead of the Order Notes. I think Subscription Notes use the same add_order_note function as $subscription->add_order_note instead of $order->add_order_note. But I have been unsuccessful in my attempts to get the $subscription variable to work in the below code.
// Add box contents (product excerpt) as order note for subscriptions
add_action( 'woocommerce_email_before_order_table', 'custom_action_on_completed_customer_email_notification', 10, 4 );
function custom_action_on_completed_customer_email_notification( $order, $sent_to_admin, $plain_text, $email ) {
if( 'customer_completed_renewal_order' == $email->id ){ // for processing order status customer notification…
$product_id = '';
foreach ($order->get_items() as $item_id => $item_values) {
$product_id = $item_values['product_id'];
break; // (optional) stop loop to first item
}
// The text for the note
$note = get_the_excerpt($product_id);
// Add the note
$order->add_order_note( $note, $is_customer_note = 1 );
// Save the data
$order->save();
}
}
Do you know what I need to add/change to make this add to a Subscription note instead of order note?

Woocommerce REST API v2: Issue with adding custom data to orders

I have a site running an old version of Woo v2.5.5 using legacy v3 for the API. I was able to use the action woocommerce_api_order_response to add data to the orders.
Something like:
add_action( 'woocommerce_api_order_response', 'add_testing_api_function', 10, 1 );
function add_testing_api_function( $order_data ) {
$order_data['foo'] = "testing";
return $order_data;
}
This works fine over the older API link:
https://example.com/wc-api/v3/orders?consumer_key=KEY&consumer_secret=SECRET
However, I need to update to Woo v3.3+ and the REST API is server up as:
https://example.com/wp-json/wc/v2/orders?consumer_key=KEY&consumer_secret=SECRET
My custom data no longer appears, and the hook does not appear to work. Is there another I can use?
WC API is indeed a great tool. Adding your own custom data to WC API shop order's response in WooCommerce 3.x can still be achieved as easily as it used to be with the legacy version of the API.
WooCommerce has these prepare filters for most of their API responses (see). Note that the format of them is woocommerce_rest_prepare_{$post_type}, where $post_type is a post type or taxonomy name like shop_orders or product_cat. In WooCommerce 2.7+ some of these filters also have a _object suffix.
As long as our intent is to add custom data to orders, the right filter to use will be woocommerce_rest_prepare_shop_order_object, where shop_order is our {$post_type} followed the _object suffix (as described above).
The following function conditionally gets user meta and returns the user's social profile's avatar url if available.
/**
* Add custom data to WC API shop order response
* Overriding "$object" here with $order so it's easier to access its properties
*/
function my_wc_rest_prepare_order( $response, $order, $request ) {
if( empty( $response->data ) )
return $response;
$order_id = $order->get_id();
// Get an instance of the WC_Order object
$order = wc_get_order($order_id);
// Get the user ID from WC_Order methods
$user_id = $order->get_customer_id(); // $order->get_user_id(); // or $order->get_customer_id();
// check for WooCommerce Social Login User Avatar
if( class_exists( 'WC_Social_Login' ) ) {
$fb_avatar = get_user_meta( $user_id, '_wc_social_login_facebook_profile_image', true );
$gplus_avatar = get_user_meta( $user_id, '_wc_social_login_google_profile_image', true );
}
$social_data = array();
$avatar_url = array();
$customer_picture = array(
'default' => get_avatar_url( $user_id ),
'facebook' => ( $fb_avatar ) ? esc_url( $fb_avatar ) : '',
'google' => ( $gplus_avatar ) ? esc_url( $gplus_avatar ) : ''
);
$response->data['social_data']['avatar_url'] = $customer_picture;
return $response;
}
add_filter( 'woocommerce_rest_prepare_shop_order_object', 'my_wc_rest_prepare_order', 10, 3 );
Result:
[{
"social_data": {
"avatar_url": {
"default": "https://secure.gravatar.com/avatar/6e27402273b47316097247a2057492f8?s=96&d=mm&r=g",
"facebook": "https://graph.facebook.com/2028542057604385/picture?width=150&height=150",
"google": ""
}
},
}]

WP REST API orderby meta_value

Need to be able to sort the results of a REST API custom post query by a meta value.
Having difficulty doing so.
I have made my post type available to the REST API and can order by the Date, Title, etc...
But when I try the Post Meta it doesn't work.
I have added the following code to try and enable the functionality but defaults to ordering by date.
function my_add_meta_vars ($current_vars) {
$current_vars = array_merge ($current_vars, array('meta_key', 'meta_value'));
return $current_vars;
}
add_filter ('query_vars', 'my_add_meta_vars');
add_filter ('rest_query_vars', 'my_add_meta_vars');
My REST API query is
mysite.com/wp-json/wp/v2/hh_equipment?filter[orderby]=meta_value_num&meta_key=equipment_price&order=desc
I have tried following the instructions here to no avail.
Running WordPress 4.8 and tried testing on 4.7 to no avail
I've got it working with the rest_' . $post_type . '_collection_params filter and rest_' . $post_type . '_query filter like so (change $post_type to needed post type slug):
// Add meta your meta field to the allowed values of the REST API orderby parameter
add_filter(
'rest_' . $post_type . '_collection_params',
function( $params ) {
$params['orderby']['enum'][] = 'YOUR_META_KEY';
return $params;
},
10,
1
);
// Manipulate query
add_filter(
'rest_' . $post_type . '_query',
function ( $args, $request ) {
$order_by = $request->get_param( 'orderby' );
if ( isset( $order_by ) && 'YOUR_META_KEY' === $order_by ) {
$args['meta_key'] = $order_by;
$args['orderby'] = 'meta_value'; // user 'meta_value_num' for numerical fields
}
return $args;
},
10,
2
);
The first filter adds your meta field to the possible values of the ordeby parameters, as by default REST API supports only: author, date, id, include, modified, parent, relevance, slug, include_slugs, title (check the ordeby param in the WP REST API handbook)
The second filter allows you to manipulate the query that returns the results when you have your meta key inside the orderby. Here we need to reset orderby to 'meta_value' or 'meta_value_num' (read more about this in WP Query class description) and set the meta key to your custom field key.
Refer below method,
I modified the existing routes to add a new args entry which validates the meta_key values which are permitted. No need to modify the rest query vars this way either.
add_filter('rest_endpoints', function ($routes) {
// I'm modifying multiple types here, you won't need the loop if you're just doing posts
foreach (['some', 'types'] as $type) {
if (!($route =& $routes['/wp/v2/' . $type])) {
continue;
}
// Allow ordering by my meta value
$route[0]['args']['orderby']['enum'][] = 'meta_value_num';
// Allow only the meta keys that I want
$route[0]['args']['meta_key'] = array(
'description' => 'The meta key to query.',
'type' => 'string',
'enum' => ['my_meta_key', 'another key'],
'validate_callback' => 'rest_validate_request_arg',
);
}
return $routes;
});
REF: https://github.com/WP-API/WP-API/issues/2308

Resources