Регистрация пользовательской таксономии в WordPress без привязки к типу записей

# Регистрируем таксономию без привязки к типу записей

В WordPress есть одна особенность: нельзя создать таксономии независимо, без привязки к типу записей. Можно зарегистрировать таксономию, но при этом при переходе на страницу создания новых элементов (терминов) мы окажемся во вкладке "Записи" в панели администратора. Мы же хотим создать собственную страницу таксономии в панели управления. Давайте разберем это по шагам.

## Задача

Мне нужно сохранить некоторые данные (строки) с возможностью добавления новых. Эти данные (строки) будут примером навыков пользователей (например, пользователь умеет готовить, убирать, стирать).

Чтобы избежать написания большого количества кода и иметь возможность легко создавать, изменять и удалять эти навыки (число которых может превышать 2000), я решил создать пользовательскую таксономию в WordPress.

### Преимущества:
- Легкость регистрации таксономии.
- Мы получаем таблицу с пагинацией и поиском.
- Возможность добавлять, изменять и удалять данные, а также расширять их с помощью метаданных терминов.
- Доступ к целому набору функций WP для отображения и управления элементами нашей таксономии.

### Недостатки:
- Неиспользуемые поля в таблице таксономии, но это мелочь.

Итак, задача — создать таксономию, не привязанную ни к одному типу записей, и добавить ее в меню панели управления.

## Решение

Теперь создадим нашу таксономию. Она нам нужна только для хранения данных, поэтому она не будет публичной (невидима на фронтенде сайта) и не будет иметь типичных параметров таксономии.

### Создание таксономии "Навыки"
```php
add_action( 'init', function (){
    register_taxonomy( 'skills', null, array(
        'labels'                => array(
            'name'          => 'Навыки',
            'singular_name' => 'Навык',
            'add_new_item'  => 'Добавить новый навык',
        ),
        'public'                => false,
        'show_ui'               => true,  // равно параметру public
        'show_in_rest'          => false, // не добавляем в REST API
        'hierarchical'          => false,
        'update_count_callback' => '__return_null',
    ) );
}, 20 );

Добавление пункта меню для таксономии в админ-панели

add_action( 'admin_menu', 'add_skills_menu_item' );
function add_skills_menu_item(){
    add_menu_page( 'Навыки', 'Навыки', 'manage_options', "edit-tags.php?taxonomy=skills", null, 'dashicons-awards', 9 );
}

Так мы получаем страницу таксономии в панели управления:

Таксономия в панели управления

Но если мы перейдем на страницу таксономии "Навыки", то окажемся на вкладке "Записи".

Теперь сделаем так, чтобы активным пунктом меню была наша таксономия, а не вкладка "Записи". В WordPress нет встроенных хуков для простого решения этой задачи, поэтому воспользуемся обходным путем.

Изменяем код добавления пункта меню

/**
 * Добавляем пункт меню для таксономии в админ-панели.
 */
add_action( 'admin_menu', 'add_skills_menu_item' );
function add_skills_menu_item() {
    global $menu;

    $tax_name = 'skills';
    $menu_title = 'Навыки';
    $capability = 'manage_options';

    $is_skills = ( ( $_GET['taxonomy'] ?? '' ) === $tax_name );

    // Удаляем 'current' для записей (по умолчанию таксономия прикрепляется к ним)
    if( $is_skills ){
        add_filter( 'parent_file', '__return_false' );
    }

    // Добавляем пункт меню
    add_menu_page( $menu_title, $menu_title, $capability, "edit-tags.php?taxonomy=$tax_name", null, 'dashicons-awards', 9 );

    // Настраиваем параметры добавленного пункта меню
    $menu_item_key = key( wp_list_filter( $menu, [ $menu_title ] ) );
    $menu_item = & $menu[ $menu_item_key ];
    foreach( $menu_item as & $val ){
        // Добавляем класс 'current' где необходимо
        if( false !== strpos( $val, 'menu-top' ) ){
            $val = 'menu-top' . ( $is_skills ? ' current' : '' );
        }

        $val = preg_replace( '~toplevel_page[^ ]+~', "toplevel_page_$tax_name", $val );
    }
}

Результат

Результат

Готово!

Дополнительные настройки

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

Вот полн��й код с учетом всех изменений:

 'Навыки',
        'singular_name' => 'Навык',
        'add_new_item'  => 'Добавить новый навык',
    ];

    public static function init() {
        add_action( 'init', static function() {
            self::register_taxonomy();
            self::bulk_add_terms_handler();
        } );

        // Добавляем пункт меню для таксономии в админ-панели
        add_action( 'admin_menu', [ __CLASS__, 'add_tax_menu_item' ] );

        // Форма для массового добавления навыков
        add_action( self::$tax_name . '_add_form', [ __CLASS__, 'bulk_add_terms_form' ] );

        // Кастомные стили на странице таксономии и на странице редактирования элемента.
        add_action( 'admin_head', [ __CLASS__, 'styles_on_edit_term_page' ] );

        // Убираем ненужные колонки
        $edit_page_key = 'edit-' . self::$tax_name;
        add_filter( "manage_{$edit_page_key}_columns", [ __CLASS__, 'remove_unused_columns' ] );
    }

    private static function register_taxonomy(): void {
        register_taxonomy(
            self::$tax_name,
            null,
            [
                'labels'       => self::$menu_labels,
                'public'       => false,
                'show_ui'      => true, // равно параметру public
                'show_in_rest' => false, // не добавляем в REST API
                'hierarchical' => false,
                'update_count_callback' => '__return_null',
            ]
        );
    }

    public static function add_tax_menu_item(): void {
        global $menu;

        $tax_name = self::$tax_name;

        $is_the_tax = ( ( $_GET['taxonomy'] ?? '' ) === self::$tax_name );

        // Удаляем 'current' для записей (по умолчанию таксономия привязана там)
        if( $is_the_tax ){
            add_filter( 'parent_file', '__return_false' );
        }

        // Добавляем пункт меню
        add_menu_page(
            self::$menu_title,
            self::$menu_title,
            self::$capability,
            'edit-tags.php?taxonomy=' . self::$tax_name,
            null,
            'dashicons-awards',
            9
        );

        // Настраиваем параметры добавленного пункта меню
        $menu_item_key = key( wp_list_filter( $menu, [ self::$menu_title ] ) );
        $menu_item = & $menu[ $menu_item_key ];
        foreach( $menu_item as & $val ){
            // Добавляем класс 'current' где необходимо
            if( false !== strpos( $val, 'menu-top' ) ){
                $val = 'menu-top' . ( $is_the_tax ? ' current' : '' );
            }

            $val = preg_replace( '~toplevel_page[^ ]+~', "toplevel_page_$tax_name", $val );
        }
    }

    /**
     * Обрабатываем запрос на массовое добавление навыков.
     */
    public static function bulk_add_terms_handler(): void {
        $new_terms = trim( $_POST[ self::$request_key ] ?? '' );
        if(  ! $new_terms || ! current_user_can( self::$capability ) ){
            return;
        }

        $new_terms = wp_unslash( $new_terms );
        $new_terms = array_filter( array_map( 'trim', explode( "n", $new_terms ) ) );

        $err_names = [];
        foreach( $new_terms as $new_term_name ){
            $data = wp_insert_term( $new_term_name, self::$tax_name );
            if( is_wp_error( $data ) ){
                $err_names[ $new_term_name ] = $data->get_error_message();
            }
        }

        // Сообщение о результате обработки запроса
        add_action( 'admin_notices', function() use ( $err_names, $new_terms ) {
            $added_count = count( $new_terms ) - count( $err_names );
            $message = "

Добавлено терминов: $added_count

"; if( $err_names ){ $message .= '

'; $message .= 'Не удалось добавить:
'; foreach( $err_names as $skill_name => $err_msg ){ $message .= '' . esc_html( $skill_name ) . ": $err_msg
"; } $message .= "

"; } echo '
' . $message . '
'; } ); } public static function bulk_add_terms_form(): void { if( ! current_user_can( self::$capability ) ){ return; } // Код выводится внутри существующей формы, поэтому мы закроем ее и откроем свою ?>

Массовое добавление навыков

Список навыков, по одному на строку.

id !== $edit_page_key ){ return; } // Скрываем ненужные поля ?>

Теперь у вас есть таксономия "Навыки", с которой можно легко работать, добавлять и редактировать элементы.

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *