55

I have a search field for the blog posts, but I need an other for a custom post type. How can I create this custom search form with a different search result layout?

1
  • On a WooCommerce (product post type) search case, just copy the woocommerce/templates/archive-product.php file to your child theme and then customize it... 1hour for this!! :(
    – gtamborero
    Commented Dec 2, 2020 at 11:19

8 Answers 8

78

Here is what I've tried and got a solution with 3 steps. Let's say your custom post type is "products"

1 . Add Function Code here you can specify the archive-search.php

function template_chooser($template)   
{    
  global $wp_query;   
  $post_type = get_query_var('post_type');   
  if( $wp_query->is_search && $post_type == 'products' )   
  {
    return locate_template('archive-search.php');  //  redirect to archive-search.php
  }   
  return $template;   
}
add_filter('template_include', 'template_chooser');    

2 . Create search result template for custom post type ( archive-search.php )

        <?php
        /* Template Name: Custom Search */        
        get_header(); ?>             
        <div class="contentarea">
            <div id="content" class="content_right">  
                     <h3>Search Result for : <?php echo htmlentities($s, ENT_QUOTES, 'UTF-8'); ?> </h3>       
                     <?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>    
                <div id="post-<?php the_ID(); ?>" class="posts">        
                     <article>        
                    <h4><a href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"><?php the_title(); ?></a></h4>        
                    <p><?php the_excerpt(); ?></p>        
                    <p align="right"><a href="<?php the_permalink(); ?>">Read     More</a></p>    
                    <span class="post-meta"> Post By <?php the_author(); ?>    
                     | Date : <?php echo date('j F Y'); ?></span>    

                    </article><!-- #post -->    
                </div>
        <?php endwhile; ?>
    <?php endif; ?>




           </div><!-- content -->    
        </div><!-- contentarea -->   
        <?php get_sidebar(); ?>
        <?php get_footer(); ?>
 
  1. Build Search Form
    In this Search Form, the value "products" is hidden and it will search only product posts.

     <div>   
        <h3>Search Products</h3>
        <form role="search" action="<?php echo site_url('/'); ?>" method="get" id="searchform">
        <input type="text" name="s" placeholder="Search Products"/>
        <input type="hidden" name="post_type" value="products" /> <!-- // hidden 'products' value -->
        <input type="submit" alt="Search" value="Search" />
      </form>
     </div>
    

for more, I would like to link you to here
http://www.wpbeginner.com/wp-tutorials/how-to-create-advanced-search-form-in-wordpress-for-custom-post-types/

7
  • Tip: when registering the post type, the publicly_queryable argument must be set to true. If not, the get_query_var('post_type') will never return the post_type value given in the url argument. codex.wordpress.org/Function_Reference/… Commented May 21, 2015 at 17:46
  • Another tip/suggested edit: get_query_var('post_type') returned an array (rather than a string) so couldn't be compared directly. Since I'm only searching one post type at a time, I simply changed my $post_type var to $post_type[0].
    – indextwo
    Commented Apr 8, 2016 at 15:51
  • is there a way to rewrite the url from http://localhost:3000/?s=cloud%27&post_type=product to http://localhost:3000/search/cloud/product
    – YarGnawh
    Commented Nov 6, 2017 at 16:33
  • @YarGnawh Sorry for late response, check this out wordpress.stackexchange.com/questions/15418/…. There is a plugin called rewrite too wordpress.org/plugins/rewrite
    – Ronald
    Commented Nov 13, 2017 at 11:20
  • 3
    For an answer being written 9 years ago, I can confirm in 2022 that it still works and with great accuracy! Excellent work @Ronald you are amazing! Commented Mar 5, 2022 at 10:24
9

Here is what works for me. Not as clean but I couldn't get any of these other answers to work.

Search form for Custom Post Type:

<form role="search" method="get" class="search-form" action="<?php echo home_url( '/' ); ?>">
    <label>
        <span class="screen-reader-text"><?php echo _x( 'Search for:', 'label' ) ?></span>
        <input type="search" class="search-field" placeholder="<?php echo esc_attr_x( 'Search …', 'placeholder' ) ?>" value="<?php echo get_search_query() ?>" name="s" title="<?php echo esc_attr_x( 'Search for:', 'label' ) ?>" />
        <input type="hidden" name="post_type" value="book" />
    </label>
    <input type="submit" class="search-submit" value="<?php echo esc_attr_x( 'Search', 'submit button' ) ?>" />
</form>

In functions.php:

function searchfilter($query) {
    if ($query->is_search && !is_admin() ) {
        if(isset($_GET['post_type'])) {
            $type = $_GET['post_type'];
                if($type == 'book') {
                    $query->set('post_type',array('book'));
                }
        }       
    }
return $query;
}
add_filter('pre_get_posts','searchfilter');

In search.php:

<?php if (have_posts()) : ?>
<?php while (have_posts()) : the_post(); ?>
    <?php if(isset($_GET['post_type'])) {
        $type = $_GET['post_type'];
           if($type == 'book') {?>

               /* Format for "book" custom post type */

           <?php } else { ?>

               /* Format for custom post types that are not "book,"
               or you can use elseif to specify a second post type the
               same way as above. Copy the default format here if you
               only have one custom post type. */

           <?php } ?>
    <?php } else { ?>

              /* Format to display when the post_type parameter
              is not set (i.e. default format) */
<?php } ?>
<?php endwhile; else: ?>

/* What to display if there are no results. */

<?php endif; ?>

Naturally in all three places you'll need to replace "book" with your custom post type.

Hope this helps someone!

3

A short code more actualized

 function template_chooser($template)   
{    
  global $wp_query; 
  $post_type = $wp_query->query_vars["pagename"];   
  if( isset($_GET['s']) && $post_type == 'products' )   
  {

    return locate_template('archive-search.php');  //  redirect to archive-search.php
  }   
  return $template;   
}
add_filter('template_include', 'template_chooser'); 
3

I was looking to use two different forms for my normal searches and my searches on a custom post type.

My custom post type uses a different header than normal pages, on my normal page, the call to my search form is:

<?php get_search_form(true); ?>

And the call to my search form in the custom post type header is:

<?php get_template_part('search','library'); ?>

Which has an additional field:

<input type="hidden" name="post_type" value="library" /> //Where "library" is my custom post type.

In the functions file I have the following code that you have provided.

/** Custom Search for Library */
function search_library($template)   
{    
  global $wp_query;   
  $post_type = get_query_var('post_type');   
  if( $wp_query->is_search && $post_type == 'library' )   
  {
    return locate_template('search-library.php');  //  redirect to archive-search.php
  }   
  return $template;   
}
add_filter('template_include', 'search_library');

Which detects if the search form is doing a search within custom fields, thus showing the search in a custom template, otherwise use the normal template.

Edit: fixed the get_search_form() function call which would have returned true no matter what.

1
  • 2
    Worth noting, but get_search_form('true') should be get_search_form(true). get_search_form is looking for a boolean input, so either true or false. By wrapping it in quotes you are feeding it a string, not a boolean parameter. The way that function is set up, both 'true' and 'false' would return the same result, because they are both non-empty strings (which causes the function to return true in both cases).
    – Mike
    Commented Feb 22, 2018 at 1:29
2

To fix the empty input search issue you can substitute the function code with this:

function template_chooser($template)   
{    
 global $wp_query;   
 $post_type = get_query_var('post_type');   
 if( isset($_GET['s']) && $post_type == 'products' )   
 {
  return locate_template('archive-search.php');  //  redirect to archive-search.php
 }   
 return $template;   
}
add_filter('template_include', 'template_chooser');
1
  • 3
    Would be great if you explain how your code works, an reveal your source of the code Commented Sep 16, 2014 at 11:00
1

I know this is an old thread, but I use custom shortcodes in my functions.php file to enable searches tied to specific post types and in my case custom taxonomies. I use Pods for a video lecture archive by year, and wanted searches to be within that year only. This is currently working (in my functions.php):

/*Custom search for Video Archives Year 2020 - adds shortcode [2020search] for the search*/
function video2020searchform( $form ) {

    $form = '<form role="search" method="get" id="searchform" action="' . home_url( '/' ) . '" >
    <div class="my-custom-search"><label class="screen-reader-text" for="s">' . __('Search for:') . '</label>
    <input type="hidden" name="video_year" value="video-archive-2020" />
    <input type="text" value="' . get_search_query() . '" name="s" id="s" placeholder="Search 2020 archives by speaker or keyword..." />
    <input type="submit" id="searchsubmit" value="'. esc_attr__('Search') .'" />
    </div>
    </form>';

    return $form;
}

add_shortcode('2020search', 'video2020searchform');
// end of 2020 search form.   

I then have a page with the highlight videos from 2020, with the search form shortcode above it so users can search the rest of 2020. name="video_year" is the custom taxonomy created by Pods, the post type is also created by Pods and is called "videos", and value="video-archive-2020" is the slug from the Pods video year I need. If you had a custom post type of "books", a taxonomy of "book-genre" and you are trying to restrict the search to "adventure", "book-genre" would be the 'name' entry and "adventure" would be the 'value'. Hope this helps someone, and thank you to all the above answers!

1

I have 10 CPTs each with it's own search result page (different layouts each) and using this was not working for me.

After more digging, I found this this approach where you condition the search.php template to load another template if there is a CPT in the URL.

basically in search.php you insert this:

<?php

// check to see if there is a post type in the URL + nonce for security
if ( isset( $_GET['post_type'], $_GET['custom_search_nonce_field'] ) && wp_verify_nonce( $_GET['custom_search_nonce_field'], 'custom_search_nonce_action' ) ) {

// save it for later
$post_type = $_GET['post_type'];

// check to see if a search template exists
if ( locate_template( 'search-' . $post_type . '.php' ) ) {
    // load it and exit
    get_template_part( 'search', $post_type );
    exit;
}

}

?>

and the search form would be something like this:

<form class="search" action="<?php echo home_url( '/' ); ?>">
  <input type="search" name="s" placeholder="Search&hellip;">
  <input type="submit" value="Search">
  <input type="hidden" name="post_type" value="kb_article">
<?php wp_nonce_field( 'custom_search_nonce_action', 'custom_search_nonce_field' ); ?>
</form>
2
  • Should there be a nonce check here?
    – Adrian B
    Commented Oct 4, 2023 at 11:23
  • 1
    yes, especially on a public form, but it depends a lot on the setup; I added in this example hoping it will make people more conscious.
    – RwkY
    Commented Feb 23 at 17:32
0

In my case, the search was working perfectly. I just needed to filter out the query to search only for Learndash Courses post type. Here's what I did:

// Filters the search query to only include 'sfwd-courses' post type
function fn_search_courses_only($query) {
    if( $query->is_search && ! is_admin() ) {
        $query->set( 'post_type', 'sfwd-courses' );
    }
}
add_action( 'pre_get_posts', 'fn_search_courses_only' );

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.