1

I am designing my first theme and I am stuck on the search functionalities of a Custom Post Type. I created a CPT 'movies' and created the archive page with the 'archive-movies.php' file in the root of the theme folder. Inside I created a basic WP_Query

get_header();

// WP_Query arguments
$args = array(
    'post_type'     => array( 'movies' ),
    'post_status'   => array( 'publish' ),
    'order'         => 'DESC',
    'orderby'       => 'date',
);

// The Query
$query = new WP_Query( $args ); ?>

<div class="row gx-5">

<?php // The Loop
if ( $query->have_posts() ) : ?>
    
    <?php while ( $query->have_posts() ) : ?>
        <?php $query->the_post();
        
        // CODE

    <?php endwhile; ?>
<?php else : ?>
    <div class="col"><h2>Nessun film inserito</h2></div>
<?php endif ?>

</div>

<?php // Restore original Post Data
wp_reset_postdata();

get_footer();

and the movies are displayed correctly. I have created a search form in a file named 'search-form.php' which is loaded via get_template_part() in the header.php file cause the form must always be visible on all pages.

<?php
$clienti = get_terms( array(
    'taxonomy'          => 'clients',
    'hide_empty'        => false,
) );
if(!empty($clienti)) {
    $clients .= '<option value="">-- Qualsiasi --</option>';
    foreach($clienti as $cliente) {
        $clients .= '<option value="'.$cliente->term_id.'">'.$cliente->name.'</option>';
    }
} else {
    $clients .= 'Nessun cliente presente nel database';
}
?>
<div id="search-form">
    <form method="get" class="row g-3" action="<?php echo get_post_type_archive_link('movies'); ?>" role="search">
        <div class="col">
            <div class="form-floating">
                <input type="text" class="form-control" id="keyword" name="keyword" placeholder="Nome progetto">
                <label for="keyword">Nome progetto</label>
            </div>
        </div>
        <div class="col">
            <div class="form-floating">
                <select class="form-select" id="clients_list" name="clients_list" aria-label="Seleziona cliente">
                    <?php echo $clients; ?>
                </select>
                <label for="clients_list">Seleziona cliente</label>
            </div>
        </div>
        <div class="col">
            <div class="form-floating">
                <select class="form-select" id="status_list" name="status_list" aria-label="Seleziona cliente">
                    <option value="">-- Qualsiasi --</option>
                    <option value="aperto">Aperto</option>
                    <option value="chiuso">Chiuso</option>
                    <option value="pagato">Pagato</option>
                </select>
                <label for="status_list">Seleziona stato</label>
            </div>
        </div>
        <div class="col">
            <div class="form-floating">
                <select class="form-select" id="expired_date" name="expired_date" aria-label="Scadenza film">
                    <option value="">-- Qualsiasi --</option>
                    <option value="scadenza">In scadenza</option>
                    <option value="scaduto">Scaduto</option>
                </select>
                <label for="expired_date">Scadenza film</label>
            </div>
        </div>
        <div class="col">
            <div class="form-floating">
                <select class="form-select" id="expired_production" name="expired_production" aria-label="Scadenza produzione">
                    <option value="">-- Qualsiasi --</option>
                    <option value="scadenza">In scadenza</option>
                    <option value="scaduto">Scaduto</option>
                </select>
                <label for="expired_production">Scadenza produzione</label>
            </div>
        </div>
        <div class="col">
            <button type="submit" class="btn btn-outline-primary btn-lg">Cerca</button>
        </div>
    </form>
</div>

In the theme functions.php file instead I inserted this code trying to use the action pre_get_posts to manipulate the WP_Query of the movies archive, but whatever I try doesn't work, the main Query is not modified, the CPT showed are always the same.

/*=================================================
CUSTOM QUERY
=================================================== */
function custom_search_query_vars_filter( $vars ) {

    $vars[] .= 'keyword';
    $vars[] .= 'clients_list';
    $vars[] .= 'status_list';
    $vars[] .= 'expired_date';
    $vars[] .= 'expired_production';
    return $vars;
}
add_filter( 'query_vars', 'custom_search_query_vars_filter' );

/*=================================================
SEARCH QUERY
=================================================== */
function movies_search( $query ) {
    if ( is_archive('movies') && $query->is_main_query() && !is_admin() ) {

        $today = date('Y-m-d');
        $lastweek = date('Y-m-d', strtotime('-7 days'));

        $keyword = get_query_var( 'keyword', FALSE );
        $clients_list = get_query_var( 'clients_list', FALSE );
        $status_list = get_query_var( 'status_list', FALSE);
        $expired_date = get_query_var( 'expired_date', FALSE);
        $expired_production = get_query_var( 'expired_production', FALSE);

        // Keywords query
        $keyword ? $keyword : $keyword = null;

        $query->set('s', $keyword);

        // Custom fields query
        $meta_query_array = array('relation' => 'AND');

        $status_list ? array_push($meta_query_array, array('key' => 'status_job', 'value' => '"' . $status_list . '"', 'compare' => 'LIKE') ) : null ;

        if($expired_date== 'scadenza' ) {
            array_push($meta_query_array, array('key' => 'expired_date', 'value' => array($today, $lastweek), 'compare' => 'BETWEEN', 'type' => 'DATE'));
        } elseif($expired_date== 'scaduto') {
            array_push($meta_query_array, array('key' => 'expired_date', 'value' => $lastweek, 'compare' => '<', 'type' => 'DATE'));
        } else {
            null;
        }

        if($expired_production == 'scadenza' ) {
            array_push($meta_query_array, array('key' => 'expired_production', 'value' => array($today, $lastweek), 'compare' => 'BETWEEN', 'type' => 'DATE'));
        } elseif($expired_production == 'scaduto') {
            array_push($meta_query_array, array('key' => 'expired_production', 'value' => $lastweek, 'compare' => '<', 'type' => 'DATE'));
        } else {
            null;
        }

        $query->set( 'meta_query', $meta_query_array );
        
        // Taxonomies query
        $tax_query_array = array('relation' => 'OR');

        $clients_list ? array_push($tax_query_array, array('taxonomy' => 'clients', 'field' => 'term_id', 'terms' => $clients_list) ) : null ;

        $query->set( 'tax_query', $tax_query_array);
    }
}
add_action( 'pre_get_posts', 'movies_search' );

Even if I put a general rule on all Queries, it always seems to be ignored.

/*=================================================
SEARCH QUERY OVERRIDE
=================================================== */
function movies_search( $query ) {
    if ( $query->is_main_query() && !is_admin() ) {

        $query->set( 's', 'Name specific movie' );

    }
}
add_action( 'pre_get_posts', 'movies_search' );

I don't think it's a compatibility problem as there are no plugins installed and in the theme I'm developing there are no other functions than using the pre_get_posts action. Thanks so much.

1 Answer 1

1

First off, as documented:

is_archive() does not accept any parameters. If you want to check if this is the archive of a custom post type, use is_post_type_archive( $post_type )

So in your code, use is_post_type_archive( 'movies' ) instead and not is_archive('movies').

Now as for the main question:

the main Query is not modified, the CPT showed are always the same

  1. I hope you actually understand what the "main query" is, which basically an instance of the WP_Query class and the instance is accessible via the global $wp_query variable. WordPress runs the main query on page load, before the page template is loaded, so the main query determines what template should be used. So for example, if you're at example.com/movies (a post type archive page), then WordPress loads the archive-movies.php template, if it exists.

  2. I believe the main query is actually being modified, but you never displayed the posts in that query and instead, you created a custom query and then displayed the posts in that query (the while ( $query->have_posts() ) ... part), which returned the same posts regardless of the filters in your search form.

    And why so, is because the $query->is_main_query() check (which is good) in your function causes the search filters to be applied only on the main query, i.e. when the $query is the global $wp_query variable. But then, in your template, the $query is not the main query and instead it's a secondary/custom instance of WP_Query.

Therefore, you should instead use the main loop like so: (this is a super simple example; see The Loop in the theme developer handbook for more details about the loop)

// In your archive-movies.php file:

while ( have_posts() ) {
    the_post();
    the_title( '<h2>', '</h2>' );
}

And remove your custom query/loop (including the wp_reset_postdata(); part).

And keep in mind that the main query already retrieved the correct post(s) from the database, hence you should just display those posts, but if for example on an archive page you want to change the number of posts per page, and/or to include posts from certain categories only, then use the pre_get_posts hook to alter the main query (arguments) instead of re-querying the database via a new WP_Query instance.

2
  • Thanks for the help! I think I will have to learn more about how Wordpress queries work, as I thought that pre_get_posts it could also work with a custom query. Leaving the default query of the archive page, now pre_get_posts it works perfectly.
    – Wolftrick
    Jul 25, 2021 at 10:01
  • pre_get_posts does indeed work with custom queries, because the main and secondary/custom queries are using the same WP_Query class. But anyway, see the 8th paragraph (that begins with "And why so,") and the last paragraph in my revised answer. :)
    – Sally CJ
    Jul 25, 2021 at 14:23

Your Answer

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

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