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 :

  1. the pre_get_posts way
  2. the query_posts way
  3. the SQL statement filters way
  4. New : the wp_the_query way

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.

//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;
	}
}

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.
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;
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *