0

I've been writing a custom search.php loop for some time now and I'm running into a weird problem. My php argument is outputting some unexpected results by repeating the template file.

Currently I have 56 posts total in my custom post type products

my $args array is configured as follows;

$search_query = get_search_query(); 

if ( get_query_var('paged') ) {
    $paged = get_query_var('paged');
} elseif ( get_query_var('page') ) { 
    $paged = get_query_var('page');
} else {
    $paged = 1;
}

$args = array(
    'post_type'         => 'products',
    'post_status'       => 'published',
    's'                 => $search_query,
    'paged'             => $paged,
    'posts_per_page'    => 15
);

// DO NOT REMOVE
global $query_string;
wp_parse_str( $query_string, $search_query );
$search = new WP_Query( $args );

My Loop is configured as follows;

<?php $i = 1; 

    if ( $search->have_posts() ) : 

        while ( $search->have_posts() ) : 

            $search->the_post();
            get_template_part( 'loop-templates/content-catalogue', get_post_format() ); 

            if($i % 3 == 0) {echo '<!-- Open a new deck --></div><div class="card-deck d-block d-lg-flex card-lg-gutters-3" data-aos="fade-up" data-aos-delay="50"><!-- Open a new deck -->';} $i++;

        endwhile; ?>

   <?php else : ?>

        <?php get_template_part( 'loop-templates/content', 'none' ); ?>

   <?php endif; ?>

   <?php understrap_pagination(); ?>
   <?php wp_reset_postdata(); ?>

My issue:

I should only have 4 pages in pagination. In reality I have 12 for some reason or another and each of the pages after the forth page is displaying my template file for no posts found. I.e.

<?php get_template_part( 'loop-templates/content', 'none' ); ?>

I'm stumped; and can't quite put my finger on what is wrong.

See my full search.php file.

1 Answer 1

2

This is another example of what is by far the most common mistake I've seen in WordPress development. I don't think it's even close to whatever the next most common is. I'm very curious as to what documentation or tutorials are out there that are teaching this approach to queries, or what could be done to the official documentation to make this clearer.

The problem is essentially that you're using a pagination function, understrap_pagination() which is (correctly) using the status of the main query (the global $wp_query object) to determine the pages to show, but your template is using your own custom query ($search) to output the posts. If these queries have mismatched arguments, a different number posts per page, or a differing current page number, then you're going to get weirdness like this.

All the main WordPress templates, like index.php, archive.php, search.php, have already queried the correct posts for you, based on the URL request, and made them accessible via the main loop:

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

endwhile; endif;

(Notice the lack of any object variable being used here)

And functions like pagination and next/previous links are all built to use this query to produce accurate output. If you use a separate query or modify this query incorrectly then those functions that depend on the state of the main query are going to give what looks like incorrect output. Pagination is the most common of these.

Your search.php loop needs to look like this:

$i = 1; 

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

        get_template_part( 'loop-templates/content-catalogue', get_post_format() ); 

        if( $i % 3 == 0 ) {
            echo '<!-- Open a new deck --></div><div class="card-deck d-block d-lg-flex card-lg-gutters-3" data-aos="fade-up" data-aos-delay="50"><!-- Open a new deck -->'; 
        }

        $i++;

    endwhile;
else : 
    get_template_part( 'loop-templates/content', 'none' );
endif;

understrap_pagination();
wp_reset_postdata();

Note that $search isn't used anywhere. This is because you must use the main query loop.

This brings us to the issue of why $search exists to begin with. From what I can tell from your full search.php template, you have 2 search forms: a "global" search, and a "catalogue" search, and you're using the $search query when the user performs a catalogue search. Creating a separate query is not the correct way to do this. For one thing, it means that regardless of which search the user performed you're performing both searches, which is ineffecient. Secondly, it's causing you this pagination issue.

So what you need to do is make both the catalogue and global versions of your template use the same main loop, as I described above. Then you need to use the pre_get_posts hook to modify the main query based on which query the user performed. From what I can tell the main thing you do is change the number of posts per page, and limit the post type to products. You can do this with this code in your theme's functions.php file:

function wpse_341991_catalogue_search( $query ) {
    if ( $query->is_search() ) {
        if ( 'catalogueSearch' === $_GET['formName'] ) {
            $query->set( 'post_type', 'products' );
            $query->set( 'posts_per_page', 15 );
        }
    }
}
add_action( 'pre_get_posts', 'wpse_341991_catalogue_search' );

Now whenever a user searches with your catalogue search form, the main query will show you the results you want, and the pagination functions should function like normal.

One last thing though. post_type is already a valid query argument that you can include in a search form. So if you want to change the post type searched by a form, you can just add a hidden field with the name set to post_type and the value set to the post type that you're searching:

<input type="hidden" name="post_type" value="products">

This means you omit your formName input, and reduce the pre_get_posts function to:

function wpse_341991_catalogue_search( $query ) {
    if ( $query->is_search() && 'products' === $query->get( 'post_type' ) ) {
        $query->set( 'posts_per_page', 15 );
    }
}
add_action( 'pre_get_posts', 'wpse_341991_catalogue_search' );

And in your template, rather than checking:

if ( strcmp( $formIdentifier, 'globalSearch' )) {

You can check:

if ( 'products' !== get_query_var( 'post_type' ) ) {
5
  • 1
    Comprehensively!
    – Max Yudin
    Commented Jul 2, 2019 at 13:54
  • Interesting read, I'll work on putting it to practice. That being said, I think the wp codex needs a few updates then as I built this page using primarily using the codex as a guide. :/
    – Lewis
    Commented Jul 2, 2019 at 13:58
  • 1
    The codex has been essentially deprecated in favour of developer.wordpress.org, however as far as I'm aware neither suggests a using the custom query as a replacement for the main query or to modify the main query. So while there maybe be documentation on how to use custom queries (which have legitimate use cases), they never suggest using anything other than the main loop in theme templates, though it's probably never discouraged either. Commented Jul 2, 2019 at 13:59
  • 1
    Precisely, if you tell a student "here's how to build a custom loop", and provide no context into use case or scenario for search. By default it's assumed it's ok. but I get your point.
    – Lewis
    Commented Jul 2, 2019 at 14:05
  • Thanks for assist :)
    – Lewis
    Commented Jul 2, 2019 at 14:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.