Wordpress orderby query sometimes not working - php

I have been looking for the error for a while and have no idea what could be causing it.
In about 80% of the time the following code works, but sometimes it just orders completely random:
function filter_where($time, $where = '') {
$where .= " AND post_date > '" . date('Y-m-d H:i:s', strtotime($time)) . "'";
$where .= " AND post_status = 'publish' ";
return $where;
}
add_filter('posts_where', 'filter_where');
$array = array(
'cat' => 2,
'meta_key' => 'ratings_score',
'orderby' => 'meta_value_num',
'order' => 'DESC',
'meta_query' => array(
array(
'key' => 'ratings_score',
),
),
'posts_per_page' => 6
);
filter_where($time);
query_posts($array);
The filter function works. I have tested it with different $time values and post_status's.
Could it just be some kind of lag or have I ordered the code in a wrong manner?
some posts do not have a meta_key of ratings_score, but all posts in cat = 2 do have this meta_key.
Maybe this has something to do with the issue?

First of all, never use query_posts
Note: This function isn't meant to be used by plugins or themes. As explained later, there are better, more performant options to alter the main query. query_posts() is overly simplistic and problematic way to modify main query of a page by replacing it with new instance of the query. It is inefficient (re-runs SQL queries) and will outright fail in some circumstances (especially often when dealing with posts pagination).
You should be using WP_Query or get_posts and only when you cannot achieve the results you are after by modifying the main query with pre_get_posts
You are correctly adding your filter before your query arguments, but then you are again adding the function again after the query arguments. I believe this is one of the big problems here. You should remove the filter after the query arguments, not re-adding them
Replace this line
filter_where($time);
with
remove_filter('posts_where', 'filter_where');
One final note, why are you defining $time but then you doesn't use it at all?

Related

Simulating SELECT YEAR(post_date) SQL query using only WP Query

I'm not entirely sure this is possible but I'm completely out of ideas. The task I'm working on requires that I specifically use WP_Query due to constraints of other efficiency plugins my organization uses.
Pure and simple, I need a way to simulate the following query using WP_Query:
"SELECT YEAR(post_date) FROM wp_posts WHERE post_status = 'publish' GROUP BY YEAR(post_date) DESC"
Before you answer, note again, using the $wpdb global is off the table, and I've attempted to employ the parse_query method from WP_Query but it doesn't seem to respond to the above query in any meaningful way.
Thank you to anyone who takes the time to think this over!
Probably try this:
<?php
//Alter the group by clause
function query_group_by_year_post_date_filter($groupby){
return 'YEAR(wp_posts.post_date) DESC '; //try only post_date if this doesn't work
}
//Alter the select query
function query_select_fields($fields) {
return 'YEAR(wp_posts.post_date)'; //try only post_date if this doesn't work
}
add_filter('posts_fields','query_select_fields');
add_filter('posts_groupby', 'query_group_by_year_post_date_filter');
$allPosts = get_posts(array( //we have to use get_posts instead of WP_Query cause get_posts supports supress_filters
'post_type' => 'post', //specify post_type here or leave it in your case.
'post_status' => 'publish',
'posts_per_page' => -1,
'suppress_filters' => true //this seems to be preset in only get_posts and not WP_Query
));
remove_filter('posts_groupby', 'query_group_by_filter');
remove_filter('posts_fields','query_select_fields');
?>
I've tried not to use $wpdb in 2 functions at the top as per your requirements.
let me know if this worked for you.

Need enlightenment about the complexity of query posts

There's something I try to understand about the process of querying posts in wordpress:
Lets say I have this code:
$args = array{
'cat' => 'animals',
'posts_per_page' => 3
}
$the_query = new WP_Query($args);
So theoretically wordpress go to the database, going through all posts, and for each post, from the first to the last, it "asks" the post for it's category, if the answer is "animals" it grabs that post and move to the one after, if the category is something else, it skip that post and move to the one after. And repeat that process n times?
Or, since I set posts_per_page to 3, after the third post that matchs "animals", the process it stops?
If the answer is the first, How can I optimize the code if, let's say I have 300 posts under "animals", but I need only the latest 3. How can I tell wordpress to stop checking after the third one that match "animals"?
I'm asking this since I'm working on a custom theme for site with more then 40000 posts, the homepage need to have at least 5 different queries like the one above, and I want to make it as efficient as possible.
I hope that's make sense.
Thanks in advance!
If you look into the WP core, you will see this piece of code in wp-includes/query.php, in the get_posts function:
if ( empty($q['offset']) ) {
$pgstrt = absint( ( $page - 1 ) * $q['posts_per_page'] ) . ', ';
} else { // we're ignoring $page and using 'offset'
$q['offset'] = absint($q['offset']);
$pgstrt = $q['offset'] . ', ';
}
$limits = 'LIMIT ' . $pgstrt . $q['posts_per_page'];
This mean that the posts_per_page parameter is translated to a basic sql LIMIT. So you don't have to worry for performance on this side.
How does 'LIMIT' parameter work in sql?
Considering the wp_terms_taxonomy.term_taxonomy_id, wp_terms_taxonomy.term_id_taxonomy, wp_term_relationships.term_taxonomy_id are all indexes the LIMIT should not select all rows, as far as I know.

Wordpress count_posts() equivalent function non-expensive method

I'm looking to count the number of posts in the last week then group them by a custom taxonomy called 'topic' So that in the next get_posts equation I can get topics by the number of posts to that area in the last week.
It can be done like this with get posts, but I am concerned that this is unnecessarily expensive on the server. Is there another way?
function count_posts_by_taxanomy($since,$taxonomy){
global $sincer;
$sincer = $since;
function filter_post_count($where = ''){
global $sincer;
$where .= " AND post_date > '" . date('Y-m-d', strtotime($sincer)) . "'";
return $where;
}
$args = array(
'numberposts' => -1,
'post_type' => 'post',
'suppress_filters' => false
);
add_filter('posts_where','filter_post_count');
$posts = get_posts($args);
remove_filter('posts_where','filter_post_count');
$count_term = array();
foreach($posts as $post){
foreach(get_the_terms($post->ID,'topic') as $term){
$count_term[$term->slug] += 1;
}
}
print_r($count_term);
}
Called like this:
count_posts_by_taxanomy('-5 days','topic');
You would be better using a custom database query. See here for more info on that: http://codex.wordpress.org/Class_Reference/wpdb
I would then suggest you store the result in a transient. You don't need to run the query on every load.
http://codex.wordpress.org/Transients_API
You can use WP_Query and call $wp_query->found_posts to find count of posts. And then do a loop and cache values.
More at : http://codex.wordpress.org/Class_Reference/WP_Query
$query = new WP_Query( array('posts_per_page'=>-1,
'post_type'=>array('post'),
'date_query' => array(array('after' => 'YOUR DATE')),
'tax_query' => array(
array(
'taxonomy' => 'YOUR TAX'
))));

Concatenate two meta keys?

I have a school timetable as a custom post type. Each post is a school class with a post meta box containing two text fields for specifying the hour and minute that a class starts in 24-hour time format:
_start_hour
_start_minute
I am trying to output the posts in order according to time e.g.
// the args
$args = array(
'post_type' => 'my-cpt',
'meta_key' => '???????',
'orderby' => 'meta_value_num',
'order' => 'ASC',
'posts_per_page' => -1,
);
// The Query
$the_query = new WP_Query( $args );
while ( $the_query->have_posts() ) : $the_query->the_post();
// ordered output according to time
endwhile;
In 'meta_key' is there some way I can concatenate the two meta keys?
I have tried 'meta_key' => '_start_hour' && '_start_minute' but this breaks the query.
Unfortunately no, wordpress doesn't support this feature, you will have to sort it by yourself after fetching it from the database and before the loop.
Disclaimer note
This is extremely ugly by design, but this is Wordpress so you have to play with what you get, you can make it less ugly if you fallback to writing the SQL queries yourself, depends on performance in my opinion, as Wordpress can be a performance degrader beast if not handled properly you should consider making it with SQL queries instead.
// Fetch all posts - (1 SQL Query)
$query = new WP_Query(array(
'post_type' => 'my-cpt',
'order' => 'ASC',
'posts_per_page' => -1,
));
foreach ($query->posts as &$post) { // N queries as the number of posts you have - totally inefficient
$post->meta = get_post_meta($post->ID);
}
usort($query->posts, function($a, $b) {
$a_time = strtotime($a->meta['_start_hour'][0] . ':' . $a->meta['_start_minute'][0]);
$b_time = strtotime($b->meta['_start_hour'][0] . ':' . $b->meta['_start_minute'][0]);
if ($a_time > $b_time)
return 1;
else if ($a_time < $b_time)
return -1;
else
return 0;
}); // Sorting by date
... the_loop ...
note that this is totally untested so it should just give you pointers on hour should you do it, and again I say, you should refactor this to join the meta keys in advance, that way you can perhaps already sort it with the SQL instead of the PHP...

How to sort a 'query_posts' function by custom field, while limiting posts by another custom field

I'm querying a series of posts in WP with the following function:
<?php
$thirtydays = date('Y/m/d', strtotime('+30 days'));
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
query_posts( array(
'post_type' => array('post', 'real-estate'),
'meta_key' => 'Time Available',
'meta_compare' => '<=',
'meta_value' => $thirtydays,
'paged' => $paged ));
?>
This part is working fine. It's basically pulling all my Real Estate posts, but only returning results that have a 'Time Available' of 30 days or less.
I need this to also order the posts in ascending order from low to high using the data from another custom field, 'Price.'
Whenever I add the standard 'orderby' => 'meta_value', 'meta_key' => 'Price' it no longer shows results within 30 days.
Is there any way I can combine these two? And is it possible to add a button which re-runs the query and sorts by Price, Bedrooms, etc? Or is this too specific for WP?
I believe this will provide you want you need. It's a class called PostsOrderedByMetaQuery that extends WP_Query and accepts new arguments 'orderby_meta_key' and 'orderby_order':
class PostsOrderedByMetaQuery extends WP_Query {
var $posts_ordered_by_meta = true;
var $orderby_order = 'ASC';
var $orderby_meta_key;
function __construct($args=array()) {
add_filter('posts_join',array(&$this,'posts_join'),10,2);
add_filter('posts_orderby',array(&$this,'posts_orderby'),10,2);
$this->posts_ordered_by_meta = true;
$this->orderby_meta_key = $args['orderby_meta_key'];
unset($args['orderby_meta_key']);
if (!empty($args['orderby_order'])) {
$this->orderby_order = $args['orderby_order'];
unset($args['orderby_order']);
}
parent::query($args);
}
function posts_join($join,$query) {
if (isset($query->posts_ordered_by_meta)) {
global $wpdb;
$join .=<<<SQL
INNER JOIN {$wpdb->postmeta} postmeta_price ON postmeta_price.post_id={$wpdb->posts}.ID
AND postmeta_price.meta_key='{$this->orderby_meta_key}'
SQL;
}
return $join;
}
function posts_orderby($orderby,$query) {
if (isset($query->posts_ordered_by_meta)) {
global $wpdb;
$orderby = "postmeta_price.meta_value {$this->orderby_order}";
}
return $orderby;
}
}
You would call it like this:
$thirtydays = date('Y/m/d', strtotime('+30 days'));
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$query = new PostsOrderedByMetaQuery(array(
'post_type' => array('post', 'real-estate'),
'meta_key' => 'Time Available',
'meta_compare' => '<=',
'meta_value' => $thirtydays,
'paged' => $paged,
'orderby_meta_key' => 'Price',
'orderby_order' => 'DESC',
));
foreach($query->posts as $post) {
echo " {$post->post_title}\n";
}
You can copy the PostsOrderedByMetaQuery class to your theme's functions.php file, or you can use it within a .php file of a plugin you may be writing.
If you want to test it quickly I've posted a self-contained version of the code to Gist which you can download and copy to your web server's root as test.php, modify for your use case, and then request from your browser using a URL like http://example.com/test.php.
Hope this helps.
-Mike
P.S. This answer is very similar to an answer I just gave over at WordPress Answers, which is the sister site of StackOverflow where lots of WordPress enthusiasts like me answer questions daily. You might want to see that answer too because it has a tad more explanation and because you might want to see WordPress Answers. Hope you'll consider posting your WordPress questions over there too in the future?
Because 'orderby' => 'meta_value' requires meta_key, and your meta_key is already in use for a comparison, I don't think you can do this. meta_key only accepts a single value and not an array of options. This is definitely a limitation and I encourage you to open a request if you don't find a solution.
As far as the button to re-run the query, you could simply reload the page and pass a query variable to change the sort. Unfortunately you still have to solve the first part of your question.
UPDATE
You could always sort the returned array yourself using PHP.
Also, not sure what you are checking with time available but you could register a filter that may help you customize the query a bit further add_filter('posts_where', ...); http://codex.wordpress.org/Function_Reference/query_posts

Categories