In the first post of this series, we knew about the application of custom fields which allows users to add arbitrary information into posts. But what do you do after that? In the recent post, you also learned how to add custom fields programmatically and display them in your theme. But you’ll see a shortcoming if there is no classification and searching. This post will guide you on how to get posts by custom fields and we’ll create an interesting application: advanced search.

If you have accessed some websites having searching function for hotel rooms or flights, you can see that the search function gives many different choices. That is called “advanced search”.

Unlike WordPress’s default search function which allows users to search text based on the title and content only, advanced search allows you to search based on various criteria. In WordPress, we implement these criteria with the help of custom fields.

We will use the plugin in the previous post and modify directly the Twenty Seventeen theme.

Tip: skip to the end of this post to watch the video tutorial if you prefer watching to reading.

Getting posts

Before learning how to get posts by custom fields, you have to know how to get posts. There are two ideal ways to get posts:

  1. get_posts: This function simply creates an instance of WP_Query then returns the an array of post objects.
  2. Using the class WP_Query: This way, you will have an instance of WP_Query and you can perform the query yourself. You can also have access to public methods and properties.

In this example, we’ll use WP_Query. To know the parameters which constructor method of WP_Query accepts, please see here.

For example: the following snippet gets 2 random posts from the website:

$args = array(
    'orderby' => 'rand',
    'posts_per_page' => '2',
);
$query = new WP_Query( $args );

Getting posts based on custom fields

WP_Query accepts a parameter $meta_query. This one will be used to create the instance of class WP_Meta_Query. Its purpose is generating SQL code to query posts based on metadata.

This parameter is an array of meta conditions. Each condition is an array where you need to set the meta key, meta value and the comparison. If the posts have meta values that satisfy the conditions, then they will be returned.

For example: To find posts that have the value of field hcf_price lower than 100, use the following code:

$args = array(
    'meta_query' => array(
        array(
            'key' => 'hcf_price',
            'value' => '100',
            'compare' => '<',
            'type' => 'NUMERIC',
        ),
    ),
);
$query = new WP_Query( $args );

WP_Meta_Query

WP_Meta_Query is a helper that allows primary query classes, such as WP_Query and WP_User_Query to filter their results by object metadata, by generating JOIN and WHERE subclauses to be attached to the primary SQL query string.

Constructor method of class WP_Meta_Query accepts the below parameters:

  1. relation: Optional. The MySQL keyword used to join the clauses of the query. Accepts ‘AND’, or ‘OR’. Default ‘AND’.
  2. A no named array: Optional. An array of first-order clause parameters, or another fully-formed meta query.
    1. key: Meta key to filter by.
    2. value: Meta value to filter by.
    3. compare: MySQL operator used for comparing the value. Accepts ‘=’, ‘!=’, ‘>’, ‘>=’, ‘<‘, ‘<=’, ‘LIKE’, ‘NOT LIKE’, ‘IN’, ‘NOT IN’, ‘BETWEEN’, ‘NOT BETWEEN’, ‘REGEXP’, ‘NOT REGEXP’, ‘RLIKE’, ‘EXISTS’ or ‘NOT EXISTS’. Default is ‘IN’ when value is an array, ‘=’ otherwise.
    4. type: MySQL data type that the meta_value column will be CAST to for comparisons. Accepts ‘NUMERIC’, ‘BINARY’, ‘CHAR’, ‘DATE’, ‘DATETIME’, ‘DECIMAL’, ‘SIGNED’, ‘TIME’, or ‘UNSIGNED’. Default is ‘CHAR’.

Writing an advanced search

Firstly, you need to set up a plugin which we did in the previous post.

Now, we have 2 methods to implement advanced search:

  1. Hook into filter pre_get_posts to modify the parameters of the main query which WordPress uses to query the result of the search page. This method is encouraged. There is only one query and all plugins needing to change the list of posts will hook into it. So its compatibility is good. However, the result may not meet your desire because every plugin can hook into it and change the result.
  2. Creating a page template and use a WP_Query to query posts and display them as the search result. This method is visual and easy to imagine how to use WP_Query and custom field. Besides, you have total right of controlling query as well as the returns.

In this post, we choose the second method. This one helps us understand vividly about the query and the way of interacting with custom fields when querying.

Create a page template

Create a file advanced-search.php in the folder wp-content/themes/twentyseventeen/page-templates with the below content:

<?php
/**
 * Template Name: Advanced Search
 */

After that, go to Pages → Add New and add 1 new page with page template is Advanced Search as in the below picture:

Create Advanced Search template by custom fields
Create Advanced Search page template for custom search result filtered by custom fields

Now, we display search form in that page template. Edit the code of that page as follows:

<?php
/**
 * Template Name: Advanced Search
 */

get_header(); ?>

    <div class="wrap">
        <div id="primary" class="content-area">
            <main id="main" class="site-main" role="main">
                <form class="advanced-search-form">
                    <div class="form-group">
                        <label>Min Price</label>
                        <input type="number" name="min-price">
                    </div>
                    <div class="form-group">
                        <label>Max Price</label>
                        <input type="number" name="max-price">
                    </div>
                    <div class="form-group">
                        <input type="submit" value="Search">
                    </div>
                </form>
            </main><!-- #main -->
        </div><!-- #primary -->
    </div><!-- .wrap -->
<?php get_footer();

Refresh the page and you will see that search form displayed as below:

Display custom fields of Advanced Search in the theme
Display the search form with min price and max price fields

Search by custom fields and display the result

Although search form is available, you have not received the right result. And there’s nothing happen when you search with filled values of min price and max price. The reason is that we have not processed the query to take products which have the price in that range.

To get the values of min/max prices, use this code:

<?php
$min_price = $_GET['min_price'] ?: '';
$max_price = $_GET['max_price'] ?: '';

Now, edit fields in the form as below:

Field min_price:

<input type="number" name="min_price" value="<?php echo esc_attr( $min_price ); ?>">

Field max_price:

<input type="number" name="max_price" value="<?php echo esc_attr( $max_price ); ?>">

Now, you need this code to display the search result:

<?php if ( $min_price || $max_price ): ?>
    <div class="search-result">
        <?php
        $args = [
            'posts_per_page' => - 1,
            'meta_query' => []
        ];
        if ( $min_price ) {
            $args['meta_query'][] = [
                'key' => 'hcf_price',
                'value' => $min_price,
                'compare' => '>=',
                'type' => 'NUMERIC'
            ];
        }
        if ( $max_price ) {
            $args['meta_query'][] = [
                'key' => 'hcf_price',
                'value' => $max_price,
                'compare' => '<=',
                'type' => 'NUMERIC'
            ];
        }
        $search_query = new WP_Query( $args );
        if ( $search_query->have_posts() ):
            while ( $search_query->have_posts() ) {
                $search_query->the_post();
                get_template_part( 'template-parts/post/content', 'excerpt' );
            }
            wp_reset_postdata();
            ?>
        <?php else: ?>
            <p>No result found.</p>
        <?php endif; ?>
    </div>
<?php endif; ?>

Pay attention that there is a part of building parameters for query $args based on the value of min price and max price in the above code. We use comparison function <=, >= to ensure that the price of products is in the selected pricing range.

The following screenshot is a result of clicking Search button: displaying only products with prices from 1 to 300:

Search result with keeping the values of searching custom fields
Searching by custom fields

Besides, there is not only comparison conditions <=, >= but also many different ones such as =, != and IN. You can combine them with custom fields to build more complex queries. E.g finding products that have green color, production year >= 2012, etc. See more about these conditions here.

You can download full code of the file advanced-search.php here.

Video tutorial

You can also watch the full video tutorial of this post here:

Conclusion

Each query by custom field will create a JOIN statement in SQL. So, if you have tens of custom fields, the performance of your code will decrease significantly. Therefore, if you see the query speed is too low, you optimize or cache that custom fields. Moreover, you should limit the number of custom fields queried.

Pay attention that custom fields are not created for querying. The main purpose of custom fields is to add data for the post. If you query so much, please consider using custom taxonomy instead.

We have learned how to create, display and query posts based on the custom fields. Now, you have all the necessary knowledge to build anything interesting for your personal blog, please embark on doing this. If you have any questions, please comment at below.


Also published on Medium.

Leave a Reply