Botones de radio para taxonomías en WordPress
Estoy intentando cambiar los checkboxes de los términos en el backend por radiobuttons. Encontré este tema: Modificando la apariencia de los inputs de taxonomías personalizadas que me ayudó a hacer esto. Sin embargo, esto convierte TODOS los checkboxes de términos en radio buttons.
¿Es posible aplicar esto solo para una taxonomía?
Mi código:
// Añadir meta boxes
add_action('add_meta_boxes','mysite_add_meta_boxes',10,2);
function mysite_add_meta_boxes($post_type, $post) {
// Iniciar buffer de salida
ob_start();
}
// Modificar la barra lateral de posts
add_action('dbx_post_sidebar','mysite_dbx_post_sidebar');
function mysite_dbx_post_sidebar() {
// Obtener y limpiar el buffer
$html = ob_get_clean();
// Reemplazar checkboxes por radio buttons
$html = str_replace('"checkbox"','"radio"',$html);
echo $html;
}
gracias

Sin embargo, esto convertirá TODAS las casillas de verificación de términos en botones de radio.
No solo eso, convertirá cualquier casilla de verificación en un meta box - ¡no es lo ideal!
En su lugar, vamos a dirigirnos específicamente a la función wp_terms_checklist()
, que se utiliza para generar la lista de casillas de verificación en el área de administración (incluyendo la edición rápida).
/**
* Usar inputs de radio en lugar de casillas de verificación para listados de términos en taxonomías específicas.
*
* @param array $args
* @return array
*/
function wpse_139269_term_radio_checklist( $args ) {
if ( ! empty( $args['taxonomy'] ) && $args['taxonomy'] === 'category' /* <== Cambiar a tu taxonomía requerida */ ) {
if ( empty( $args['walker'] ) || is_a( $args['walker'], 'Walker' ) ) { // No sobrescribir walkers de terceros.
if ( ! class_exists( 'WPSE_139269_Walker_Category_Radio_Checklist' ) ) {
/**
* Walker personalizado para cambiar inputs de casilla de verificación a radio.
*
* @see Walker_Category_Checklist
*/
class WPSE_139269_Walker_Category_Radio_Checklist extends Walker_Category_Checklist {
function walk( $elements, $max_depth, ...$args ) {
$output = parent::walk( $elements, $max_depth, ...$args );
$output = str_replace(
array( 'type="checkbox"', "type='checkbox'" ),
array( 'type="radio"', "type='radio'" ),
$output
);
return $output;
}
}
}
$args['walker'] = new WPSE_139269_Walker_Category_Radio_Checklist;
}
}
return $args;
}
add_filter( 'wp_terms_checklist_args', 'wpse_139269_term_radio_checklist' );
Nos enganchamos al filtro wp_terms_checklist_args
, luego implementamos nuestro propio "walker" personalizado (una familia de clases utilizadas para generar listas jerárquicas). Desde ahí, es un simple reemplazo de cadena de type="checkbox"
por type="radio"
si la taxonomía coincide con lo que hemos configurado (en este caso "category").

Precaución, esta es una gran respuesta pero presenta un problema para la Edición Rápida. Aunque transforma las casillas de verificación de edición rápida en botones de radio, no selecciona el botón de radio marcado, sino que limpia la lista. Si usas la edición rápida, tienes que marcar la categoría cada vez, de lo contrario se elimina.

@Howdy_McGee Mira mi respuesta para un enfoque con algunos cambios adicionales que superan la limitación.

La edición rápida en la asignación de categorías no establece el valor. Por favor, guíanos.

Aquí hay un pequeño ajuste que debe hacerse para WP v5.3 y versiones superiores:
function walk( $elements, $max_depth, ...$args ) {
$output = parent::walk( $elements, $max_depth, ...$args );
Esas dos líneas necesitan tener los puntos suspensivos colocados antes del $args
o arrojará un error.
Voy a intentar editar la respuesta anterior...

Hay una solicitud de función para integrar esta funcionalidad en el núcleo de WordPress, que está marcada como "aceptada" pero ha estado inactiva por un tiempo. Podrías participar/ayudar a desarrollarla: https://core.trac.wordpress.org/ticket/14877

El siguiente código hace básicamente lo mismo que @TheDeadMedic hizo en su respuesta, una buena respuesta que me llevó hasta la mitad del camino, así que esto es un complemento a ella. Por preferencia personal, opté por hacerlo con start_el
.
→ asegúrate de reemplazar TU-TAXONOMÍA en el código de abajo según tus necesidades
add_filter( 'wp_terms_checklist_args', 'wpse_139269_term_radio_checklist_start_el_version', 10, 2 );
function wpse_139269_term_radio_checklist_start_el_version( $args, $post_id ) {
if ( ! empty( $args['taxonomy'] ) && $args['taxonomy'] === 'TU-TAXONOMÍA' ) {
if ( empty( $args['walker'] ) || is_a( $args['walker'], 'Walker' ) ) { // No sobrescribir walkers de terceros.
if ( ! class_exists( 'WPSE_139269_Walker_Category_Radio_Checklist_Start_El_Version' ) ) {
class WPSE_139269_Walker_Category_Radio_Checklist_Start_El_Version extends Walker_Category_Checklist {
public function start_el( &$output, $category, $depth = 0, $args = array(), $id = 0 ) {
if ( empty( $args['taxonomy'] ) ) {
$taxonomy = 'category';
} else {
$taxonomy = $args['taxonomy'];
}
if ( $taxonomy == 'category' ) {
$name = 'post_category';
} else {
$name = 'tax_input[' . $taxonomy . ']';
}
$args['popular_cats'] = empty( $args['popular_cats'] ) ? array() : $args['popular_cats'];
$class = in_array( $category->term_id, $args['popular_cats'] ) ? ' class="popular-category"' : '';
$args['selected_cats'] = empty( $args['selected_cats'] ) ? array() : $args['selected_cats'];
/** Este filtro está documentado en wp-includes/category-template.php */
if ( ! empty( $args['list_only'] ) ) {
$aria_cheched = 'false';
$inner_class = 'category';
if ( in_array( $category->term_id, $args['selected_cats'] ) ) {
$inner_class .= ' selected';
$aria_cheched = 'true';
}
$output .= "\n" . '<li' . $class . '>' .
'<div class="' . $inner_class . '" data-term-id=' . $category->term_id .
' tabindex="0" role="checkbox" aria-checked="' . $aria_cheched . '">' .
esc_html( apply_filters( 'the_category', $category->name ) ) . '</div>';
} else {
$output .= "\n<li id='{$taxonomy}-{$category->term_id}'$class>" .
'<label class="selectit"><input value="' . $category->term_id . '" type="radio" name="'.$name.'[]" id="in-'.$taxonomy.'-' . $category->term_id . '"' .
checked( in_array( $category->term_id, $args['selected_cats'] ), true, false ) .
disabled( empty( $args['disabled'] ), false, false ) . ' /> ' .
esc_html( apply_filters( 'the_category', $category->name ) ) . '</label>';
}
}
}
}
$args['walker'] = new WPSE_139269_Walker_Category_Radio_Checklist_Start_El_Version;
}
}
return $args;
}
Como @Howdy_McGee comentó correctamente, esto no funciona bien con la edición rápida/inline. El código anterior maneja el guardado correctamente, pero el radio en la edición inline no aparece seleccionado. Por supuesto, queremos que esté seleccionado. Para esto, he hecho lo siguiente:
→ escribir algo de código jQuery para manejar el estado seleccionado
→ nombre del archivo: editphp-inline-edit-tax-radio-hack.js - usado abajo para el enqueue
jQuery(document).ready(function($) {
var taxonomy = 'status',
post_id = null,
term_id = null,
li_ele_id = null;
$('a.editinline').on('click', function() {
post_id = inlineEditPost.getId(this);
$.ajax({
url: ajaxurl,
data: {
action: 'wpse_139269_inline_edit_radio_checked_hack',
'ajax-taxonomy': taxonomy,
'ajax-post-id': post_id
},
type: 'POST',
dataType: 'json',
success: function (response) {
term_id = response;
li_ele_id = 'in-' + taxonomy + '-' + term_id;
$( 'input[id="'+li_ele_id+'"]' ).attr( 'checked', 'checked' );
}
});
});
});
→ necesitamos una acción AJAX - como se ve en el bloque de código anterior
add_action( 'wp_ajax_wpse_139269_inline_edit_radio_checked_hack', 'wpse_139269_inline_edit_radio_checked_hack' );
add_action( 'wp_ajax_nopriv_wpse_139269_inline_edit_radio_checked_hack', 'wpse_139269_inline_edit_radio_checked_hack' );
function wpse_139269_inline_edit_radio_checked_hack() {
$terms = wp_get_object_terms(
$_POST[ 'ajax-post-id' ],
$_POST[ 'ajax-taxonomy' ],
array( 'fields' => 'ids' )
);
$result = $terms[ 0 ];
echo json_encode($result);
exit;
die();
}
→ encolando el script anterior
→ cambia la ruta según tus necesidades
add_action( 'admin_enqueue_scripts', 'wpse_139269_inline_edit_radio_checked_hack_enqueue_script' );
function wpse_139269_inline_edit_radio_checked_hack_enqueue_script() {
wp_enqueue_script(
'editphp-inline-edit-tax-radio-hack-js',
get_template_directory_uri() . '/tu/ruta/editphp-inline-edit-tax-radio-hack.js',
array( 'jquery' )
);
}
Esto funciona bastante bien hasta ahora, pero solo la primera vez. Cuando se abre la edición inline por segunda vez, perdemos el estado seleccionado nuevamente. Obviamente, no queremos eso. Para solucionarlo, usé un método que encontré aquí por @brasofilo. Lo que hace es recargar la sección de edición inline actualizada. Esto hace que el radio checkbox se muestre correctamente, sin importar cuántas veces se cambie.
→ asegúrate de reemplazar TU-TIPO-DE-POST en el código de abajo según tus necesidades
add_action( 'wp_ajax_inline-save', 'wpse_139269_wp_ajax_inline_save', 0 );
function wpse_139269_wp_ajax_inline_save() {
global $wp_list_table;
check_ajax_referer( 'inlineeditnonce', '_inline_edit' );
if ( ! isset($_POST['post_ID']) || ! ( $post_ID = (int) $_POST['post_ID'] ) )
wp_die();
if ( 'page' == $_POST['post_type'] ) {
if ( ! current_user_can( 'edit_page', $post_ID ) )
wp_die( __( 'No tienes permiso para editar esta página.' ) );
} else {
if ( ! current_user_can( 'edit_post', $post_ID ) )
wp_die( __( 'No tienes permiso para editar este post.' ) );
}
if ( $last = wp_check_post_lock( $post_ID ) ) {
$last_user = get_userdata( $last );
$last_user_name = $last_user ? $last_user->display_name : __( 'Alguien' );
printf( $_POST['post_type'] == 'page' ? __( 'Guardar deshabilitado: %s está editando esta página actualmente.' ) : __( 'Guardar deshabilitado: %s está editando este post actualmente.' ), esc_html( $last_user_name ) );
wp_die();
}
$data = &$_POST;
$post = get_post( $post_ID, ARRAY_A );
// Como viene de la base de datos.
$post = wp_slash($post);
$data['content'] = $post['post_content'];
$data['excerpt'] = $post['post_excerpt'];
// Renombrar.
$data['user_ID'] = get_current_user_id();
if ( isset($data['post_parent']) )
$data['parent_id'] = $data['post_parent'];
// Estado.
if ( isset($data['keep_private']) && 'private' == $data['keep_private'] )
$data['post_status'] = 'private';
else
$data['post_status'] = $data['_status'];
if ( empty($data['comment_status']) )
$data['comment_status'] = 'closed';
if ( empty($data['ping_status']) )
$data['ping_status'] = 'closed';
// Excluir términos de taxonomías que no deberían aparecer en Edición Rápida.
if ( ! empty( $data['tax_input'] ) ) {
foreach ( $data['tax_input'] as $taxonomy => $terms ) {
$tax_object = get_taxonomy( $taxonomy );
/** Este filtro está documentado en wp-admin/includes/class-wp-posts-list-table.php */
if ( ! apply_filters( 'quick_edit_show_taxonomy', $tax_object->show_in_quick_edit, $taxonomy, $post['post_type'] ) ) {
unset( $data['tax_input'][ $taxonomy ] );
}
}
}
// Hack: wp_unique_post_slug() no funciona para borradores, así que fingiremos que nuestro post está publicado.
if ( ! empty( $data['post_name'] ) && in_array( $post['post_status'], array( 'draft', 'pending' ) ) ) {
$post['post_status'] = 'publish';
$data['post_name'] = wp_unique_post_slug( $data['post_name'], $post['ID'], $post['post_status'], $post['post_type'], $post['post_parent'] );
}
// Actualizar el post.
edit_post();
$wp_list_table = _get_list_table( 'WP_Posts_List_Table', array( 'screen' => $_POST['screen'] ) );
$level = 0;
$request_post = array( get_post( $_POST['post_ID'] ) );
$parent = $request_post[0]->post_parent;
while ( $parent > 0 ) {
$parent_post = get_post( $parent );
$parent = $parent_post->post_parent;
$level++;
}
$wp_list_table->display_rows( array( get_post( $_POST['post_ID'] ) ), $level );
if( $_POST['post_type'] == 'TU-TIPO-DE-POST' ) {
?>
<script type="text/javascript">
document.location.reload(true);
</script>
<?php
}
wp_die();
}
Nota: No probado exhaustivamente, pero hasta ahora funciona bien

Puedes usar el parámetro meta_box_cb
de la función register_taxonomy
para definir tu propia función para el meta_box
. Con la ayuda de este artículo he creado este fragmento:
function YOUR_TAXONOMY_NAME_meta_box($post, $meta_box_properties){
$taxonomy = $meta_box_properties['args']['taxonomy'];
$tax = get_taxonomy($taxonomy);
$terms = get_terms($taxonomy, array('hide_empty' => 0));
$name = 'tax_input[' . $taxonomy . ']';
$postterms = get_the_terms( $post->ID, $taxonomy );
$current = ($postterms ? array_pop($postterms) : false);
$current = ($current ? $current->term_id : 0);
?>
<div id="taxonomy-<?php echo $taxonomy; ?>" class="categorydiv">
<ul id="<?php echo $taxonomy; ?>-tabs" class="category-tabs">
<li class="tabs"><a href="#<?php echo $taxonomy; ?>-all"><?php echo $tax->labels->all_items; ?></a></li>
</ul>
<div id="<?php echo $taxonomy; ?>-all" class="tabs-panel">
<input name="tax_input[<?php echo $taxonomy; ?>][]" value="0" type="hidden">
<ul id="<?php echo $taxonomy; ?>checklist" data-wp-lists="list:symbol" class="categorychecklist form-no-clear">
<?php foreach($terms as $term){
$id = $taxonomy.'-'.$term->term_id;?>
<li id="<?php echo $id?>"><label class="selectit"><input value="<?php echo $term->term_id; ?>" name="tax_input[<?php echo $taxonomy; ?>][]" id="in-<?php echo $id; ?>"<?php if( $current === (int)$term->term_id ){?> checked="checked"<?php } ?> type="radio"> <?php echo show_symbol( $term->name ); ?></label></li>
<?php }?>
</ul>
</div>
</div>
<?php
}
Para hacer uso de este meta_box, debes pasar este parámetro a la función register_taxonomy
:
'meta_box_cb' => 'YOUR_TAXONOMY_NAME_meta_box'
Lo hermoso de este código es que no necesitas pasar ningún parámetro, ya que se basa en los parámetros pasados por la función register_taxonomy
. Estos son el objeto post
y un array que contiene información sobre el metabox en sí.

Excelente respuesta en general. Pero ten cuidado: Tienes <?php echo show_symbol( $term->name ); ?> como salida, ¿no debería ser <?php echo $term->name; ?>? No pude encontrar nada sobre una función con ese nombre. Y como sugerencia: Ya que estás mostrando todas las categorías disponibles, probablemente no necesites imprimir las pestañas.

Si prefieres hacerlo funcionar con un plugin, quizás quieras echar un vistazo a https://wordpress.org/plugins/radio-buttons-for-taxonomies/
Este plugin te permite reemplazar los cuadros de taxonomía predeterminados con un metabox personalizado que utiliza botones de radio... limitando efectivamente cada entrada a un solo término en esa taxonomía.
