I have two arrays that have a parent category and sub category each appear in a select list, how do I make the sub category show items only from its parent category?
Always wanted to do an exercise on chained selections using Ajax, so, here we go ;)
This is a full plugin and should be installed in wp-content/plugins/your-plugin-name
folder. Consists of three files, the plugin itself, the Javascript file and the Ajax loader image.
Install the plugin and activate, and insert the following in some theme template file:
<?php
if( class_exists( 'BRSFL_Chained_Selection' ) ) {
// Parameters: ( $cat_id, $dropdown_text )
BRSFL_Chained_Selection::print_cats( 1, 'All Makes' );
}
?>
Also, adjust the two calls to wp_dropdown_categories
as desired. Check the code comments for details.
The sub-categories dropdown is modified in response to changes in the categories dropdown:
<?php
/**
* Plugin Name: Chained Categories
* Plugin URI: http://stackoverflow.com/q/15748968/1287812
* Description: Demonstration of chained categories with Ajax.
* Plugin structure based on <a href="https://gist.github.com/3804204">Plugin Class Demo</a>, by Thomas Scholz.
* Use the dropdowns in the theme with this PHP method call: BRSFL_Chained_Selection::print_cats();
* Author: Rodolfo Buaiz
* Author URI: http://wordpress.stackexchange.com/users/12615/brasofilo
*/
add_action(
'plugins_loaded',
array ( BRSFL_Chained_Selection::get_instance(), 'plugin_setup' )
);
class BRSFL_Chained_Selection
{
/**
* Plugin instance.
*
* @see get_instance()
* @type object
*/
protected static $instance = NULL;
/**
* URL to this plugin's directory.
*
* @type string
*/
public $plugin_url = '';
/**
* Path to this plugin's directory.
*
* @type string
*/
public $plugin_path = '';
/**
* Access this plugin’s working instance
*
* @wp-hook plugins_loaded
* @since 2012.09.13
* @return object of this class
*/
public static function get_instance()
{
NULL === self::$instance and self::$instance = new self;
return self::$instance;
}
/**
* Used for regular plugin work.
*
* @wp-hook plugins_loaded
* @since 2012.09.10
* @return void
*/
public function plugin_setup()
{
$this->plugin_url = plugins_url( '/', __FILE__ );
$this->plugin_path = plugin_dir_path( __FILE__ );
$this->load_language( 'chainedselections' );
add_action( 'wp_enqueue_scripts', array( $this, 'script_enqueuer' ) );
add_action( 'wp_ajax_custom_query', array( $this, 'custom_query' ) );
add_action( 'wp_ajax_nopriv_custom_query', array( $this, 'custom_query' ) );
}
/**
* Constructor. Intentionally left empty and public.
*
* @see plugin_setup()
* @since 2012.09.12
*/
public function __construct() {}
/**
* Enqueue frontend scripts
*/
public function script_enqueuer()
{
wp_register_script(
'ajax-quote'
, plugin_dir_url( __FILE__ ) . '/ajax.js'
, array( 'jquery' )
);
wp_enqueue_script( 'ajax-quote' );
wp_localize_script(
'ajax-quote'
, 'wp_ajax'
, array(
'ajaxurl' => admin_url( 'admin-ajax.php' )
, 'ajaxnonce' => wp_create_nonce( 'ajax_chained_selection_validate' )
, 'icon' => plugin_dir_url( __FILE__ ) . '/ajax-loader.gif'
)
);
}
/**
* Ajax create sub-categories dropdown
*/
public function custom_query()
{
// Security
check_ajax_referer( 'ajax_chained_selection_validate', 'security' );
// Check if jQuery posted the data
if( !isset( $_POST[ 'chained_subcat_id' ] ) )
return false;
// Adjust parameters
$carMakes = array(
'show_option_all' => '',
'show_option_none' => 'All ' . $_POST[ 'chained_subcat_name' ],
'orderby' => 'ID',
'order' => 'ASC',
'show_count' => 0,
'hide_empty' => 0,
'exclude' => 0,
'echo' => 1,
'selected' => 0,
'child_of' => $_POST[ 'chained_subcat_id' ],
'hierarchical' => 1,
'name' => 'chained-subcontainer',
'id' => '',
'class' => 'postform',
'depth' => 1,
'tab_index' => 0,
'taxonomy' => 'category',
'hide_if_empty' => false
);
// Print sub-categories
wp_dropdown_categories( $carMakes );
exit();
}
/**
* Loads translation file.
*
* Accessible to other classes to load different language files (admin and
* front-end for example).
*
* @wp-hook init
* @param string $domain
* @since 2012.09.11
* @return void
*/
public function load_language( $domain )
{
$locale = apply_filters( 'plugin_locale', get_locale(), $domain );
// Load translation from wp-content/languages if exist
load_textdomain(
$domain, WP_LANG_DIR . $domain . '-' . $locale . '.mo'
);
// Load regular plugin translation
load_plugin_textdomain(
$domain,
FALSE,
$this->plugin_path . '/languages'
);
}
/**
* Print the dropdown in the frontend
*/
public static function print_cats( $cat_id, $dropdown_text )
{
// Adjust parameters
$carMakes = array(
'show_option_all' => '',
'show_option_none' => $dropdown_text,
'orderby' => 'ID',
'order' => 'ASC',
'show_count' => 0,
'hide_empty' => 0,
'exclude' => 0,
'echo' => 1,
'selected' => 0,
'child_of' => $cat_id,
'hierarchical' => 1,
'name' => 'chained-categories',
'id' => '',
'class' => 'postform',
'depth' => 1,
'tab_index' => 0,
'taxonomy' => 'category',
'hide_if_empty' => false
);
// Print categories
wp_dropdown_categories( $carMakes );
// Empty dropdown for sub-categories
echo '<div id="chained-subcontainer">
<select name="chained-subcategories" id="chained-subcategories">
<option value="">- Select a category first -</option>
</select>
</div>';
}
}
jQuery( document ).ready( function( $ )
{
var data = {
action: 'custom_query',
security: wp_ajax.ajaxnonce
};
$( "#chained-categories" ).on( "change", function( e )
{
// Add specific data to the variable, used to query the sub-categories
data[ 'chained_subcat_id' ] = $( this ).val();
data[ 'chained_subcat_name' ] = $(
'#chained-categories option[value='
+ $( this ).val()
+ ']'
).text();
// A sub-category was selected
if( $( this ).val() > 0 )
{
// Ajax loader icon
$( '#chained-subcontainer' ).html( '<img src="' + wp_ajax.icon + '">' );
// Ajax call
$.post(
wp_ajax.ajaxurl,
data,
// No error checking is being done with the response
function( response )
{
$( '#chained-subcontainer' ).html( response );
}
);
}
// No selection, show default
else
{
$( '#chained-subcontainer' ).html( '<select name="chained-subcategories" id="chained-subcategories"><option value="">- Select a category first -</option></select>' );
}
});
} );
Why not use objects? You need a factory to make cars.
a great reference: http://sourcemaking.com/creational_patterns
I also like to think about keep objects small, simple, and to do as little as possible. Break functions down to simple concepts like "make" and "show". That makes them interchangable and extensible. You will eventually be able to just ask for $this->model->
I would approach like this:
1 object to organize the data //model
another to build your rows//controller
another to display //view
To start looking at it like that, write some functions first to gain an understanding of what you are wanting to know.
foreach (make)->show(models);
You may discover you need to query the data differently... In other words, ask the db a more specific question up front rather than filter it after you've received it. Maybe filtering now seems quicker, but how many other questions and filtering will you have to do later?
One more comment: php is more controllerish and javascript feels more viewish. I say solve the problems in their most appropriate and simplest context- stick with php on this issue.
The only way to do this without AJAX is to get a list of all your "Make" categories, then generate a dropdown for each "Model" of each "Make" using wp_dropdown_categories() with the child_of parameter. Hide all the "Make" dropdowns upon page load, attach a change event handler to the "Make" dropdown and when it's called, show the appropriate "Model" dropdown while hiding all the rest. The showing/hiding can be done with jQuery or pure JS. Each "Model" dropdown will have to have a unique ID that can be used to identify which "Make" it belongs to.