Радиокнопки для таксономий WordPress - как настроить для одной таксономии

26 мар. 2014 г., 15:01:35
Просмотры: 18.6K
Голосов: 11

Я пытаюсь заменить чекбоксы для терминов в админке на радиокнопки. Нашел эту тему: Изменение вида элементов управления для пользовательских таксономий, которая помогла мне в этом. Однако этот код изменяет ВСЕ чекбоксы терминов на радиокнопки.

Можно ли применить это только для одной таксономии?

Мой код:

add_action('add_meta_boxes','mysite_add_meta_boxes',10,2);
function mysite_add_meta_boxes($post_type, $post) {
  ob_start(); // Начинаем буферизацию вывода
}
add_action('dbx_post_sidebar','mysite_dbx_post_sidebar');
function mysite_dbx_post_sidebar() {
  $html = ob_get_clean(); // Получаем содержимое буфера
  $html = str_replace('"checkbox"','"radio"',$html); // Заменяем чекбоксы на радиокнопки
  echo $html; // Выводим измененный HTML
}

спасибо

1
Комментарии

Есть запрос на добавление этой функциональности в ядро WordPress, который помечен как "принятый", но долгое время не развивался. Можете подключиться к обсуждению или помочь в разработке: core.trac.wordpress.org/ticket/14877

Mike Eng Mike Eng
20 нояб. 2021 г. 06:02:53
Все ответы на вопрос 4
5
20

Однако это превратит ВСЕ чекбоксы терминов в радиокнопки.

Более того, это затронет любые чекбоксы внутри метабоксов — что не идеально!

Вместо этого давайте точечно нацелимся на функцию wp_terms_checklist(), которая используется для генерации списка чекбоксов в админке (включая быстрое редактирование).

/**
 * Заменяем чекбоксы на радиокнопки в чеклистах терминов для указанных таксономий.
 *
 * @param   array   $args
 * @return  array
 */
function wpse_139269_term_radio_checklist( $args ) {
    if ( ! empty( $args['taxonomy'] ) && $args['taxonomy'] === 'category' /* <== Укажите вашу таксономию */ ) {
        if ( empty( $args['walker'] ) || is_a( $args['walker'], 'Walker' ) ) { // Не переопределяем сторонние walker'ы.
            if ( ! class_exists( 'WPSE_139269_Walker_Category_Radio_Checklist' ) ) {
                /**
                 * Кастомный walker для замены чекбоксов на радиокнопки.
                 *
                 * @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' );

Мы используем фильтр wp_terms_checklist_args, затем реализуем собственный кастомный "walker" (классы, используемые для генерации иерархических списков). После этого просто заменяем строку type="checkbox" на type="radio", если таксономия соответствует указанной (в данном случае "category").

26 мар. 2014 г. 15:57:07
Комментарии

Внимание, это отличное решение, но оно вызывает проблему с быстрым редактированием. Хотя оно и преобразует чекбоксы быстрого редактирования в радиокнопки, оно не выбирает отмеченную радиокнопку, а вместо этого очищает список. При быстром редактировании каждый раз приходится заново выбирать категорию, иначе она удаляется.

Howdy_McGee Howdy_McGee
2 июн. 2015 г. 21:02:06

@Howdy_McGee Смотрите мой ответ для подхода с дополнительными изменениями, которые преодолевают это ограничение.

Nicolai Grossherr Nicolai Grossherr
15 июл. 2015 г. 16:30:59

При быстром редактировании категории значение не устанавливается. Пожалуйста, подскажите решение.

Ravi Patel Ravi Patel
6 дек. 2018 г. 16:40:07

Небольшая корректировка, необходимая для WP версии 5.3 и выше: function walk( $elements, $max_depth, ...$args ) { $output = parent::walk( $elements, $max_depth, ...$args );

В этих двух строках необходимо поставить многоточие перед $args, иначе будет выдаваться ошибка.

Попробую внести правки в ответ выше...

Tony Djukic Tony Djukic
5 апр. 2020 г. 04:45:16

Есть запрос на добавление этой функциональности в ядро WordPress, который помечен как "принятый", но долгое время неактивен. Можете присоединиться к обсуждению/помочь в реализации: https://core.trac.wordpress.org/ticket/14877

Mike Eng Mike Eng
20 нояб. 2021 г. 05:28:34
0

Следующий код делает практически то же самое, что и решение @TheDeadMedic в его ответе, который помог мне наполовину, так что это своего рода дополнение к нему. По личным предпочтениям я выбрал реализацию через start_el.

→ убедитесь заменить YOUR-TAXONOMY в коде ниже в соответствии с вашими потребностями

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'] === 'YOUR-TAXONOMY' ) {
        if ( empty( $args['walker'] ) || is_a( $args['walker'], 'Walker' ) ) { // Не переопределяем сторонние walkers.
            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'];

                        /** Этот фильтр описан в 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;
}

Как правильно отметил в своем комментарии @Howdy_McGee, это не работает корректно с быстрым/встроенным редактированием. Приведенный выше код правильно обрабатывает сохранение, но радио-кнопка при встроенном редактировании не отмечена. Конечно, мы хотим исправить это, поэтому я сделал следующее:

→ написать jQuery-код для обработки состояния checked
→ имя файла: editphp-inline-edit-tax-radio-hack.js - используется ниже для подключения

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' );
            }
        });
    });

});

→ нам нужно AJAX-действие - как видно в блоке кода выше

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();
}

→ подключение скрипта выше
→ измените путь в соответствии с вашими потребностями

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() . '/your/path/editphp-inline-edit-tax-radio-hack.js',
        array( 'jquery' )
    );
}

Это работает довольно хорошо, но только в первый раз - при повторном открытии встроенного редактирования состояние checked снова теряется. Конечно, мы этого не хотим. Чтобы обойти это, я использовал метод, найденный здесь от @brasofilo. Он перезагружает обновленный раздел встроенного редактирования. Это приводит к правильному отображению радио-кнопки, независимо от того, сколько раз она изменялась.

→ убедитесь заменить YOUR-POST-TYPE в коде ниже в соответствии с вашими потребностями

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( __( 'Вы не можете редактировать эту страницу.' ) );
        } else {
            if ( ! current_user_can( 'edit_post', $post_ID ) )
                wp_die( __( 'Вы не можете редактировать эту запись.' ) );
        }

        if ( $last = wp_check_post_lock( $post_ID ) ) {
            $last_user = get_userdata( $last );
            $last_user_name = $last_user ? $last_user->display_name : __( 'Кто-то' );
            printf( $_POST['post_type'] == 'page' ? __( 'Сохранение отключено: %s сейчас редактирует эту страницу.' ) : __( 'Сохранение отключено: %s сейчас редактирует эту запись.' ),    esc_html( $last_user_name ) );
            wp_die();
        }

        $data = &$_POST;

        $post = get_post( $post_ID, ARRAY_A );

        // Поскольку данные приходят из базы.
        $post = wp_slash($post);

        $data['content'] = $post['post_content'];
        $data['excerpt'] = $post['post_excerpt'];

        // Переименование.
        $data['user_ID'] = get_current_user_id();

        if ( isset($data['post_parent']) )
            $data['parent_id'] = $data['post_parent'];

            // Статус.
            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';

            // Исключаем термины таксономий, которые не должны появляться в Быстром редактировании.
            if ( ! empty( $data['tax_input'] ) ) {
                foreach ( $data['tax_input'] as $taxonomy => $terms ) {
                    $tax_object = get_taxonomy( $taxonomy );
                    /** Этот фильтр описан в 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 ] );
                    }
                }
            }

            // Хак: wp_unique_post_slug() не работает для черновиков, поэтому мы сделаем вид, что наша запись опубликована.
            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'] );
            }

            // Обновляем запись.
            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'] == 'YOUR-POST-TYPE' ) {
                ?>
                    <script type="text/javascript">
                        document.location.reload(true);
                    </script>
                <?php
            }

    wp_die();
}



Примечание: Не всесторонне протестировано, но пока работает хорошо

15 июл. 2015 г. 16:29:44
1

Вы можете использовать параметр meta_box_cb функции register_taxonomy для определения собственной функции метабокса. С помощью этой статьи я создал следующий сниппет:

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
}

Чтобы использовать этот метабокс, вам нужно передать этот параметр в функцию register_taxonomy:

'meta_box_cb'                => 'YOUR_TAXONOMY_NAME_meta_box'

Прелесть этого кода в том, что вам не нужно передавать никаких параметров, так как он использует параметры, переданные ему функцией register_taxonomy. Это объект post и массив, содержащий информацию о самом метабоксе.

11 сент. 2014 г. 16:39:35
Комментарии

Отличный ответ в целом. Но будьте осторожны: у вас есть <?php echo show_symbol( $term->name ); ?> в качестве вывода, разве не должно быть <?php echo $term->name; ?>? Не смог найти ничего о функции с таким названием. И как предложение: раз вы показываете все доступные категории, вероятно, вам не нужно выводить вкладки.

André R. Kohl André R. Kohl
26 июн. 2021 г. 13:55:39
0

Если вы предпочитаете реализовать это с помощью плагина, обратите внимание на https://wordpress.org/plugins/radio-buttons-for-taxonomies/

Этот плагин позволяет заменить стандартные блоки таксономий на пользовательские метабоксы с радиокнопками, что фактически ограничивает каждую запись одним термином в этой таксономии.

30 авг. 2018 г. 10:32:02