Howdy WP developer!
In this snippet, I will describe three different techniques to alter the WordPress default query with an example showing how to add a secondary page to the front page in the Customizr theme
Those are quite advanced WordPress programming techniques, so before diving into it make sure you are familiar with the following concepts :
- Hooks in WordPress : learn more here
- WordPress query : learn more here
Altering the WordPress default query in WordPress
There are many ways to get posts in WordPress, but we can distinghish two techniques :
- Altering the WordPress default query in a particular context => what we are going to do here
- Creating a new query from scratch with get_posts() or a new WP_query for example. This topic will not be treated here.
In the following snippets review the different techniques to alter the WordPress default query :
- the pre_get_posts way
- the query_posts way
- the SQL statement filters way
- New : the wp_the_query way
Case study : adding a second page to the front page in the Customizr theme
Let’s say you have defined a static page as front page and you want to display another page below it.
How to use this code? The following code snippets have to be copied and pasted in the functions.php file of a child theme.
The pre_get_posts way
This is the easiest and recommended way to alter the main query. This ‘pre_get_posts’ hook is called after the query variable object is created, but before the actual query is run.
add_action('pre_get_posts','alter_query'); function alter_query($query) { //gets the global query var object global $wp_query; //gets the front page id set in options $front_page_id = get_option('page_on_front'); if ( 'page' != get_option('show_on_front') || $front_page_id != $wp_query->query_vars['page_id'] ) return; if ( !$query->is_main_query() ) return; $query-> set('post_type' ,'page'); $query-> set('post__in' ,array( $front_page_id , [YOUR SECOND PAGE ID] )); $query-> set('orderby' ,'post__in'); $query-> set('p' , null); $query-> set( 'page_id' ,null); //we remove the actions hooked on the '__after_loop' (post navigation) remove_all_actions ( '__after_loop'); }
The query_posts() way
query_posts() is meant for altering the main loop. It does so by replacing the query used to generate the main loop content. Once you use query_posts(), your post-related global variables and template tags will be altered. Conditional tags that are called after you call query_posts() will also be altered – this may or may not be the intended result.
Even if this is a not the best way to alter the query, this technique shows how to make use of some interesting hooks before and after the loop in the Customizr theme.
//those hooks are located in the index.php template of the Customizr theme add_action( '__before_loop' , 'alter_query' ); add_action( '__after_loop' , 'alter_query' ); function alter_query() { //we call the global query variable from the current instance of WP_query class. global $wp_query; //gets the front page id set in options $front_page_id = get_option('page_on_front'); //checks the context before altering the query if ( 'page' != get_option('show_on_front') || $front_page_id != $wp_query->query_vars['page_id'] ) return; //this switch statement allows us to use the same call back function for two different hooks switch ( current_filter() ) { case '__before_loop': $args = array( 'post_type' => 'page', 'post__in' => array( $front_page_id , [YOUR SECOND PAGE ID] ), 'orderby' => 'post__in'//we want to keep the order defined in the 'post__in' array ); //execute the query_posts( $args ); //set global $wp_query object back initial values for boolean and front page id $wp_query->is_page = true; $wp_query->is_singular = true; $wp_query->query_vars['p'] = $front_page_id; $wp_query->query_vars['page_id'] = $front_page_id; break; case '__after_loop': //reset the custom query wp_reset_query(); break; } }
This whole query_posts() issue is well explained by Andrew Nacin (WP core developer), here :
http://wordpress.tv/2013/03/15/andrew-nacin-wp_query-wordpress-in-depth/
The SQL statement filters way
Last but not least, this is a more advanced technique offering more flexibility if you need to do complex queries. For example this technique is the best way to build a query that compares dates from custom fields. (I will give an example of this approach soon)
In this example, we filter the where and orderby sql statements. The join statement does not need to be modified here.
add_filter( 'posts_where' , 'posts_where_statement' ); function posts_where_statement( $where ) { //gets the global query var object global $wp_query; //gets the front page id set in options $front_page_id = get_option('page_on_front'); //checks the context before altering the query if ( 'page' != get_option('show_on_front') || $front_page_id != $wp_query->query_vars['page_id'] ) return $where; //changes the where statement $where = " AND wp_posts.ID IN ('{$front_page_id}', [YOUR SECOND PAGE ID] ) AND wp_posts.post_type = 'page' "; //removes the actions hooked on the '__after_loop' (post navigation) remove_all_actions ( '__after_loop'); return $where; } add_filter( 'posts_orderby' , 'posts_orderby_statement' ); function posts_orderby_statement($orderby) { global $wp_query; $front_page_id = get_option('page_on_front'); //checks the context before altering the query if ( 'page' != get_option('show_on_front') || $front_page_id != $wp_query->query_vars['page_id'] ) return $orderby; //changes the orderby statement $orderby = " FIELD( wp_posts.ID, '{$front_page_id}' ,[YOUR SECOND PAGE ID] )"; return $orderby; }
The $wp_the_query way
The following generic code snippet uses a global var called $wp_the_query where WordPress carrefully store the initial main query.
@see core code in wp-settings.
The snippets uses the wordpress template_redirect hook : it’s defined in wp-includes/template-loader.php.
I kind of like it because it is fired once the main query and conditional tags have been fully setup and before the wp_head action.
It’s a really the perfect place to setup hooks that will handle conditions on the main query elements.
//setup hooks in the template_redirect action => once the main WordPress query is set add_action( 'template_redirect', 'hooks_setup' , 20 ); function hooks_setup() { if (! is_home() ) //<= you can also use any conditional tag here return; add_action( '__before_loop' , 'set_my_query' ); add_action( '__after_loop' , 'set_my_query', 100 ); } function set_my_query() { global $wp_query, $wp_the_query; switch ( current_filter() ) { case '__before_loop': //replace the current query by a custom query //Note : the initial query is stored in another global named $wp_the_query $wp_query = new WP_Query( array( 'post_type' => 'post', 'post_status' => 'publish', //others parameters... ) ); break; default: //back to the initial WP query stored in $wp_the_query $wp_query = $wp_the_query; break; } }
These techniques can easily be adapted to many cases : custom post types, taxonomies, custom fields, …
Related resources :
36 thoughts on “Three techniques to alter the query in WordPress”
Great in depth explanation. I’d like to add (for any newbie in the WP world, like I was once) that it’s possible to alter SOME query parameters using query_posts while ALSO preserving the rest of the original query, by doing:
In this case the order parameter was changed… but the rest of the query is preserved.
hi im trying to overide the default page wordpress shows as static. i am using a preg_match to choose what language is displayed.
(i modified my get_locale() for this to work)
when wp loads it always loads the “page_on_front” value from the db . the idea here is to use some hook and tell wp to load an alternate page stored as “page_on_front_en”
I created a file that is “required” at the beginning of functions.php from the code posted above then i improvised .not too sure i am going the right direction but this dosen’t work.
Great articles, but I still have a problem: I can’t search a querytring at same time in TITLE or BODY or POST META.
Looking at generated query I realized the problem, the query is something like
…in order words, it will list posts only if their title and/or body contain my query string.
Any suggestion?
Thanks in advance
Fabrizio
Hi Benny,
To deal with dates in query, you’ll need to use the SQL statement filters way.
There’s also this method : http://kovshenin.com/2012/order-by-post-meta-casted-date-wp_query/
Hope this helps !
Hi thanks for the great article!
I’m trying to alter the home page’s main query to 1) show only CPTs and 2) order them by the event start date, rather than post date. This is what I have so far, but it won’t change the order. Removing the comments breaks all the queries on the page including the main nav. using $query->set() statements for meta_key etc either breaks the main query (no posts) or simply fails to orderby. Any help you can offer is greatly appreciated.
Hi Nicolas,
is there a way to create complex custom SQL JOIN query and get results into the global $post variable?
I can’t use args array for WP_Query since I need to query meta_key from post joining users meta_key and terms taxonomy too.
Many thanks in advance.
Dario.
Hi Dario,
yes you can do that whith the WordPress query filters.
There’s an example of posts_orderby and posts_where implementation in the above snippet.
Hope this helps
Hi, I would like to change the post_type that is handled by my theme and replace post by products
I i use pre_get_post to set the query on products i can’t access my pages or other posts.
what do you recommand?
thanks
Hi,
this is hard to say whithout seeing the actual code you have been writing in functions.php.
I recommend to read the WordPress codex about custom post type first.
Hope this will help!
I have /mypage i want to behave,act, be same style, like my taxonomy product-category/clothing/ but without shortcode , how convert page query to taxonomy query ?
Hi Bogdan,
For tax queries, everything you need to know is here : http://codex.wordpress.org/Class_Reference/WP_Query#Taxonomy_Parameters
Best
Hi Nicolas,
I’m trying to create a simple sort so my visitors can order my custom post types from A-Z and from A-Z.
I thought this was a simple task but wasn’t able to find a solution. I don’t know if you can guide me to the right direction. I understand that I need to use add_query_arg to modify my url and that I need to use pre_get_post to alter the query.
I want the user to click the link on the page to sort the posts alphabetically (title).
Hi Martin,
you could pass the order as an URL parameter and use it to get the order value with $_GET.
Hope this helps!
I’ll try again, I really would like to embed php in my pages 🙂
Hi Jeffrey,
Embedding php in a page is a separate topic than using an externa databasel in WP.
Embedding php : you might want to read those guides to start with WP :
http://codex.wordpress.org/Developer_Documentation
http://www.presscustomizr.com/customizr/how-to-customize-customizr-wordpress-theme/
About using an external DB :
http://codex.wordpress.org/Editing_wp-config.php
http://bavotasan.com/2011/access-another-database-in-wordpress/
Hope this will help!
I have a custom db that has user content that is separate from the WP db. I would like to be able to use php within my pages. For instance,: Hello . Can you point me in a direction?
oops, the the example was this “Hello,
HI!
I would change my website home page. I don’t like the default page because it’s too empty(with only the slider and the circle bottoms). I would add some writing with the news or redo it.
How can I do?
Hi TAC, you could start by playing with the Front page settings in the WordPress customizer. There you can choose to disaply your last blog post a specific page on home, below the slider and the featured pages.
Hope this helps!
Hi Nicolas,
I’m not sure if the question I’m going to ask is related to this post or not, but I haven’t find any better post. I try it anyway, so please forgive me if I’m wrong.
I would like to create a special category template to be used when displaying certain category archive. I’ve been reading some articles at wordpress codex but I feel I can’t use what they say because the file structure in Customizr is a bit different. Could you tell me how to address this situation?
Thank you very much.
Jaume
Hello Nicolas,
I see you’re around here at this moment. 😉
Could you tell me anything about this question I sent you some days ago?
Thank you very much.
Jaume
Hi Jaume,
In Customizr, you can use the WordPress template hierarchy like in any other WordPress themes. I personnaly prefer the “hook” way because I don’t like to repeat myself when coding. :).
To create a special templates for specific category,
1)just follow WordPress guidelines explained here :https://codex.wordpress.org/Template_Hierarchy to name your file
2) copy the entire content of index.php file from Customizr in your category template
3) customize your code.
Another way would be to use the Customizr hook API with conditional tags.
Hope this helps!
Hi- I am also trying something similar. I would like to keep the posts on the front page but also have a page for the blog posts so viewers can see if both places. Any ideas. I’ve created a blog template page but can’t get the sidebars to show up like other single post pages. Any suggestions would be great!
dev.srbenefitsolution.com
In the end I solved it using the “add posts to pages” plugin.
The layout isn’t as nice as the one from the theme itself but it did the trick.
Nevertheless it would be a fine extra for the customizr theme.
Hi,
I’m looking for a way to keep the theme front page and make al the post appear on a static page.
Is there any way to do so ?
thx!
Hi Kenneth, you can do that without adding a single line of code in Customizr.
Open the customizer options, then go to Front Page > Front Page displays and choose a static page (which could be empty if you don’t need any content on home). Then choose the page for your blog posts.
Hope this helps!
Nicolas & Co,
Thank you guys for creating this great theme, Your work and such good support in regards to it is well appreciated! If you ever have those moments (we all do) where your feeling overwhelmed just remember your making an awesome difference to people both newbies and experienced alike,
Thanks Heaps guys/gals! Love your work and much respect from Australia.
-Brandon
Thanks Brandon!
can you help me to get the functions.php right?
I want to show all posts from category 2 on the page with id 662
thx
I am looking for help to build a static page, which contains all posts from a specific category – can you help me?
Hi Viktor, you might want to use the pre_get_posts way to do so.
To set a particular category, use the following code :
you can set several categories id separated by commas, and you can also exclude some categories by prefixing their id with a minus sign.
Hope this will help you
I am afraid, I cannot get it right:
It works for posts but not for pages: what do I have to change for the function alter_query to show posts of a category on a page?
thanx!
how to define it in customize page?