Instantly share code, notes, and snippets.

Embed
What would you like to do?
Building An Advanced WordPress Search With WP_Query https://www.smashingmagazine.com/2016/03/advanced-wordpress-search-with-wp_query/
<?php
/**
* @package Smashing_plugin
* @version 1.0
*/
/*
Plugin Name: Smashing plugin
Plugin URI: https://www.smashingmagazine.com/2016/03/advanced-wordpress-search-with-wp_query/
Description: This is an example plugin for Smashing Magazine readers.
Author: Carlo Daniele
Version: 1.0
Author URI: http://carlodaniele.it/en/
*/
/**
* Plugin setup
* Register post type and shortocode
*/
function sm_setup() {
$labels = array(
'name' => __( 'Accommodation', 'smashing_plugin' ),
'singular_name' => __( 'Accommodation', 'smashing_plugin' ),
'add_new_item' => __( 'Add New Accommodation', 'smashing_plugin' )
);
$args = array(
'labels' => $labels,
'description' => '',
'public' => true,
'show_ui' => true,
'has_archive' => true,
'show_in_menu' => true,
'exclude_from_search' => false,
'capability_type' => 'post',
'map_meta_cap' => true,
'hierarchical' => false,
'rewrite' => array( 'slug' => 'accommodation', 'with_front' => true ),
'query_var' => true,
'menu_icon' => 'dashicons-admin-multisite',
'supports' => array( 'title', 'editor', 'thumbnail', 'author', 'custom-fields' ),
'register_meta_box_cb' => 'sm_add_meta_boxes',
'taxonomies' => array( 'typology' )
);
register_post_type( 'accommodation', $args );
add_shortcode( 'sm_search_form', 'sm_search_form' );
}
add_action( 'init', 'sm_setup' );
/**
* Register custom taxonomy
*/
function sm_register_custom_taxonomies() {
$labels = array(
'name' => __( 'Typologies', 'smashing_plugin' ),
'label' => __( 'Typologies', 'smashing_plugin' ),
'add_new_item' => __( 'Add new Typology', 'smashing_plugin' ),
);
$args = array(
'labels' => $labels,
'hierarchical' => true,
'label' => __( 'Typologies', 'smashing_plugin' ),
'show_ui' => true,
'query_var' => true,
'rewrite' => array( 'slug' => 'typology', 'with_front' => true ),
'show_admin_column' => true,
);
register_taxonomy( 'typology', array( 'accommodation' ), $args );
}
add_action( 'init', 'sm_register_custom_taxonomies' );
/**
* Add custom meta boxes
*/
function sm_add_meta_boxes(){
add_meta_box( 'sm_features_meta_box', __( 'Features', 'smashing_plugin' ), 'sm_build_features_meta_box', 'accommodation', 'side' );
add_meta_box( 'sm_location_meta_box', __( 'Location', 'smashing_plugin' ), 'sm_build_location_meta_box', 'accommodation', 'normal' );
}
/**
* Print custom meta box
*/
function sm_build_features_meta_box( $post ){
// make sure the form request comes from WordPress
wp_nonce_field( basename( __FILE__ ), 'sm_features_meta_box_nonce' );
// retrieve current field values
$type = get_post_meta( $post->ID, '_sm_accommodation_type', true );
$facilities = get_post_meta( $post->ID, '_sm_accommodation_facilities', false );
// an array of default facilities
$available_facilities = array( 'essentials', 'kitchen', 'internet', 'parking', 'tv', 'washer', 'wireless' );
?>
<div class='inside'>
<p><strong>Type of accommodation</strong></p>
<p>
<select name="customfields[type]">
<option value="" <?php selected( $type, "" ); ?>>Select</option>
<option value="entire" <?php selected( $type, 'entire' ); ?>><?php _e( 'Entire house', 'smashing_plugin' ); ?></option>
<option value="private" <?php selected( $type, 'private' ); ?>><?php _e( 'Private room', 'smashing_plugin' ); ?></option>
<option value="shared" <?php selected( $type, 'shared' ); ?>><?php _e( 'Shared room', 'smashing_plugin' ); ?></option>
</select>
</p>
<p><strong><?php _e( 'Facilities', 'smashing_plugin' ); ?></strong></p>
<p>
<?php
foreach ( $available_facilities as $f ) {
if( !isset( $facilities[0][$f] ) ){
$facilities[0][$f] = 0;
}
?>
<input type="checkbox" name="customfields[facilities][<?php echo $f; ?>]" value="<?php echo $f; ?>" <?php checked( $facilities[0][$f], $f ); ?>><?php echo ucfirst($f); ?><br />
<?php
}
?>
</p>
</div>
<?php
}
/**
* Print custom meta box
*/
function sm_build_location_meta_box( $post ){
wp_nonce_field( basename( __FILE__ ), 'sm_location_meta_box_nonce' );
$custom_fields = get_post_custom( $post->ID );
$address = isset( $custom_fields['_sm_accommodation_address'][0] ) ? $custom_fields['_sm_accommodation_address'][0] : '';
$city = isset( $custom_fields['_sm_accommodation_city'][0] ) ? $custom_fields['_sm_accommodation_city'][0] : '';
$country = isset( $custom_fields['_sm_accommodation_country'][0] ) ? $custom_fields['_sm_accommodation_country'][0] : '';
?>
<div class="inside">
<p><strong>Address</strong></p>
<p><input type="text" id="sm_accommodation_address" name="customfields[address]" value="<?php echo esc_attr( $address ); ?>" /></p>
<p><strong>City</strong></p>
<p><input type="text" id="sm_accommodation_city" name="customfields[city]" value="<?php echo esc_attr( $city ); ?>" /></p>
<p><strong>Country</strong></p>
<p><input type="text" id="sm_accommodation_country" name="customfields[country]" value="<?php echo esc_attr( $country ); ?>" /></p>
</div>
<?php
}
/**
* Store custom meta box data
*/
function sm_save_meta_boxes_data( $post_id ){
// verify first meta box nonce
if ( !isset( $_POST['sm_features_meta_box_nonce'] ) || !wp_verify_nonce( $_POST['sm_features_meta_box_nonce'], basename( __FILE__ ) ) ){
return;
}
// verify second meta box nonce
if ( !isset( $_POST['sm_location_meta_box_nonce'] ) || !wp_verify_nonce( $_POST['sm_location_meta_box_nonce'], basename( __FILE__ ) ) ){
return;
}
// return if autosave
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ){
return;
}
// Check the user's permissions.
if ( isset( $_POST['post_type'] ) && 'accommodation' == $_POST['post_type'] ) {
if ( ! current_user_can( 'edit_post', $post_id ) ){
return;
}
}
// custom fields values
$customfields = ( isset( $_POST['customfields'] ) ) ? (array) $_POST['customfields'] : array();
if( count( $customfields ) == 0 ){
return;
}
foreach ($customfields as $key => $value) {
// checks if is bidimensional array
// and sanitize values
if( is_array( $value ) ){
$value = array_map( 'sanitize_text_field', $value );
// if not an array
// sanitize value
}else{
$value = sanitize_text_field( $value );
}
update_post_meta( $post_id, '_sm_accommodation_' . $key, $value );
}
}
add_action( 'save_post', 'sm_save_meta_boxes_data' );
/**
* Register custom query vars
*
* @link https://codex.wordpress.org/Plugin_API/Filter_Reference/query_vars
*/
function sm_register_query_vars( $vars ) {
$vars[] = 'type';
$vars[] = 'city';
return $vars;
}
add_filter( 'query_vars', 'sm_register_query_vars' );
/**
* Build a custom query based on several conditions
* The pre_get_posts action gives developers access to the $query object by reference
* any changes you make to $query are made directly to the original object - no return value is requested
*
* @link https://codex.wordpress.org/Plugin_API/Action_Reference/pre_get_posts
*
*/
function sm_pre_get_posts( $query ) {
// check if the user is requesting an admin page
// or current query is not the main query
if ( is_admin() || ! $query->is_main_query() ){
return;
}
// edit the query only when post type is 'accommodation'
// if it isn't, return
if ( !is_post_type_archive( 'accommodation' ) ){
return;
}
$meta_query = array();
// get query var values
// defaults to empty string
if( !empty( get_query_var( 'city' ) ) ){
$meta_query[] = array( 'key' => '_sm_accommodation_city', 'value' => get_query_var( 'city' ), 'compare' => 'LIKE' );
}
if( !empty( get_query_var( 'type' ) ) ){
$meta_query[] = array( 'key' => '_sm_accommodation_type', 'value' => get_query_var( 'type' ), 'compare' => 'LIKE' );
}
if( count( $meta_query ) > 1 ){
$meta_query['relation'] = 'AND';
}
if( count( $meta_query ) > 0 ){
$query->set( 'meta_query', $meta_query );
}
}
add_action( 'pre_get_posts', 'sm_pre_get_posts', 1 );
/**
* Build search form markup
*/
function sm_search_form( $args ){
// The Query
// meta_query expects nested arrays, even if you only have one query
// to add the category param
$sm_query = new WP_Query( array( 'post_type' => 'accommodation', 'posts_per_page' => '-1', 'meta_query' => array( array( 'key' => '_sm_accommodation_city' ) ) ) );
// The Loop
if ( $sm_query->have_posts() ) {
$cities = array();
while ( $sm_query->have_posts() ) {
$sm_query->the_post();
$city = get_post_meta( get_the_ID(), '_sm_accommodation_city', true );
// populate an array of all occurrences (non duplicated)
if( !in_array( $city, $cities ) ){
$cities[] = $city;
}
}
}
/* Restore original Post Data */
wp_reset_postdata();
if( count($cities) == 0){
return;
}
asort($cities);
$select_city = '<select name="city">';
$select_city .= '<option value="">' . __( 'Select city', 'smashing_plugin' ) . '</option>';
foreach ($cities as $city ) {
$select_city .= '<option value="' . $city . '">' . $city . '</option>';
}
$select_city .= '</select>' . "\n";
reset($cities);
$args = array( 'hide_empty' => false );
$typology_terms = get_terms( 'typology', $args );
if( is_array( $typology_terms ) ){
$select_typology = '<select name="typology">';
$select_typology .= '<option value="" selected="selected">' . __( 'Select typology', 'smashing_plugin' ) . '</option>';
foreach ( $typology_terms as $term ) {
$select_typology .= '<option value="' . $term->slug . '">' . $term->name . '</option>';
}
$select_typology .= '</select>' . "\n";
}
$select_type = '<select name="type">';
$select_type .= '<option value="" selected="selected">' . __( 'Select room type', 'smashing_plugin' ) . '</option>';
$select_type .= '<option value="entire">' . __( 'Entire house', 'smashing_plugin' ) . '</option>';
$select_type .= '<option value="private">' . __( 'Private room', 'smashing_plugin' ) . '</option>';
$select_type .= '<option value="shared">' . __( 'Shared room', 'smashing_plugin' ) . '</option>';
$select_type .= '</select>' . "\n";
$output = '<form id="smform" action="' . esc_url( home_url() ) . '" method="GET" role="search">';
$output .= '<div class="smtextfield">' . '<input type="text" name="s" placeholder="Search key..." value="' . get_search_query() . '" /></div>';
$output .= '<div class="smselectbox">' . $select_city . '</div>';
$output .= '<div class="smselectbox">' . $select_typology . '</div>';
$output .= '<div class="smselectbox">' . $select_type . '</div>';
$output .= '<input type="hidden" name="post_type" value="accommodation" />';
$output .= '<p><input type="submit" value="Go!" class="button" /></p></form>';
return $output;
}
@kr3t3n

This comment has been minimized.

Copy link

kr3t3n commented Feb 19, 2017

Hi, thank you very much for this tutorial! I am trying to apply it on a small website using custom taxonomy with Parent-Child relation and am wondering how to make the input form field to dynamically load CHILD values based on the user selected PARENT. If possible, can you illustrate how would that work?

Thank you very much in advance!

Regards,

Georgi

@MichaelChristman

This comment has been minimized.

Copy link

MichaelChristman commented Mar 7, 2017

Hey Carlo,

First I wanted to say that this article has been extremely helpful. I am curious if there is a simple way to restrict the text search field to search only within the archive page of the custom post type.

From your article I gather the name = "s" attribute of the text input element is where I should look, however I am uncertain what I should change it to, or if this is more than just a simple tweak.

Any help regarding this matter is greatly appreciated.
Cheers,
Michael

edit: I have found a simple solution for this here for those looking to do the same

@carlodaniele

This comment has been minimized.

Copy link
Owner

carlodaniele commented Mar 11, 2017

Hi Georgi,
you may use the get_term_children function. It returns an array of all term children. You could iterate over the elements of the array to build the required options.

Michael,
you cas specifically declare the post type you're searching for posts when you build the query. I used the is_post_type_archive (line 244), but you have many ways to set a specific post type. The article you've highlighted provides a good solution.

@mf-7

This comment has been minimized.

Copy link

mf-7 commented Oct 20, 2018

How do we call the search box in a webpage after we activate the plugin?

Edit:

I belive iv found the solution. Please tell me if this is right:

echo sm_search_form($args);

Iv got the search box with the fields to choose :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment