Query_Posts() Should Be Avoided

query_posts() should be avoided?

I have done a very detailed explanation on this very topic on WPSE, and for the sake of the value and benefit it might have for SO users, here is the complete post copied from that question on WPSE. For interest sake, here is a link to the complete post on WPSE: Some doubts about how the main query and the custom query works in this custom theme?

Your actual question is basically when to run a custom query and when to make use of the main query. Lets break it down in three parts

PART ONE

When to run a custom query (This is not a definitive list)

  • To create custom content sliders

  • To create a featured content area in a page

  • On page.php templates if you need to display posts

  • If you require custom content on a static front page

  • Display related, popular or informational posts

  • Any other secondary or supplementary content outside the scope of the main query

When to make use of the main query.

To display the primary content on

  • On your homepage and the page set as a blogpage in the backend

  • All archive pages which includes templates like archive.php, category.php, author.php, taxonomy.php, tag.php and date.php

PART TWO

To select all the featured posts I use this line that create a new WP_Query object that define a query having the specific tag featured:

So, from what I have understand, this is not the WordPres main query but it is a new query created by me. From what I have understand it is better create a new query (as done) and not use the main query when I want perform this kind of operations

Correct. This falls out of scope for the main query. This is secondary or supplementary content which cannot be created with the main query. You SHOULD ALWAYS use either WP_Query or get_posts to create your custom queries.

NEVER USE query_posts to create custom queries, or even any other query. My emphasis.

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).

Moving on

Ok, going on I show all the posts that have not the featured tag, to do this I use this code snippet that on the contrary modify the main query:

query_posts( array( 'tag__not_in' => array ( $term->term_id )));

So I think that this is pretty horrible. Is it true?

That is all wrong and your statement is unfortunately true. As said before, NEVER use query_posts. It runs a complete new query, which is bad for performance, and it most cases breaks pagination which is an integral part of the main query for pagination to work correctly.

This is your primary content, so you should be using the main query with the default loop, which should look like this, and this is all you need

<?php
if (have_posts()) :
// Start the Loop.
while (have_posts()) : the_post();
get_template_part('content', get_post_format());

endwhile;
else :
// If no content, include the "No posts found" template.
get_template_part('content', 'none');

endif;
?>

You can completely get rid of this part, delete it, burn it and forget about it

<?
// get the term using the slug and the tag taxonomy
$term = get_term_by( 'slug', 'featured', 'post_tag' );
// pass the term_id to tag__not_in
query_posts( array( 'tag__not_in' => array ( $term->term_id )));
?>

OK, once you've done that, you'll see that posts from the feature tag appear in your home page using the main query and default loop.

The correct way of removing this tag from the homepage is with pre_get_posts. This is the proper way to alter the main query and the hook you should always use to make changes to your primary content loop.

So, the code with pre_get_posts is correct and this is the function that you should use. Just one thing, always do a check that you are not on an admin page because pre_get_posts alters the back end as well. So this is the proper code to use in functions.php to remove posts tagged featured from the homepage

function exclude_featured_tag( $query ) {
if ( !is_admin() && $query->is_home() && $query->is_main_query() ) {
$query->set( 'tag__not_in', 'array(ID OF THE FEATURED TAG)' );
}
}
add_action( 'pre_get_posts', 'exclude_featured_tag' );

PART THREE

Extra reading material which will be helpful in future

  • Conditional tags

  • When should you use WP_Query vs query_posts() vs get_posts()?

  • When to use WP_query(), query_posts() and pre_get_posts

  • Query Overview

  • Guidance with The Loop for CMS

In WordPress, when using query_posts(), why does pagination not work?

query_posts() is not recommended for use this way, as it will overwrite the main query, and specific to your case, does not support pagination by default. You should probably use get_posts() or use the WP_Query object.

If you must use query_posts(), then there is an explanation on the link above on how to add the paged parameter to a query.

Here's the same code, but using get_posts().

<?php 
$args = array('numberposts' => 10, 'category' => 2);
$posts = get_posts($args);
foreach($posts as $post) {
// Use the $post object here in your content template
<?php get_template_part('content',get_post_format()); ?>
}
<?php next_posts_link('« Older Posts'); ?>
<?php previous_posts_link('Newer Posts »'); ?>

How can I use query_posts to filter posts from a specific year?

<?php query_posts('cat=3&posts_per_page=10&year=2004'); ?>

pass the year to the query

Show custom fields with query_posts

Custom fields aren't returned as post objects. That is why you don't see them when doing a var_dump() of your query. You have to retrieve them manually unfortunately. You have to make use of get_post_meta to retrieve custom field data for a post

A point of note, never use query_posts. You should rather use WP_Query

WHY SHOULD query_posts NOT BE USED

Further info which you should read - query_posts() should be avoided?

Wordpress query_posts posts_per_page not working

Please try wp_reset_query(); before your code.

// Reset Query
wp_reset_query();
query_posts('posts_per_page=4&post_type=page&pagename=media');

if(have_posts() ) :
while(have_posts()) : the_post();

WordPress - pre_get_posts in place of query_posts on pages

How to use the 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.

Notes

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:

table

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.

query_posts with custom taxonomy

Generally speaking the consensus is to avoid query_posts at all costs. The official documentation even backs this up:

Note: This function will completely override the main query and isn’t intended for use by plugins or themes. Its overly-simplistic approach to modifying the main query can be problematic and should be avoided wherever possible. In most cases, there are better, more performant options for modifying the main query such as via the ‘pre_get_posts’ action within WP_Query.

Instead, get_posts() will do everything that you want it to do. For your case, you just want to add on a tax_query with your information.

$posts = get_posts(
[
'post_type' => 'quotes',
'numberposts' => 10,
'tax_query' => [
[
'taxonomy' => 'quote_category',
'field' => 'name',
'terms' => 'Dogs',
],
],
]
)

The terms parameter also accepts an array if you need multiple:

                'terms' => ['Dogs', 'Cats'],

Edit

If you are using get_template_part then you are probably also using the standard WordPress loop, too. That is actually one of the few times that you could use query_posts but I still personally just don't use it nor recommend it. In an ideal world, you'd probably use a filter just as pre_get_posts but I unfortunately I don't have time to write code to test that. You should be able to use the below code.

This code is an action that calls an anonymous function which adds a feed using another anonymous function as a callback. Your code does the exact same thing, however you are using named functions which is 100% fine, safe and common to do. I just personally prefer to keep my hooks and logic all together and not chase names down. Once again, totally personal preference.

If you still receive an internal error, make sure that both WordPress and PHP debugging is enabled so that you can see what the actual error is, and it is probably a little typo on my part.

add_action(
'init',
static function () {
add_feed(
'quotes',
static function () {

// Override the main query since the render functions wants to use the loop
global $wp_query;
$wp_query = new WP_Query(
[
'post_type' => 'quotes',
'post_count' => 10,
'tax_query' => [
[
'taxonomy' => 'quote_category',
'field' => 'name',
'terms' => 'Dogs',
],
],
]
);

get_template_part('rss', 'quotes');
}
);
}
);

How to make query_posts (tag OR tag) in wordpress?

The comma , delimiter is equivalent to OR in a tag query apparently:

$query = new WP_Query( array( 'tag' => 'bread,baking' ) );
while( $query->have_posts() ) : $query->the_post();
// Do stuff in the loop
endwhile;

More info: https://codex.wordpress.org/Class_Reference/WP_Query#Tag_Parameters



Related Topics



Leave a reply



Submit