My situation is somewhat complex, I'll try to explain it as succinctly as possible.
I'm currently using query_posts
to modify the main query on custom pages on my site, which as far as I can tell works quite well, though I've read that using query_posts is bad practice for a number of different reasons.
So, why am I using query_posts
and not creating a WP_Query
object you may ask?
It's because I'm using the infinite-scroll plugin, infinite-scroll doesn't play nice with WP_query, but it works absolutely fine when you simply modify the main query with query_posts. For example, pagination doesn't work using infinite scroll + WP_query (main concern).
On one page, I'm modifying the query to get most viewed posts.
<?php $paged = get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1; ?>
<?php query_posts( array( 'meta_key' => 'wpb_post_views_count', 'orderby' => 'meta_value_num', 'order' => 'DESC' , 'paged' => $paged, ) ); ?>
<?php if (have_posts()) : ?>
<?php while ( have_posts() ) : the_post() ?>
<?php if ( has_post_format( 'video' )) {
get_template_part( 'video-post' );
}elseif ( has_post_format( 'image' )) {
get_template_part( 'image-post' );
} else {
get_template_part( 'standard-post' );
}
?>
<?php endwhile;?>
<?php endif; ?>
So after a lot of reading I gather that my other option to modify the main query is using pre_get_posts
, though I'm somewhat unsure as to how to go about this.
Take this for example:-
function textdomain_exclude_category( $query ) {
if ( $query->is_home() && $query->is_main_query() ) {
$query->set( 'cat', '-1,-2' );
}
}
add_action( 'pre_get_posts', 'textdomain_exclude_category' );
Alright, so simple enough - if it's the home page, modify the main query and exclude two categories.
What I'm confused about and can't figure out is:-
the use case scenario for custom page templates. With my query_posts
modification I can just drop in the array before if (have_posts())
, select my page template, publish it and away I go.
With pre_get_posts
I can't figure out how to say for example $query->most-viewed
etc
array( 'meta_key' => 'wpb_post_views_count', 'orderby' => 'meta_value_num', 'order' => 'DESC' , 'paged' => $paged, ) );
How the heck do I do that with pre_get_posts
and make sure it's paginated, ie. works with infinite scroll? In all the examples I've seen with pre_get_posts
there's no arrays.
You can simply add following code in your functions. php file in your WordPress theme directory. function searchfilter($query) { if ($query->is_search && ! is_admin() ) { $query->set('post_type',array('trip')); } return $query; } add_filter('pre_get_posts','searchfilter');
In WordPress, pre_get_posts is an action that makes it possible to modify an existing WP_Query , before that query is actually run. pre_get_posts offers some solutions that are more performant than writing a custom WP_Query , and enables solutions to other problems that would be quite difficult otherwise.
The “main query” is whatever WordPress uses to build the content on the current page. For instance, on my Genesis category archive it's the 10 most recent posts in that category. The first four examples above all require altering the main query.
Place a call to query_posts() in one of your Template files before The Loop begins. The WP_Query object will generate a new SQL query using your parameters. When you do this, WordPress ignores the other parameters it receives via the URL (such as page number or category).
pre_get_posts
hook to display list of posts on a page, through a custom page template?I've been playing with the pre_get_posts
hook and here's one idea
Step #1:
Ceate a page called for example Show with the slug:
example.com/show
Step #2:
Create a custom page template:
tpl_show.php
located in the current theme directory.
Step #3:
We construct the following pre_get_posts
action callback:
function b2e_pre_get_posts( $query )
{
$target_page = 'show'; // EDIT to your needs
if ( ! is_admin() // front-end only
&& $query->is_main_query() // main query only
&& $target_page === $query->get( 'pagename' ) // matching pagename only
) {
// modify query_vars:
$query->set( 'post_type', 'post' ); // override 'post_type'
$query->set( 'pagename', null ); // override 'pagename'
$query->set( 'posts_per_page', 10 );
$query->set( 'meta_key', 'wpb_post_views_count' );
$query->set( 'orderby', 'meta_value_num' );
$query->set( 'order', 'DESC' );
// Support for paging
$query->is_singular = 0;
// custom page template
add_filter( 'template_include', 'b2e_template_include', 99 );
}
}
add_action( 'pre_get_posts', 'b2e_pre_get_posts' );
where
function b2e_template_include( $template )
{
$target_tpl = 'tpl_show.php'; // EDIT to your needs
remove_filter( 'template_include', 'b2e_template_include', 99 );
$new_template = locate_template( array( $target_tpl ) );
if ( ! empty( $new_template ) )
$template = $new_template; ;
return $template;
}
This should also give us pagination:
example.com/show/page/2
example.com/show/page/3
etc.
I updated the answer and removed the query-object part modification, based on the suggestion from @PieterGoosen, since it could e.g. break the breadcrumbs on his setup.
Also removed the is_page()
check within the pre_get_posts
hook, since it might still give some irregularities in some cases. The reason is that the query-object is not always available. This is being worked on, see e.g. #27015. There are workarounds possible if we want to use the is_page()
or is_front_page()
.
I constructed the following table, just to get a better overview of some of the properties and query varaiables of the main WP_Query
object, for a given slug:
It's interesting to note that the pagination in WP_Query
depends on the nopaging
not being set and the current page not being singular (from the 4.4 source):
// Paging
if ( empty($q['nopaging']) && !$this->is_singular ) {
$page = absint($q['paged']);
if ( !$page )
$page = 1;
// If 'offset' is provided, it takes precedence over 'paged'.
if ( isset( $q['offset'] ) && is_numeric( $q['offset'] ) ) {
$q['offset'] = absint( $q['offset'] );
$pgstrt = $q['offset'] . ', ';
} else {
$pgstrt = absint( ( $page - 1 ) * $q['posts_per_page'] ) . ', ';
}
$limits = 'LIMIT ' . $pgstrt . $q['posts_per_page'];
}
where we can see that the LIMIT
part of the generated SQL query is within the conditional check. This explains why we modify the is_singular
property above.
We could have used other filter/hooks, but here we used pre_get_posts
as mentioned by the OP.
Hope this help.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With