I added a new sorting option to Woocommerce, which would sort by the lowest price. All my prices are stored in a custom attribute, along with some other serialized data. What I want is to have a callback function which would unserialize this data, check for the lowest price and sort by that price.
Every post I saw about custom ordering options, makes use of the woocommerce_get_catalog_ordering_args filter to add orderby arguments to WP_Query, but I haven't seen any way to sort the results of the returned query via a callback function.
Is there any way to achieve this? I was looking at woocommerce_after_product_ordering based on the name, but couldn't find much info about it. Any help would be much appreciated!
function my_woocommerce_product_sorting($orderby) {
$orderby['price_asc'] = "Sort by cheapest";
$orderby['price_desc'] = "Sort by most expensive";
return $orderby;
}
add_filter("woocommerce_catalog_orderby", "my_woocommerce_product_sorting", 20);
I think it will be best solution when you store custom price with separate new meta key like wooocommerce store product price in post meta name _price and value with numeric price.
mysql order by serialized data?
When you store separated then it will change wp query args like :
function my_woocommerce_get_catalog_ordering_args( $args ){
$order_by = isset( $_GET['orderby'] ) ? wc_clean( $_GET['orderby'] ) : $args['orderby'];
if( $order_by == 'price_desc' || $order_by == 'price_asc' ){ // Check match to custom filter
$args['orderby'] = "meta_value_num ID";
$args['order'] = $order_by == 'price_desc' ? 'DESC' : 'ASC';
$args['meta_key'] = '_my_custom_price'; // Put your price custom post meta price key name
}
return $args;
}
add_filter( 'woocommerce_get_catalog_ordering_args', 'my_woocommerce_get_catalog_ordering_args' );
Related
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 );
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]');
I would like on my site to be able to order the products page (woocommerce)
as well as with the classic settings
sort by popularity
sort by average rating
sort by newness
sort by price: low to high
sort by price: high to low
but also with an attribute present in all products
sort by my custom attribute: low to high
sort by my custom attribute: high to low
I found various posts about it and made several tests ... but I'm not a programmer ... I still haven't been able to solve the problem.
I found this article
https://docs.woocommerce.com/document/custom-sorting-options-ascdesc/
explaining how to add
sort by random
and it works correctly
but I didn't understand, starting from this code what you need to add / modify to replace random with my custom attribute.
My attribute name is "pa_kj"
Its value is alphanumeric
I thank everyone in advance for any suggestions
I found this code on the net
https://gist.github.com/bekarice/0df2b2d54d6ac8076f84
<?php
/**
* Adds WooCommerce catalog sorting options using postmeta, such as custom fields
* Tutorial: http://www.skyverge.com/blog/sort-woocommerce-products-custom-fields/
**/
function skyverge_add_postmeta_ordering_args( $sort_args ) {
$orderby_value = isset( $_GET['orderby'] ) ? wc_clean( $_GET['orderby'] ) : apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) );
switch( $orderby_value ) {
// Name your sortby key whatever you'd like; must correspond to the $sortby in the next function
case 'location':
$sort_args['orderby'] = 'meta_value';
// Sort by meta_value because we're using alphabetic sorting
$sort_args['order'] = 'asc';
$sort_args['meta_key'] = 'location';
// use the meta key you've set for your custom field, i.e., something like "location" or "_wholesale_price"
break;
case 'points_awarded':
$sort_args['orderby'] = 'meta_value_num';
// We use meta_value_num here because points are a number and we want to sort in numerical order
$sort_args['order'] = 'desc';
$sort_args['meta_key'] = 'points';
break;
}
return $sort_args;
}
add_filter( 'woocommerce_get_catalog_ordering_args', 'skyverge_add_postmeta_ordering_args' );
// Add these new sorting arguments to the sortby options on the frontend
function skyverge_add_new_postmeta_orderby( $sortby ) {
// Adjust the text as desired
$sortby['location'] = __( 'Sort by location', 'woocommerce' );
$sortby['points_awarded'] = __( 'Sort by points for purchase', 'woocommerce' );
return $sortby;
}
add_filter( 'woocommerce_default_catalog_orderby_options', 'skyverge_add_new_postmeta_orderby' );
add_filter( 'woocommerce_catalog_orderby', 'skyverge_add_new_postmeta_orderby' );
but it doesn't work with the latest version of Woocommerce, it always returns the page with no products found.
Is there anyone experienced who can recommend the code to be modified?
Thanks again
I am using wordpress+woocommerce and getting issue in sorting. By default i have set option "Custom Ordering+Name" in woocommerce settings. But my other filters like sort by price, Popularity are not working. I have tried to use this hook and it's working but it shows by default all products with highest to low price. I want to set "Custom ordering" when page will load first time and then if user will choose other option like price, popularity then ordering should work according to selected filters.
add_filter('woocommerce_get_catalog_ordering_args', 'am_woocommerce_catalog_orderby');
function am_woocommerce_catalog_orderby( $args ) {
$args['meta_key'] = '_price';
$args['orderby'] = 'meta_value_num';
$args['order'] = 'desc';
return $args;
}
You can you use this code in your functions.php file to set the combobox filter to Sort by price: high to low. I don't know why your filters are not working, they work fine for me in WooCommerce 3.3.3.
add_filter('woocommerce_default_catalog_orderby', 'modify_woocommerce_default_catalog_orderby');
function modify_woocommerce_default_catalog_orderby( $orderby ) {
if( empty( $orderby ) ) {
return 'price-desc';
}
return $orderby;
}
I'm having the following problem. I have set up a Wordpress site with WooCommerce to serve a webshop with only books.
I have created some product attributes that are based on taxonomies like 'publisher' and 'author' (as multiple products can share an author or a publisher)
I would like to be able to sort my products not only on the Woocommerce default fields like 'title' and 'price' but also on these taxonomy fields.
Say for example:
order by Author ASC or order by publisher DESC
As far as I have discovered there is no way to do this with Wordpress core functions. Some say this is because it makes no sense to sort by taxonomy fields, but with the above example I can't understand why you don't want to sort by for example author.
I have played around with sortby meta_value, but this is just querying the direct postmeta, not the taxonomies.
I have knowledge of php, so any solutions involving additional code in my functions.php file will do.
I found out a way how to solve this:
https://gist.github.com/jayarnielsen/12f3a586900aa6759639
I slightly modified the code so it equals the way Woocommerce uses to sort by the system fields "title" and "price" which is adding a url query-param named "sortby" a value structured like this: "field"-"direction"
So to sort by a product property called "pa_tax1" ascending you'll need to add to the url: sortby=pa_tax1-asc
add_filter('posts_clauses', 'posts_clauses_with_tax', 10, 2);
function posts_clauses_with_tax( $clauses, $wp_query ) {
global $wpdb;
// Array of taxonomies you want to sort on
// in case of Woocommerce Properties, be sure to add the pa_ prefix
$taxonomies = array('pa_tax1', 'pa_tax2', 'pa_tax3');
// If no orderby query param is found, do nothing
if( !isset($wp_query->query['orderby']) ) {
return $clauses;
}
// Explode the orderby query-param
$orderQuery = explode('-', $wp_query->query['orderby']);
$orderBy = [];
$orderBy['field'] = $orderQuery[0];
$orderBy['direction'] = (isset($orderQuery[1])) ? strtoupper($orderQuery[1]) : 'ASC';
// Only add clauses, if the sortby field is in our array
if( in_array($orderBy['field'], $taxonomies) ) {
$clauses['join'] .= "
LEFT OUTER JOIN {$wpdb->term_relationships} AS rel2 ON {$wpdb->posts}.ID = rel2.object_id
LEFT OUTER JOIN {$wpdb->term_taxonomy} AS tax2 ON rel2.term_taxonomy_id = tax2.term_taxonomy_id
LEFT OUTER JOIN {$wpdb->terms} USING (term_id)
";
$clauses['where'] .= " AND (taxonomy = '".$orderBy['field']."' OR taxonomy IS NULL)";
$clauses['groupby'] = "rel2.object_id";
$clauses['orderby'] = "GROUP_CONCAT({$wpdb->terms}.name ORDER BY name ASC) ";
$clauses['orderby'] .= ( 'ASC' == strtoupper( $orderBy['direction'] ) ) ? 'ASC' : 'DESC';
return $clauses;
}
else {
return $clauses;
}
}