i'm seeing some strange behaviour I cannot explain inside a category-based template loop.
i have a custom query filter for the category template, preselecting a couple of custom post types to query for:
add_filter( 'pre_get_posts', 'cust_posts_collection' );
function cust_posts_collection( $query ) {
if ( (is_category() && $query->is_main_query()) )
$query->set( 'post_type', array( 'cust_post_type_1', 'cust_post_type1' ) );
return $query;
}
this results in a proper $wp_query object, containing among others an array of posts. let's say for a given category x there are 4 posts. when i var_dump $wp_query i can verify
["posts"]=>&array(4)
and i can see all the posts and their data in the dump.
however, when i loop then over that object:
<?php if ( $wp_query->have_posts() ) while ( $wp_query->have_posts() ) : $wp_query->the_post();
var_dump($post);
endwhile; ?>
all i see is two posts.
how is this possible?
are there any configuration defaults on the loop functions that i am missing?
I was able to solve the bug:
it turns out that there was another loop in the header partial, prior to the loop exposing the bug.
the first loop had a break statement right after an if conditional - the idea: find the first occurrence of a certain custom post type and then break out of the loop.
the problem: this break did not properly reset a global post index variable or something along those lines. the next loop then got a wrong state of the index, causing it to jump as many initial posts as had been looped over in the previous loop.
adding rewind_posts() just before the break fixed this for me.
Related
I have a problem with a website that has been working for YEARS. Suddenly something has changed (no plugins were added).
Basically, I have a "post" page that displays 1 post at the top and then a couple of posts later down the page. The problem I have is that in these secondary posts, after establishing a new wp_query object, the "the_content()" and even trying to manually dump "$second_loop_post->post_content" into an apply_filters function returns nothing. The "the_content()" returns NULL. The kicker? the_title() and every other call works just fine for pulling out information. I can even pull out the unformatted info. But there seems to be something wrong with basic "apply_filters" which is also affecting "the_content()".
See below. Basically, when I load the page and try to do a custom query (even without displaying anything beforehand), it won't work. If I remove the custom $wp_query below however, it will spit out the information for that post page. The problem is I need the second and third queries I do to also display this content information .... and nothing. I'm going crazy. Help please.
global $wp_query;
$original_query = $wp_query;
$wp_query = null;
$wp_query = new WP_Query( array(
'posts_per_page' => 1
) );
if (have_posts()):
while (have_posts() ) : the_post();
the_title();
the_content();
endwhile;
endif;
While wordpress is doing the main loop, how does it determine which post belongs to the current page?
<?php while ( have_posts() ) : the_post(); ?>
<?php
get_template_part( 'template-parts/content', get_post_format() );
?>
<?php endwhile; ?>
In the above example, how does WordPress determine which post will be looped over?
Will the loop go through all the posts?
The main query runs for every page request. The main query makes use of the WP_Query class to fetch posts according to the parameters set via the URL structure of the page. WP_Query works exactly the same in the main query as it is with a custom query, the only diffirence here is the way how paramteres are passed. For the main query, the parameters passed is determined by URL, while for a custom query the parameters are manually set by the user
In short, this is what happens on every single page request made by the browser
The URL is checked and the current permalink structure is checked against the saved structure in the db. If any other permalink structure is in place in stead of the default permalink structure, the current permalink structure is checked against the default structure, and the URL is matched
A set of parameters with values are generated against the URL and permalink structure. Remember, the URL structure is a set of $_GET variables, this $_GET variables is a key/value pair that represents a parameter/value pair in WP_Query. The blank parameters that are still left (there are plenty) after the others have been filled from the URL are filled with default values. Here for instance is the posts per page which is set by get_option( 'posts_per_page' ) which represents the amount of posts set in the back end reading settings.
In short, so far, what the above says is, if you request a category page of your site, the URL is checked and is determined to be a category page. The category is identified and passed to the query as a key/value pair, which will at the end determine that the current page must show posts from this particular category
From all these parameters and their values, the SQL query is build accordingly which will query the database for posts according to the conditions set in the SQL query. The posts are returned and stored as objects
These post objects together with other important information is stored in one big object, the main query object, which is stored in the $wp_query global variable
Up till now, the loop has done absolutely nothing. All of the above happens whether or not you have a loop on the page or not. The loop only does two things
Access the main query object and loops through the post objects. How these objects are send to screen is determined by how the HTML markup, PHP and CSS is constructed inside the loop. The loop uses a while() loop which checks after the completion of one post if there is another post to display. If there is, the while() continues to execute, and it does so till no more posts are left and then kills execution. This is basic PHP, so be sure to check how a while() loop works
The second function the loop does is setting value of the $post global. This is done by the the_post() call inside the loop. On every iteration, the $post global is set to the current post being looped though
This is just a basic overview. Each process is huge and quite difficult to understand, and therefor, one can never address the whole complete process in detail. I hope this helps
I am pretty sure the short answer is, "it's in the query for the current page". The codex discusses the loop and understanding its use:
Using The Loop, WordPress processes each post to be displayed on the current page, and formats it according to how it matches specified criteria within The Loop tags. Any HTML or PHP code in the Loop will be processed on each post.
The Loop
Well I think show the post with this codes
<?php
get_template_part( 'loop', 'index' );
?>
u can use wp_query Object
<?php
// The Query
$args = array (
'post_type' => 'post',
'post_status' => 'publish',
);
$the_query = new WP_Query( $args );
// The Loop
if ( $the_query->have_posts() ) {
echo '<ul>';
while ( $the_query->have_posts() ) {
$the_query->the_post();
echo '<li>' . get_the_title() . '</li>';
}
echo '</ul>';
} else {
// no posts found
}
/* Restore original Post Data */
wp_reset_postdata();
?>
Other Code is here
<?php
if ( have_posts() ) {
while ( have_posts() ) {
the_title();
the_content();
}
}
?>
I am reading that query_posts() should be avoided in favor of wp_query() and pre_get_posts(). I am not confident with messing with the Loop and do not fully understand the codex.
Does the code below use query_posts() ? If yes and since query_posts() should be avoided, can you suggest a method that does not use query_posts() but still accomplish the same thing?
This code in functions.php is used to sort posts by random or by price.
function my_custom_query($query){
if ( $query->is_home() && $query->is_main_query() ) {
$sort= $_GET['sort'];
if($sort == "pricelow"){
$query->set( 'meta_key', 'price' );
$query->set( 'orderby', 'meta_value_num' );
$query->set( 'order', 'ASC' );
}
if($sort == "random"){
$query->set( 'orderby', 'rand' );
}
}
}
add_action( 'pre_get_posts', 'my_custom_query' );
.
Link A (Random) and Link B (Price) are posted in my menu by using this code. Thus the visitor to the website can sort the posts simply by clicking a link.
Random
Price
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
Creating a new WP_Query() object is always fine.
$sort= $_GET['sort'];
if($sort == "pricelow"){
$sort_args = array('meta_key' => 'price', 'orderby' => 'meta_value_num', 'order', 'ASC');
$new_query = new WP_Query($sort_args);
}
blah blah blah...
No no no sorry about that. I didn't see the pre_get_posts hook.
The code in your question is good for hooking queries. As in described in WordPress Plugin API/Action Reference/pre_get_posts:
pre_get_posts runs before WP_Query has been setup.
So it hooks the default WP_Query() where you want (in your code, it changes WP_Query on GET request).
In your template files, use new WP_Query($args).
In category.php, I want to get list of posts in that category.
I found two ways: using get_posts($args) and The Loop.
get_posts() way
$args = array (
"category" => ID );
$posts = get_posts($args);
// then use foreach to loop the $posts
The Loop way
<?php if (have_posts() ): ?>
<?php while (have_posts() ): the_post(); ?>
...
<?php endwhile; ?>
<?php endif; ?>
So which one is more efficient?
From what I have found in searching, get_posts() is for custom templates while The Loop is used inside the template that follows the Wordpress naming convention.
I prefer get_posts(), but if there's a big overhead compared to The Loop, I should re-think it again.
I finally found the answer.
When we open a page using the right template (template that follows the naming convention), Wordpress automatically do query to get all the relevant posts or content
So if I use $posts = get_posts($args);, it means I do additional query which is unnecessary.
$posts = get_posts($args); should only be used outside template for example on sidebar that always be there on every pages.
I am looking for a way to present search results in my custom wordpress theme.
I was hoping being able to present the results like this:
Displaying 4 search results for "test"
Pages
testpage 1
testpage 2
Posts
testpost 1
testpost 2
I wrote a function is_type_page that I can use inside the loop (2 loops), but this breaks the pagination functionality.
Any suggestions how to achieve this?
I would run 2 separate loops on the page, after the first loop for pages run rewind_posts() and then run the loop again. Also the key to pagination is making sure the global $paged variable is being picked up on by both loops. $paged is how wordpress separates posts into pages. i.e. if you go to page 2 of something then the global $paged = 2.
Hope that helps
Multiple loops using rewind_posts here
Running two loops is the way to go if you want them displayed with separate headers. Here is code to get them to show intermingles as they come up by date created...
<?php while (have_posts()) : the_post(); ?>
<?php if ( $post->post_type == 'page' ) { ?>
**DISPLAY PAGE**
<?php } else { ?>
**DISPLAY POST**
<?php else : endif; ?>