最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

wp query - Create ONE callback for all page templates, post filter queries + paginated pages, triggering pagination via AJAX

programmeradmin2浏览0评论

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 ? "&#8678 {$previous_link}" : "" ).
    "</span>".

    "<span id=\"next-link\">".
    ( $next_link ? "{$next_link} &#8680" : "" ).
    "</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 ? "&#8678 {$previous_link}" : "" ).
    "</span>".

    "<span id=\"next-link\">".
    ( $next_link ? "{$next_link} &#8680" : "" ).
    "</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??

Share Improve this question edited Jan 9, 2022 at 17:25 DevelJoe asked Jan 7, 2022 at 18:37 DevelJoeDevelJoe 5376 silver badges21 bronze badges 6
  • 1 is there a reason you're using a secondary custom query instead of modifying the main query with 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:47
  • This loop is called in a callback which is called from the frontend via AJAX (correctly setup wp-ajax) request to query for specific posts using values provided via the frontend. Does that help you? I'm unaware about the fact that WP has different kinds of queries (main and not-main, seemingly), but thought, the docs say you should use the WP_Query class for page templates, so I'll most likely also use it in ajax calls..? – DevelJoe Commented Jan 7, 2022 at 18:57
  • To be precise, I use this callback both on a page template (I simply call the callback from within the page template) and an ajax request, fired from the frontend via that same page to do the above-mentioned. – DevelJoe Commented Jan 7, 2022 at 19:13
  • Ah! So there is no main query because AJAX. The main query is the query that was created using the parameters generated by the URL + rewrite rules, and is what functions such as the_post or have_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
  • Also note that the first paragraph of the doc you linked to is mainly talking about how it breaks pagination, and how it is not the best way to modify the default query. – Tom J Nowell Commented Jan 7, 2022 at 19:22
 |  Show 1 more comment

2 Answers 2

Reset to default 3

Because 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 )."'>&#8678 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 &#8680" :
      ">"
    ).
    "</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.

发布评论

评论列表(0)

  1. 暂无评论