orderby multiple meta_keys and then orderby date_modified - php

A post is created for each talent with a status of either coming-soon or in-town or others saved as meta_values. I needed to display posts first by "coming-soon" and then "intown" and then "others".
I realized that I was looking for orderby multiple meta_keys and found a solution here. I converted the syntax in order for it to work with pre_get_posts hook ( since it is main loop in category.php). As I need, code below mostly works and shows results ordered by coming-soon->in-town->others.
add_filter('pre_get_posts', 'pre_get_posts_hook' );
function pre_get_posts_hook($wp_query) {
if (is_category() && $wp_query->is_main_query()) {
$wp_query->set('meta_query',
array( 'relation' => 'OR',
'soon' => array( 'key' => 'coming_soon',
'value' => 'yes',
'compare' => '='
),
town' => array( 'key' => 'in-town',
'value' => 'positive',
'compare' => '='
),
'others' => array( 'key' => 'others',
'value' => 'null',
'compare' => '='
)
)
);
$wp_query->set('orderby', array('soon' => 'DESC', 'town' => 'DESC', 'others' => 'DESC'));
return $wp_query;
}
}
Now, I need to run a second level of orderby. I mean, with the code above, first I get the posts with meta_key coming-soon, I expect these posts to be again ordered by date_modified and possibly a control on their order eg. ASC, DESC. I am quite sure that it is possible. I just have no clue how do I nest it further.

Solved. I will leave the answer in case it may help someone in future. I had to replace this line.
$wp_query->set('orderby', array('soon' => 'DESC', 'town' => 'DESC', 'others' => 'DESC'));
with this line.
$wp_query->set('orderby', array('meta_value' => 'DESC', 'modified' => 'DESC' ));
I think the code is pretty self-explanatory. What it does is, it will allow you to run you primary-orderby and then once you get those results, you can run a secondar-orderby. Of course, the second associative array is not limited to modified, it can be any other parameters of orderby. Eg. title, author etc.

Related

get_terms - get total number of results when 'include' array is given

I want to get 12 term objects by calling get_terms; and I have a specific array of terms_ids which should come first.
Here is my query so far:
$term_args = array(
'taxonomy' => 'coupon_store',
'number' => 12,
'include' => $location_terms,
'meta_query' => array(
array(
'key' => '_wpc_is_featured',
'value' => 'on',
'compare' => '=',
),
)
);
$store_terms = get_terms( $term_args );
The problem is that since $location_terms consists of only 3 values, the whole result count is also restricted to 3. I know this is not quite possible as per get_terms documentation.
Is there any hack to get the rest 9 results, after getting the 3 from that array?
UPDATE
I have achieved it by using 2 queries as described in #Yoda's answer. Is there any way to get it done by using get_terms only once.
So... this basically doesn't work.
The include parameter sets up a where condition in the SQL query that requires all results to be within the include array.
So, that's not great for what you're after.
However, I do have a solution for you!
$term_args = array(
'taxonomy' => 'coupon_store',
'number' => 12,
'include' => $location_terms,
'meta_query' => array(
array(
'key' => '_wpc_is_featured',
'value' => 'on',
'compare' => '=',
),
)
);
$store_terms_location = get_terms( $term_args );
$term_args = array(
'taxonomy' => 'coupon_store',
'number' => 12 - count($store_terms_location), // only get as many as you still need
'exclude' => $location_terms,
'meta_query' => array(
array(
'key' => '_wpc_is_featured',
'value' => 'on',
'compare' => '=',
),
)
);
$store_terms_other = get_terms( $term_args );
// merge the two arrays together in order of priority
$store_terms = array_merge($store_terms_location, $store_terms_other);
So, to cover what this does:
Get up to 12 location terms
Get the remaining number of terms from a list that excludes the ones we checked for earlier
Merge the two lists together
This should give you the results you need. You can tidy it up, use some conditionals to determine whether the latter part needs to run, etc. Build on the general idea and make it fit what you're trying to do with your code.
What you are trying to do doesn't make a whole lot of sense.
If you already know the identity of the three terms you to have get first in your array, making a query to get those is kinda irrelevant. You could simply make two separate queries and merge the results.
E.g.:
$first_terms = array(
'taxonomy' => 'coupon_store',
'include' => $location_terms,
);
$store_terms_1 = get_terms( $term_args );
$remaining_terms = array(
'taxonomy' => 'coupon_store',
'number' => 9,
'exclude' => $location_terms,
'meta_query' => array(
array(
'key' => '_wpc_is_featured',
'value' => 'on',
'compare' => '=',
),
)
);
$store_terms_2 = get_terms( $term_args );
$resulting_terms = array_merge( $store_terms_1, $store_terms_2 );
Without knowing more about your data, it is not easy to do much more.
Guesstimating a bit, since you are already using term metas in your query, you could add another term meta with the order, and use that to order your results. This way you wouldn't need to hardcode those first three terms that you want to have on top, and you would only need to make one query.
E.g.:
$store_terms = [
'taxonomy' => 'coupon_store',
'number' => 12,
'meta_key' => 'coupon_store_term_order,
'orderby' => 'meta_value_num',
'meta_query' => [
[
'key' => '_wpc_is_featured',
'value' => 'on',
'compare' => '=',
],
]
];
This way, you have to set up a new term meta for your terms (coupon_store_term_order), and save there your desired order. You would need a bit more fiddling (e.g. deal with terms with no defined order, etc).
And logically, I'm further assuming that these three terms are also featured terms. Otherwise, making two requests and merging is still the only logical way to go.

WP_Query over two meta keys are sorted separately

I've stumbled upon a WordPress query problem I don't seem to be able to fix.
I've got a custom post type called products that has two fields, price and price_campaign. Sometimes, price is only set, sometimes it's only price_campaign. I still want my final query to output all products in order, ordered by price/price_campaign, as if these two fields were the same. I don't seem to be able to do that, though:
$query->set(
'meta_query',
array(
'relation' => 'AND',
'price_campaign' => array(
'key' => 'price_campaign',
'type' => 'NUMERIC',
'compare' => 'EXISTS'
),
'price' => array(
'key' => 'price',
'type' => 'NUMERIC',
'compare' => 'EXISTS'
),
)
);
$query->set(
'orderby',
array(
'meta_value_num' => 'ASC',
'title' => 'ASC'
)
);
But what happens is that I first get the products that has price set and then all the ones with price_campaign set.
If I change it to 'relation' => 'OR' they are just put out in a completely random order, where neither prices nor titles seem to matter.
I really can't figure out what's the problem. Any ideas?

Need assistance with wp_user_query not working

I need some help getting a wp_user_query to work; been struggling and trying lots of stuff but cannot get it to work proper way.
here is my code:
$args = array(
'meta_query' => array(
'role' => 'personal-injury-lawyer',
'orderby' => 'meta_value',
'meta_key' => 'lawyer_numeric_rank',
'order' => 'ASC',
array(
'key' => 'lawyer_location',
'value' => 'London',
'compare' => '='
),
)
);
$london_user_query = new WP_User_Query($args);
if ( ! empty($london_user_query->results)) {
foreach ( $london_user_query->results as $user ) {
I know the foreach loop is not closed; just trying shorten this...
I am trying to do three things on this query:
Show roles of personal-injury-lawyer
Show where meta_key field of lawyer_location = London
Order by meta_value where meta_key = lawyer_numeric_rank in ASC order
I have tried this a number of ways but cannot get this to work with both filtering on London location and also ordering by the rank...
The code right now does filter on location; but the orderby part is not working; if i remove the location filter from this; then the orderby does work...
I hope someone can help with this.
you were forming the meta query argument incorrectly. The below code should help you with quite a few more meta queries as well.
$args = array(
'role'=> 'personal-injury-lawyer', //assuming you have created a role that corresponds..
'order' => 'ASC',
'orderby' => 'meta_value',
'meta_key' => 'lawyer_numeric_rank',
'meta_query' => array(
// 'relation'=> 'AND', --> if you want more conditions
array(
'key' => 'lawyer_location',
'value' => 'London',
'compare' => '='
),
/* even more conditions with OR, cumulative effect is match lawyer location AND 1 of the nested arrays
array(
'relation' => 'OR',
array(
'key' => '',
'value' => '',
'compare' => '=',
),
array(
'key' => '',
'value' => '',
'compare' => '=',
),
),
*/
)
);

How do you order by 2 meta values in Wordpress?

I am using Woocommerce, although my question relates to WP_Query in general. I am creating a custom WP_Query loop for bestselling products and I want to allow users to be able to sort the results by price, from high to low and low to high. The problem is in Woocommerce both _price and total_sales are meta fields and as far as I can tell I can only orderby 1 meta field in Wordpress loops. Is there a way around this?
My full code including some of my attempts is here, the most relevant code segment looks like as follows:
$queryArgs = array(
'post_type' => 'product',
'posts_per_page' => -1,
'post_status' => 'publish',
'meta_query' => array(
array(
'key' => '_visibility',
'value' => array('catalog', 'visible'),
'compare' => 'IN'
)
),
'meta_key' => '_price',
'orderby' => array('meta_value_num' => $sortBy['terms'])
);
The actual code is more complicated than this because it's built for a range of filtering and sorting options, but this is the gist of it and bestsellers is the only thing giving me issues because of the two meta_keys. I've read this but it doesn't apply to custom meta fields.
I've tried:
'meta_key' => '_price total_sales'
'meta_key => array('_price', 'total_sales')
'orderby' => array('meta_value_num' => $sortBy['terms'], 'meta_value_num' => 'DESC')
Nothing seems to work. I've also tried hooking onto the various WP_Query filters but the problem is this query is part of a dynamically generated loop so I can't 'hack' or hard-code anything.
Use the meta query with arrays:
'meta_query' => array (
array (
'key' => '_visibility',
'value' => array('catalog', 'visible'),
'compare' => 'IN'
),
array (
'key' => '_price',
'value' => "VALUE WHAT YOU WANT, NOT ASC/DESC",
)
),

Wordpress Custom Meta Query Search is so slow when in OR relation

I made a search which is basically querying custom meta fields and showing the results. For some reason, the search takes endless hours. When I change the relation to "AND" it works great but when I change the relation to "OR" it's dead.
Currently in the database I have 5 records so it shouldn't take time.
This is the query
$args = array(
'post_type' => 'shipping-schedules',
'posts_per_page' => 5,
'post_status' => 'publish',
'paged' => $paged,
'meta_query' => array(
'relation' => 'OR',
array(
'key' => 'schedules_port',
'value' => $sfrom,
'compare' => 'LIKE'
),
array(
'key' => 'schedules_port',
'value' => $sto,
'compare' => 'LIKE'
),
array(
'key' => 'schedules_vessel',
'value' => $svessel,
'compare' => 'LIKE'
),
array(
'key' => 'schedules_voyage',
'value' => $svoyage,
'compare' => 'LIKE'
),
array(
'key' => 'schedules_arrival',
'value' => $sdate_arrival,
'compare' => '>=',
'type' => 'NUMERIC'
),
array(
'key' => 'schedules_departure',
'value' => $sdate_departure,
'compare' => '<=',
'type' => 'NUMERIC'
)
)
);
As you can see in the above query this line 'relation' => 'AND' changing it to 'relation' => 'OR' will result the problem am facing slow querying.
I found this problem too... but the problem is I needed to use OR for my particular case.
After reading a ton of info, such as indexing the post meta table values (this actually sounds like a good idea, but my code was in a plugin so couldn't do this) I found a way that seems to run the same query a lot faster.
I have no idea why, and haven't begun to poke around under the hood, but instead of doing a WP meta query use the filter posts_where and modify the WHERE clause yourself adding in your own arguments..
Here's my code:
add_filter('posts_where', array($this, 'filter_meta_query_where')); //modify the where clause to filter meta data
add_filter('posts_join' , array($this, 'filter_meta_join')); //join the meta table to the posts query
This is the main function that adds the meta query (I've added conditions for schedules_port, you'll have to build the rest yourself the way you want):
function filter_meta_query_where($where = '')
{
global $wpdb;
global $wp_query;
global $sf_form_data;
if(!is_admin())
{
$where .= " AND (($wpdb->postmeta.meta_key = 'schedules_port' AND ($wpdb->postmeta.meta_value LIKE '%$sfrom%' OR $wpdb->postmeta.meta_value LIKE '%$sto%')))";
}
return $where;
}
And here's the code for the join
function filter_meta_join($join)
{
global $wpdb;
$join .= " LEFT JOIN $wpdb->postmeta ON $wpdb->posts.ID = $wpdb->postmeta.post_id ";
return $join;
}
Hope that helps

Categories