152

You are right to say: Never use query_posts anymore pre_get_posts pre_get_posts is a filter, for altering any query. It is most often used to alter only the 'main query': add_action('pre_get_posts','wpse50761_alter_query'); function wpse50761_alter_query($query){ if( $query->is_main_query() ){ //Do something to main query } } (...


58

There are two different contexts for loops: main loop that happens based on URL request and is processed before templates are loaded secondary loops that happen in any other way, called from template files or otherwise Problem with query_posts() is that it is secondary loop that tries to be main one and fails miserably. Thus forget it exists. To modify ...


33

I see many people prefer to use pre_get_posts hook instead of query_posts Yay! So pre_get_posts filters a WP_Query object which means anything you could do via query_posts() you can do via $query->set() and $query->get(). In particular we can make use of the meta_query attribute (see Codex): $meta_query = array( array( ...


24

The simplest way is to add the action right before the query and remove it immediately after. add_action('pre_get_posts', 'some_function_in_functionsphp'); $my_secondary_loop = new WP_Query(...); remove_action('pre_get_posts', 'some_function_in_functionsphp'); if( $my_secondary_loop->have_posts() ): while( $my_secondary_loop->have_posts() ): $...


24

There are legitimate scenarios for using query_posts($query), for example: You want to display a list of posts or custom-post-type posts on a page (using a page template) You want to make pagination of those posts work Now why would you want to display it on a page instead of using an archive template? It's more intuitive for an administrator (your ...


22

I know this is an old question, but it is a bit confusing and hopefully will help someone. The reason that `$query->set doesn't work is because the query has already been parsed and now we need to also update the tax_query object also. Here is how I did it: function my_tax_query( $query ) { $package_id = 12345; $tax_query = array( 'taxonomy' ...


20

Set orderby to post__in. This preserves post ID order given in the post__in array (available with Version 3.5). $args = array ( 'posts_per_page' => -1, 'post__in' => $ids, 'orderby' => 'post__in' );


16

If you don't wish to always click the "Title" column to sort your posts by title, you can place this code in either your currently active WordPress theme's functions.php file, or within a plugin. This will automatically always sort your posts for you, so you don't have to click the title column every time. You can use this for setting default sort order on ...


15

I'll take another shot. The following should modify the main query, such that it will include in its loop any posts that belong to no term of the Edition custom taxonomy. add_filter('pre_get_posts','better_editions_archive'); function better_editions_archive( $query ) { if ( $query->is_tax( 'edition' ) && $query->is_main_query() ) { ...


14

As of Wordpress 3.7 a new action named parse_tax_query was added exactly for this purpose. function kia_no_child_terms($wp_query) { $wp_query->tax_query->queries[0]['include_children'] = 0; } add_action('parse_tax_query', 'kia_no_child_terms'); This hook modifies the values of both query_vars and tax_query. Using the pre_get_posts method ...


14

If you change your if statement to use $query->is_main_query() like the example on the is_main_query() codex page, does that resolve the issue? The nav menus are created by a query, so usually this problem stems from the pre_get_posts interfering with the menus query. Hence, I wonder whether that check isn't working.


14

pre_get_posts will run the same query, so both will take same time. But, If you utilize pre_get_posts action you will save one or more SQL queries. Right now, WordPress is running default query and then you run your query with this function which replace the results of the default query (resulting, default query is of no use). Below is how you can move your $...


14

To achieve adding a custom sortable column to the WP_List_Table of your post type within the WordPress administration back-end dashboard, you will need to do the following: Replace all occurrences of YOUR-POST-TYPE-NAME with your actual post type name. Replace all occurrences of YOUR-TAXONOMY-NAME with your actual taxonomy name. Replace all occurrences of ...


14

Improve your conditional to include a check for post type being queried. It can be done via WP_Query::get method So, where you have if ( !is_admin() ){ $query->set( 'meta_key', '_ct_selectbox_52f65ae267764' ); $query->set( 'meta_value', $city ); return; } replace with if ( ! is_admin() && in_array ( $query->get('post_type'), ...


14

As Milo said : $query->set('meta_key', 'wpcf-object-sold-status' ); $query->set('orderby', array('meta_value' => 'ASC', 'date' => 'DESC')); // $query->set('order', 'ASC DESC' ); // not needed Relevant link: https://make.wordpress.org/core/2014/08/29/a-more-powerful-order-by-in-wordpress-4-0/


13

Use is_main_query() to modify only the main query so menu will stay not affected. Try This: add_action( 'pre_get_posts', 'fteh_pre_get_posts' ); function fteh_pre_get_posts( $query ){ if( !is_admin() && $query->is_main_query() && isset( $query->query_vars['type'] ) ) $types = explode( ',', $query->...


13

I finally got it working, but not with the code in my question. I totally scrapped that whole idea and restarted going in a new direction. NOTE: If anyone is ever able to sort out the issues in my question, feel free to post an answer. Also, if you have any other solutions, feel free to post an answer. REWORKED CLASS AND SOLUTION: What I tried to do here ...


12

Here is an approach to account for sticky posts by getting the number of sticky posts (if any) and include that in the calculation posts_per_page parameter: add_action('pre_get_posts', 'ad_custom_query'); function ad_custom_query($query) { if ($query->is_main_query() && is_home()) { // set the number of posts per page $...


12

Easy Peasy, just tested 2018, using in production currently. $query->set( 'meta_query', array( 'relation' => 'OR', array( 'key' => 'custom_meta_key', 'compare' => 'EXISTS' ), array( 'key' => 'custom_meta_key', 'compare' => 'NOT EXISTS' ) ) ); $query->set( 'orderby', 'meta_value title'...


12

When 'pre_get_posts' is fired, there are a lot of things that WordPress has already done like setup all the query variables (also the query you don't send) and setup all the conditional properties (all the is_* properties). So, if you want completely replace the query you should reset all that things and set your own. In fact, even if your code works, ...


11

You're almost there mate. Try this though. <?php add_action('pre_get_posts', 'filter_press_tax'); function filter_press_tax( $query ){ if( $query->is_tax('press') && $query->has_term('press')): $query->set('posts_per_page', 5); return; endif; } ?> You can use any conditional tag or ...


10

Late answer as the most upvoted answer will break your query and simply isn't true in some major points. The main WP_Query and it's filters First, WordPress internally uses query_posts() (a thin wrapper around WP_Query that shouldn't be used in themes or plugins) to do a WP_Query. This WP_Query is acting as the main loop/query. This query will run through ...


9

I modify WordPress query from functions.php: //unfortunately, "IS_PAGE" condition doesn't work in pre_get_posts (it's WORDPRESS behaviour) //so you can use `add_filter('posts_where', ....);` OR modify "PAGE" query directly into template file add_action( 'pre_get_posts', 'myFunction' ); function myFunction($query) { if ( ! is_admin() && $...


9

This method will return all of the posts including those with and without the requested meta_key, but it will do weird things when ordering. add_action('pre_get_posts', 'my_stuff'); function my_stuff ($qry) { $qry->set( 'meta_query', array( 'relation' => 'OR', # Matches to this meta_query should be added to those ...


8

Just use the appropriate conditionals: function change_order_for_events( $query ) { //only show future events and events in the last 24hours $yesterday = current_time('timestamp') - 24*60*60; if ( $query->is_main_query() && (is_tax('event_type') || is_post_type_archive('wr_event')) ) { $query->set( 'meta_key', '...


8

Your filter has a bug in it, namely when you call is_main_query, you're not checking if the passed query is the main query, your checking if the currently active query is the main query, which will always be true. So instead try this: add_action( 'pre_get_posts', 'some_name'); function some_name($query) { if ($query->is_front_page() && $...


8

Instead of trying to display all the matching events on your by-date page, you could try to display through ?post_type=event like this: function rewrite_rule_by_date() { add_rewrite_rule('by\-date/([0-9]{4}\-[0-9]{2}\-[0-9]{2})$', 'index.php?post_type=event&event_date=$matches[1]', 'top'); } add_action( 'init', 'rewrite_rule_by_date' ); function ...


8

After a bit of investigation... If you pass a category to is_category it uses get_queried_object to grab data-- see the source. get_queried_object returns NULL for categories that do not exist. You can demonstrate that with: function custom_posts_per_page($query) { var_dump(get_queried_object()); } add_filter( 'pre_get_posts', 'custom_posts_per_page' ); ...


8

It does't work because you can't use set to change all query vars. Simplest way to do the trick is set 's' to an empty string: add_action( 'pre_get_posts', function( $query ) { if ( ! is_admin() && $query->is_main_query() && $query->is_search() ) { $taxquery = array( ... ); $query->set( 'tax_query', $taxquery ); $...


7

Just to outline some improvements to the accepted answer since WordPress evolved over the time and some things are different now (five years later): pre_get_posts is a filter, for altering any query. It is most often used to alter only the 'main query': Actually is an action hook. Not a filter, and it will affect any query. The main query appears in ...


Only top voted, non community-wiki answers of a minimum length are eligible