I'm querying posts using the following wp query:
function custom_retrieve_posts_function() {
$paged = get_query_var( "paged" ) ? get_query_var( "paged" ) : 1;
$args = array(
'post_type' => 'my_post_type',
'posts_per_page' => 7,
'paged' => $paged
);
$query = new \WP_Query( $args );
$output = "";
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
// Iterate next post of query result
$query->the_post();
$output .= get_the_content();
}
// Only display arrows if links have been obtained, so do it in this way
$previous_link = get_previous_posts_link(
esc_html__( 'Zurück', 'custom-text-domain' )
);
$next_link = get_next_posts_link(
esc_html__( 'Weiter', 'custom-text-domain' ),
$query->max_num_pages
);
$nav_links = "<div id=\"navigation-links\">".
"<span id=\"previous-link\">".
( $previous_link ? "⇦ {$previous_link}" : "" ).
"</span>".
"<span id=\"next-link\">".
( $next_link ? "{$next_link} ⇨" : "" ).
"</span>".
"</div>";
$output .= $nav_links;
// Reset the wp_query loop
wp_reset_postdata();
} else {
$output = "<p class=\"no-results\">Oops!</p>";
}
echo $output;
}
Problem is that certain posts of page n are seemingly randomly repeated on page(s) m, with m > n.
The amount / limit of posts to be shown in feeds and blogs is 7, I've set that in the wp admin options. Still, same problem persists.
Any idea why this is happening?
UPDATE
The function custom_retrieve_posts_function()
(defined in a namespace within a class etc., but all of this is omitted here for simplicity) is called within a PHP script bound to the AJAX hook responsible for executing a custom filtered query via ajax.
So the script is called like this:
Main Plugin File holds this:
add_action(
'wp_ajax_myplugin_search_custom',
function() {
require MYPLUGIN_AJAX_DIR.'/custom_search.php';
wp_die();
}
);
add_action(
'wp_ajax_nopriv_myplugin_search_custom',
function() {
require MYPLUGIN_AJAX_DIR.'/custom_search.php';
wp_die();
}
);
With the content of AJAX_DIR.'/custom_search.php'
being (again simplified, all naming conflicts are 100% avoided, so all classnames / namespaces are again omitted):
check_ajax_referer( 'ajax-nonce-content' );
require MYPLUGIN.'/custom_search_function.php';
custom_retrieve_posts_function();
I then also have a page template defined in my themes/mycustomtheme/templates
directory, and within that page template, I simply call
require MYPLUGIN.'/custom_search_function.php';
custom_retrieve_posts_function();
Note that all of this works perfectly, expect from the problem that already displayed posts are re-displayed on subsequently paginated pages.
UPDATE 2
Sadly, I'm not able to make it working after making the commented adaptations via js (additionally pass the number of the page of the pagination to query). Like when I do this query via AJAX:
$args = array(
'post_type' => 'my_post_type',
'tax_query' => $tax_query_array,
'orderby' => 'meta_value_num',
'meta_key' => 'my_metas_key',
'order' => 'ASC',
'posts_per_page' => 7,
'paged' => 2
);
I still get one of the posts already shown on page 1 (a random one).
Same if I add $args['offset'] = 7
and / or $args['page'] = 2
, so I'm confused; what am I still not properly understanding here??
I'm querying posts using the following wp query:
function custom_retrieve_posts_function() {
$paged = get_query_var( "paged" ) ? get_query_var( "paged" ) : 1;
$args = array(
'post_type' => 'my_post_type',
'posts_per_page' => 7,
'paged' => $paged
);
$query = new \WP_Query( $args );
$output = "";
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
// Iterate next post of query result
$query->the_post();
$output .= get_the_content();
}
// Only display arrows if links have been obtained, so do it in this way
$previous_link = get_previous_posts_link(
esc_html__( 'Zurück', 'custom-text-domain' )
);
$next_link = get_next_posts_link(
esc_html__( 'Weiter', 'custom-text-domain' ),
$query->max_num_pages
);
$nav_links = "<div id=\"navigation-links\">".
"<span id=\"previous-link\">".
( $previous_link ? "⇦ {$previous_link}" : "" ).
"</span>".
"<span id=\"next-link\">".
( $next_link ? "{$next_link} ⇨" : "" ).
"</span>".
"</div>";
$output .= $nav_links;
// Reset the wp_query loop
wp_reset_postdata();
} else {
$output = "<p class=\"no-results\">Oops!</p>";
}
echo $output;
}
Problem is that certain posts of page n are seemingly randomly repeated on page(s) m, with m > n.
The amount / limit of posts to be shown in feeds and blogs is 7, I've set that in the wp admin options. Still, same problem persists.
Any idea why this is happening?
UPDATE
The function custom_retrieve_posts_function()
(defined in a namespace within a class etc., but all of this is omitted here for simplicity) is called within a PHP script bound to the AJAX hook responsible for executing a custom filtered query via ajax.
So the script is called like this:
Main Plugin File holds this:
add_action(
'wp_ajax_myplugin_search_custom',
function() {
require MYPLUGIN_AJAX_DIR.'/custom_search.php';
wp_die();
}
);
add_action(
'wp_ajax_nopriv_myplugin_search_custom',
function() {
require MYPLUGIN_AJAX_DIR.'/custom_search.php';
wp_die();
}
);
With the content of AJAX_DIR.'/custom_search.php'
being (again simplified, all naming conflicts are 100% avoided, so all classnames / namespaces are again omitted):
check_ajax_referer( 'ajax-nonce-content' );
require MYPLUGIN.'/custom_search_function.php';
custom_retrieve_posts_function();
I then also have a page template defined in my themes/mycustomtheme/templates
directory, and within that page template, I simply call
require MYPLUGIN.'/custom_search_function.php';
custom_retrieve_posts_function();
Note that all of this works perfectly, expect from the problem that already displayed posts are re-displayed on subsequently paginated pages.
UPDATE 2
Sadly, I'm not able to make it working after making the commented adaptations via js (additionally pass the number of the page of the pagination to query). Like when I do this query via AJAX:
$args = array(
'post_type' => 'my_post_type',
'tax_query' => $tax_query_array,
'orderby' => 'meta_value_num',
'meta_key' => 'my_metas_key',
'order' => 'ASC',
'posts_per_page' => 7,
'paged' => 2
);
I still get one of the posts already shown on page 1 (a random one).
Same if I add $args['offset'] = 7
and / or $args['page'] = 2
, so I'm confused; what am I still not properly understanding here??
2 Answers
Reset to default 3Because of this line:
$paged = get_query_var( "paged" ) ? get_query_var( "paged" ) : 1;
This is inside a function that is used on both an AJAX handler, and on a page request. get_query_var
pulls the parameters from the main query ( aka the primary WP_Query
that powers functions such as the_post
, have_posts()
etc ). But this will never work in an AJAX request.
For this reason, when you request the next page from AJAX it will always result in page 1
. If you want something other than page 1 then you need to include the number of the page you want in the javascript that sends the AJAX request, pass it along, then read it and put it in the WP_Query
parameters directly. Even if there was a main query it would not be the same main query.
Remember, every request to the server loads WordPress from a blank slate, AJAX request handlers share nothing with the page that your browser has open unless you explicitly send them the data you want to share along with the request as POST/GET variables. There is no shared pagination state behind the scenes or magic that glues it together.
Additionally, because you included the pagination HTML in the AJAX response you'll now have a next page link that leads to page 2, but triggers AJAX that loads page 1 ( why would it know to load page 2? get_query_var
won't work, there is no main query to get a query var from! ).
What's more, I have a strong suspicion that you're either unaware of archive-mycustompost.php
, the pre_get_posts
filter, or the ability to change the URL of the CPT archive when registering the CPT, or that query parameters for the main query come from the URL and you can add extra parameters e.g. example/?s=test&posts_per_page=7&paged=2
gets the second page of a search query for "Test" that has 7 posts per page. In the majority of cases a secondary query and a custom page template are completely unnecessary.
Okay finally got this working. For reasons I will not detail here, I preferred a pagination solution which uses ONE single callback which I use everywhere where I need the pagination. In other words, I call the callback call on both upon page load of the template pages needing the query AND as well when click on a pagination link OR applying any search filter to query for the posts.
In cases which do not require you to do this, of course you can refer to the discussion among @Tom J Nowell and me (in his answer above, thx again!). If possible with your design, you thus may use the WP REST API (instead of using the AP AJAX API) to create the request, an archive page + WP's main query loop + the related pagination features associated to that main query.
If you prefer to create a WP pagination solution which uses exclusively WP AJAX via $_POST
, you can easily do this by providing the additional number of the page you're requesting to your ajax handler via JS, and simply use 1 if none is provided. So my function above became:
function custom_retrieve_posts_function( $paged = 1 ) {
if ( isset($_POST['page_to_request'] ) ) {
$paged = intval( sanitize_text_field( isset( $_POST['page_to_request'] ) ) );
}
$args = array(
'post_type' => 'my_custom_post_type',
'tax_query' => $tax_query_array,
'orderby' => array(
'meta_value_num' => 'ASC',
'date' => 'DESC'
),
'meta_key' => 'my_custom_meta_key',
'posts_per_page' => 7,
'paged' => $paged
);
$query = new \WP_Query( $args );
$output = "";
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
// Iterate next post of query result
$query->the_post();
$output .= get_the_content();
}
// Only display arrows if links have been obtained, so do it in this way
$max_num_pages = $query->max_num_pages;
$nav_links = "<div id=\"navigation-links\">".
// If we're not on the first page, add a link allowing to go back to
// the one previous to the one currently requested
"<span id=\"previous-link\"".(
( $max_num_pages !== 1 ) ?
" data-previous-page='".( $paged - 1 )."'>⇦ Back" :
">"
).
"</span>".
// If we're not on the last page, add a link allowing to go forward to
// the page subsequent to the one currently requested
"<span id=\"next-link\"".(
( $max_num_pages > $paged ) ?
" data-next-page='".( $paged + 1 )."'>Next ⇨" :
">"
).
"</span>".
"</div>";
$output .= $nav_links;
// Reset the wp_query loop
wp_reset_postdata();
} else {
$output = "<p class=\"no-results\">Oops!</p>";
}
echo $output;
}
Note that I've added date
into my order
clause, additionally to what's been specified in my question above. This is because I've found out that page n of your pagination may show posts of previous pages m where m < n, if you have many posts which have the same meta value. Even using the offset parameter of the wp query etc. will not eliminate this problem. So I thought of first using the ordering I need in my case (the meta key), and then subsequently according to another value which is unique to posts, such as the posts' date. My thought was that like this I can assure that the posts will always be retrieved in exactly the same order, even if their meta value is the same. And well, to have the same order is crucial for proper pagination, no matter the context in which the function is called.
As I imagine, WP transforms this WP_Query array into a SQL query to query for the according data. So, when using a data field which is unique to a post, but not a date but sth like a primary key in the DB, this may result in faster query times. Hence, instead of the date
order key, you may use ID
.
pre_get_posts
to change its parameters? Pagination functions in WordPress all work off of the main query by default. Also if the main query has 4 pages but your query has 5, then the 5th page may not work as you expected because there is not 5th page on the main query. You might find it 404's instead. Also what file is this in? Is it a page template? Are you trying to build a CPT archive? What is it trying to implement? – Tom J Nowell ♦ Commented Jan 7, 2022 at 18:47main
and not-main
, seemingly), but thought, the docs say you should use theWP_Query
class for page templates, so I'll most likely also use it in ajax calls..? – DevelJoe Commented Jan 7, 2022 at 18:57the_post
orhave_posts
etc refer to, much like there's a current post. If your query is inside an AJAX callback that's super important information, AJAX requests do not have pagination, you have to provide it. It looks like you have not provided the full code, you'll need to edit your question to include this. Don't try to be helpful and hide things you think aren't relevant, that's probably were the error was – Tom J Nowell ♦ Commented Jan 7, 2022 at 19:21