Skip to content

Instantly share code, notes, and snippets.

@jtsternberg
Last active April 12, 2020 05:12
Show Gist options
  • Save jtsternberg/5959920 to your computer and use it in GitHub Desktop.
Save jtsternberg/5959920 to your computer and use it in GitHub Desktop.
Removes and replaces the built-in taxonomy metabox with our radio-select metabox.
<?php
if ( !class_exists( 'WDS_Taxonomy_Radio' ) ) {
/**
* Removes and replaces the built-in taxonomy metabox with our radio-select metabox.
* @link http://codex.wordpress.org/Function_Reference/add_meta_box#Parameters
*/
class WDS_Taxonomy_Radio {
// Post types where metabox should be replaced (defaults to all post_types associated with taxonomy)
public $post_types = array();
// Taxonomy slug
public $slug = '';
// Taxonomy object
public $taxonomy = false;
// New metabox title. Defaults to Taxonomy name
public $metabox_title = '';
// Metabox priority. (vertical placement)
// 'high', 'core', 'default' or 'low'
public $priority = 'high';
// Metabox position. (column placement)
// 'normal', 'advanced', or 'side'
public $context = 'side';
// Set to true to hide "None" option & force a term selection
public $force_selection = false;
/**
* Initiates our metabox action
* @param string $tax_slug Taxonomy slug
* @param array $post_types post-types to display custom metabox
*/
public function __construct( $tax_slug, $post_types = array() ) {
$this->slug = $tax_slug;
$this->post_types = is_array( $post_types ) ? $post_types : array( $post_types );
add_action( 'add_meta_boxes', array( $this, 'add_radio_box' ) );
}
/**
* Removes and replaces the built-in taxonomy metabox with our own.
*/
public function add_radio_box() {
foreach ( $this->post_types() as $key => $cpt ) {
// remove default category type metabox
remove_meta_box( $this->slug .'div', $cpt, 'core' );
// remove default tag type metabox
remove_meta_box( 'tagsdiv-'.$this->slug, $cpt, 'core' );
// add our custom radio box
add_meta_box( $this->slug .'_radio', $this->metabox_title(), array( $this, 'radio_box' ), $cpt, $this->context, $this->priority );
}
}
/**
* Displays our taxonomy radio box metabox
*/
public function radio_box() {
// uses same noncename as default box so no save_post hook needed
wp_nonce_field( 'taxonomy_'. $this->slug, 'taxonomy_noncename' );
// get terms associated with this post
$names = wp_get_object_terms( get_the_ID(), $this->slug );
// get all terms in this taxonomy
$terms = (array) get_terms( $this->slug, 'hide_empty=0' );
// filter the ids out of the terms
$existing = ( !is_wp_error( $names ) && !empty( $names ) )
? (array) wp_list_pluck( $names, 'term_id' )
: array();
// Check if taxonomy is hierarchical
// Terms are saved differently between types
$h = $this->taxonomy()->hierarchical;
// default value
$default_val = $h ? 0 : '';
// input name
$name = $h ? 'tax_input['. $this->slug .'][]' : 'tax_input['. $this->slug .']';
echo '<div style="margin-bottom: 5px;">
<ul id="'. $this->slug .'_taxradiolist" data-wp-lists="list:'. $this->slug .'_tax" class="categorychecklist form-no-clear">';
// If 'category,' force a selection, or force_selection is true
if ( $this->slug != 'category' && !$this->force_selection ) {
// our radio for selecting none
echo '<li id="'. $this->slug .'_tax-0"><label><input value="'. $default_val .'" type="radio" name="'. $name .'" id="in-'. $this->slug .'_tax-0" ';
checked( empty( $existing ) );
echo '> '. sprintf( __( 'No %s', 'wds' ), $this->taxonomy()->labels->singular_name ) .'</label></li>';
}
// loop our terms and check if they're associated with this post
foreach ( $terms as $term ) {
$val = $h ? $term->term_id : $term->slug;
echo '<li id="'. $this->slug .'_tax-'. $term->term_id .'"><label><input value="'. $val .'" type="radio" name="'. $name .'" id="in-'. $this->slug .'_tax-'. $term->term_id .'" ';
// if so, they get "checked"
checked( !empty( $existing ) && in_array( $term->term_id, $existing ) );
echo '> '. $term->name .'</label></li>';
}
echo '</ul></div>';
}
/**
* Gets the taxonomy object from the slug
* @return object Taxonomy object
*/
public function taxonomy() {
$this->taxonomy = $this->taxonomy ? $this->taxonomy : get_taxonomy( $this->slug );
return $this->taxonomy;
}
/**
* Gets the taxonomy's associated post_types
* @return array Taxonomy's associated post_types
*/
public function post_types() {
$this->post_types = !empty( $this->post_types ) ? $this->post_types : $this->taxonomy()->object_type;
return $this->post_types;
}
/**
* Gets the metabox title from the taxonomy object's labels (or uses the passed in title)
* @return string Metabox title
*/
public function metabox_title() {
$this->metabox_title = !empty( $this->metabox_title ) ? $this->metabox_title : $this->taxonomy()->labels->name;
return $this->metabox_title;
}
}
$custom_tax_mb = new WDS_Taxonomy_Radio( 'custom-tax-slug' );
// Update optional properties
// $custom_tax_mb->priority = 'low';
// $custom_tax_mb->context = 'normal';
// $custom_tax_mb->metabox_title = __( 'Custom Metabox Title', 'yourtheme' );
// $custom_tax_mb->force_selection = true;
}
@helen
Copy link

helen commented Jul 9, 2013

Some thoughts:

  • Use the add_meta_boxes hook rather than admin_menu. The latter works, obviously, but it's always better to be semantic.
  • Core saving of non-hierachical taxonomy terms expects a slug, not the ID. You'll end up creating new terms that are named things like "55" (or assigning to that term, if it exists already).
  • Instead of blindly removing the metaboxes, use is_taxonomy_hierarchical() to check instead (also good for the above point).
  • Radio for selecting none is not translatable. There's no good existing label to use, though. Personally, I wouldn't bother having this option, as I wouldn't use a radio select in a situation where you'd need to clear your selection.
  • If you replace the category metabox, you can actually set no category. I'm not 100% sure on this, but I have a feeling there are places in core that assume that a category is always set for posts.
  • I wouldn't place the metabox high on the side - by default for me it appeared above the publish metabox, which is probably not what you want.
  • To help avoid conflicts, wrap this up in class_exists().
  • Nitpicky semantics, but in the constructor, I'd put the $tax_slug arg first. You could even make the $post_types arg optional, then, and just have it do it for all post types with that taxonomy associated by default.

@jtsternberg
Copy link
Author

@helenhousandi Awesome pointers. Thanks for taking the time to look over the snippet & write your thoughts.

  • Changed!
  • I've updated the class to save the terms properly for both types (hierarchical and non)
  • Similar solution used
  • I made it a translatable string & also made that an optional class parameter
  • Conditionally removed the "none" option for 'category'
  • Yep, this is definitely a preference/need-based issue, so I've set the priority and the context as editable parameters
  • Updated!
  • I like it! Done!

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