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.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 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, useis_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
-
I hope you actually understand what the “main query” is, which basically an instance of the
WP_Queryclass and the instance is accessible via the global$wp_queryvariable. 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 atexample.com/movies(a post type archive page), then WordPress loads thearchive-movies.phptemplate, if it exists. -
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$queryis the global$wp_queryvariable. But then, in your template, the$queryis not the main query and instead it’s a secondary/custom instance ofWP_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.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0