-
-
Save carlodaniele/fd5847f4baf42ca84771273ce21771fd to your computer and use it in GitHub Desktop.
<?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; | |
} |
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
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.
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 :)
Hello, thank you for the article. I will show how this will ultimately.
My example loop code is below:
<?php
query_posts( array( 'post_type' => 'ilanlar') );
if ( have_posts() ) : while ( have_posts() ) : the_post();
?>
<div class="row item col-xs-12 col-sm-6 col-md-6 col-lg-4 col-xl-4">
<div class="mdc-card property-item grid-item column-3">
<div class="thumbnail-section">
<div class="row property-status">
</div>
<div class="property-image">
<div class="swiper-container">
<div class="swiper-wrapper">
<div class="swiper-slide">
<?php if ( has_post_thumbnail()) : ?>
<img src="<?php echo get_the_post_thumbnail_url();?>" alt="<?php the_title(); ?>" data-src="<?php echo get_the_post_thumbnail_url();?>" class="slide-item swiper-lazy">
<?php else: ?>
<?php endif;?>
<div class="swiper-lazy-preloader"></div>
</div>
</div>
</div>
</div>
</div>
<div class="property-content-wrapper">
<div class="property-content">
<div class="content">
<h1 class="title"><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>
<p class="row address flex-nowrap">
<i class="material-icons text-muted">location_on</i>
<span><?php echo get_post_meta(get_the_ID(), "menkuladres_ilanadresi", true); ?></span>
</p>
<div class="row between-xs middle-xs">
<h3 class="primary-color price">
<span><?php echo get_post_meta(get_the_ID(), "menkul_ilanfiyati", true); ?> TL</span>
</h3>
</div>
<div class="d-none d-md-flex d-lg-flex d-xl-flex">
<div class="description mt-3">
<p><?php the_excerpt(); ?></p>
</div>
</div>
<div class="features mt-3">
<p><span>İlan No</span><span><?php the_ID(); ?></span></p>
<p><span>İlan Durumu</span><span><?php the_terms( $post->ID, 'ilandurumu'); ?></span></p>
<p><span>Konut Tipi</span><span><?php the_terms( $post->ID, 'ilankonuttipi'); ?></span></p>
<p><span>M2</span><span><?php echo get_post_meta(get_the_ID(), "menkul_m2net", true); ?></span></p>
<p><span>Oda Sayısı</span><span><?php echo get_post_meta(get_the_ID(), "menkul_odasayisi", true); ?></span></p>
</div>
</div>
<div class="grow"></div>
<div class="actions row between-xs middle-xs">
<p class="row date mb-0">
<i class="material-icons text-muted">date_range</i>
<span class="mx-2"><?php the_time('j F, Y'); ?></span>
</p>
<a href="<?php the_permalink(); ?>" class="mdc-button mdc-button--outlined">
<span class="mdc-button__ripple"></span>
<span class="mdc-button__label">DETAYLAR</span>
</a>
</div>
</div>
</div>
</div>
</div>
<?php endwhile; endif; wp_reset_query(); ?>
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