The right way to create a custom search page for complex custom post types

The question:

I am trying to create a custom search page to show only custom posts types that meet specific search criteria. I have a complex form that involves different selects, radio buttons and text input fields. I am comfortable using the WP_Query class to query for the posts that are identified via the search form, but I’m not sure if I should create additional templates or use the built in search page.

Let me give you a for instance to help communicate my conundrum. Imagine you have the CPT “Person”. For each person, you have “Birth Date”, “Gender”, “Height”, and “Weight”. You want to search for all males born after 1972, who are taller than 6 feet tall. How would you work out this search? Again, I can easily put together $args for the WP_Query class to get these posts. I need to know the best way to integrate this with WordPress.

Ultimately, I need a solution the satisfies the following:

1) Preserves pagination on the search page
2) Only searches for the specific CPT, not other CPTs

And, I have the following questions

1) Should I have a separate template for this, or can I integrate this directly with search.php? My current solution feels hacky and I would like to develop an alternative. I have created a new Page in the WP admin. I use this page’s URL as the action attribute in the search form. I then use a custom template to display results of that page. This seems wrong to me.

2) If I integrate this with search.php how do I preserve the regular search results, in addition to having these lovely custom search results?

Thank you so much for your help!

EDIT: On further investigation, I’m beginning to think that pre_get_posts will likely be involved in a “better way” to do this.

The Solutions:

Below are the methods you can try. The first solution is probably the best. Try others if the first one doesn’t work. Senior developers aren’t just copying/pasting – they read the methods carefully & apply them wisely to each case.

Method 1

I found a way to make this work. I was right with the thought that pre_get_posts was the way to go. First, I added an action:

// Filter the search page
add_filter('pre_get_posts', 'search_pre_get_posts');

Then I added the function. This function first checks that the context is correct. In my custom search form, I use a nonce. The nonce helps me verify that it’s my custom search that is being run, not the built in search. I then manipulate the query variables that will be sent to the query. I’m showing my full function here, which includes all of the validation and sanitization of variables, as well as how the query is manipulated:

/**
 * Filters for search of events
 *
 * When a search is initiated, this filter will check to see if it is an
 * event search. If so, it will process the search data associated with
 * event searches and add it to the $query object prior to it being used
 * to query the database.
 *
 * @param $query The query object generated for this page
 * @return void
 */
function search_pre_get_posts($query)
{
    // Verify that we are on the search page that that this came from the event search form
    if($query->query_vars['s'] != '' && is_search() && isset($_GET['prefix-submitted']) && $_GET['prefix-submitted'] && wp_verify_nonce($_GET['prefix-search-nonce'], 'prefix-search'))
    {
        // Always order by date
        $query->set('orderby', 'meta_value');
        $query->set('order', 'ASC');
        $query->set('meta_key', '_datetime-from');

        // Set the text string for the search
        $clean_text = (isset($_GET['prefix-text'])) ? wp_strip_all_tags($_GET['prefix-text']) : '';
        $query->set('s', $clean_text);

        // Validate dates
        $clean_from = (isset($_GET['prefix-from'])) ? $this->handle_date($_GET['prefix-from']) : false;
        $clean_to = (isset($_GET['prefix-to'])) ? $this->handle_date($_GET['prefix-to']) : false;

        // Validate taxonomies
        $clean_type = (isset($_GET['prefix-type'])) ? $this->handle_taxonomy_id($_GET['prefix-type'], 'prefix-type') : false;
        $clean_location = (isset($_GET['prefix-location'])) ? $this->handle_taxonomy_id($_GET['prefix-location'], 'prefix-location') : false;

        // Build meta_query variable based on date pieces
        if($clean_from && $clean_to)
        {
            $meta_query = array(
                array(
                    'key' => '_datetime-from',
                    'value' => array($clean_from, $clean_to),
                    'compare' => 'BETWEEN',
                    'type' => 'DATE'
                )
            );
        }
        elseif($clean_from)
        {
            $meta_query = array(
                array(
                    'key' => '_datetime-from',
                    'value' => $clean_from,
                    'compare' => '>=',
                    'type' => 'DATE'
                )
            );
        }
        elseif($clean_to)
        {
            $meta_query = array(
                array(
                    'key' => '_datetime-from',
                    'value' => $clean_from,
                    'compare' => '>=',
                    'type' => 'DATE'
                )
            );
        }
        else
            $meta_query = '';

        // Set the meta query
        $query->set('meta_query', $meta_query);

        // Build tax query based on taxonomy pieces
        if($clean_type && $clean_location)
        {
            $tax_query = array(
                'relation' => 'AND',
                array(
                    'taxonomy' => 'prefix-type',
                    'field' => 'id',
                    'terms' => $clean_type,
                    'operator' => 'IN'
                ),
                array(
                    'taxonomy' => 'prefix-location',
                    'field' => 'id',
                    'terms' => $clean_location,
                    'operator' => 'IN'
                )
            );
        }
        elseif($clean_type)
        {
            $tax_query = array(
                array(
                    'taxonomy' => 'prefix-type',
                    'field' => 'id',
                    'terms' => $clean_type,
                    'operator' => 'IN'
                )
            );
        }
        elseif($clean_location)
        {
            $tax_query = array(
                array(
                    'taxonomy' => 'prefix-location',
                    'field' => 'id',
                    'terms' => $clean_location,
                    'operator' => 'IN'
                )
            );
        }
        else
            $tax_query = '';

        // Set the tax query
        $query->set('tax_query', $tax_query);
    }
}


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

Leave a Comment