Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

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.

share|improve this question
add comment

1 Answer

up vote 2 down vote accepted

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);
    }
}
share|improve this answer
    
Just a heads up - there is a bug in the sample code, the code in the block elseif($clean_to) actually still references _datetime-from and $clean_from. –  Dean Taylor May 19 '12 at 20:38
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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