Как сортировать сообщения в таксономии WordPress

Опубликовано: 2020-12-18

На данный момент Nelio опубликовал несколько плагинов в репозитории WordPress.org. Некоторые из них совершенно бесплатны и предлагают элегантные решения распространенных проблем, такие как, например, Nelio Maps, которая позволяет вам вставлять карту Google на ваши страницы или записи, созданные с помощью Gutenberg, и Nelio Compare Images, которая позволяет вам вставлять блок для, как следует из названия, сравнения двух изображений рядом. Мы также опубликовали пару плагинов премиум-класса, которые помогают нам оплачивать счета: Nelio Content и Nelio A/B Testing.

Если вы посмотрите на наш веб-сайт, то увидите, что наши два плагина премиум-класса занимают видное место, поскольку у них обоих есть несколько целевых страниц и страниц с ценами, и у них обоих есть соответствующая база знаний, где пользователи могут найти ответ на свои вопросы. Сегодня мы поговорим о том, как мы создавали страницы документации для Nelio A/B Testing и Nelio Content и что нам пришлось сделать, чтобы отсортировать вопросы по своему усмотрению.

Как создавать собственные типы записей в WordPress

Во-первых, давайте создадим собственный тип сообщения, чтобы отслеживать все вопросы, которые будут содержаться в нашей базе знаний. Вы можете увидеть результат на следующем снимке экрана, где есть экземпляр этого нового типа записи для базы знаний Nelio A/B Testing:

База знаний Nelio A/B Testing
Пример вопроса в базе знаний Nelio A/B Testing.

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

 function nelio_add_help_type() { $labels = array( 'name' => __( 'Questions', 'nelio-help' ), 'singular_name' => __( 'Question', 'nelio-help' ), 'menu_name' => __( 'Testing's Help', 'nelio-help' ), 'all_items' => __( 'All Questions', 'nelio_help' ), 'add_new_item' => __( 'Add New Question', 'nelio_help' ), 'edit_item' => __( 'Edit Question', 'nelio_help' ), 'view_item' => __( 'View Question', 'nelio_help' ), ); register_post_type( 'nab_help', array( 'capability_type' => 'post', 'labels' => $labels, 'map_meta_cap' => true, 'menu_icon' => 'dashicons-welcome-learn-more', 'public' => true, 'show_in_rest' => true, 'supports' => [ 'title', 'editor', 'author' ], ) ); } add_action( 'init', 'nelio_add_help_type' );

и вуаля! Возможно, вы уже знаете, что существуют такие плагины, как Advanced Custom Fields, которые помогают создавать и настраивать интерфейс пользовательских типов сообщений, но я не обязательно рекомендую его для реализации такого простого примера.

Кстати, если вам интересно, где этот фрагмент должен быть размещен, не пропустите наш учебник о том, как настроить WordPress с помощью пользовательских плагинов.

Как создать новую таксономию для организации контента

Наша база знаний разделена на «Темы» и «Ключевые слова», как вы можете видеть на следующем снимке экрана:

Скриншот панели инструментов WordPress, показывающий наш новый тип записей с двумя таксономиями: темы и ключевые слова.
Снимок экрана панели инструментов WordPress, показывающий наш новый тип сообщений «Справка по тестированию» с двумя таксономиями: « Темы » и « Ключевые слова ».

Темы и ключевые слова эквивалентны категориям и тегам, которые по умолчанию есть в сообщениях WordPress. Давайте просто сосредоточимся на первом, так как второе точно такое же.

Как вы уже поняли, тема — это пользовательская таксономия, которую мы создали для нашего пользовательского типа контента. Чтобы добавить новую таксономию к типу записи WordPress, вам просто нужно использовать функцию register_taxonomy следующим образом:

 function nelio_add_help_taxonomy() { $labels = array( 'name' => __( 'Topics', 'nelio-help' ), 'singular_name' => __( 'Topic', 'nelio-help' ), 'menu_name' => __( 'Topics', 'nelio-help' ), 'all_items' => __( 'All Topics', 'nelio_help' ), /* ... */ ); register_taxonomy( 'nab_topic', [ 'nab_help' ], array( 'hierarchical' => true, 'label' => __( 'Topic', 'nelio-help' ), 'query_var' => true, 'show_admin_column' => false, 'show_ui' => true, 'show_in_rest' => true, ) ); } add_action( 'init', 'nelio_add_help_taxonomy' );

Функция довольно проста в использовании. Во-первых, вы называете таксономию (в нашем примере nab_topic ). Затем вы указываете типы сообщений, к которым они относятся (у нас есть только один: nab_help ). Наконец, вы добавляете еще несколько аргументов для настройки таксономии. Результатом стала новая таксономия, с помощью которой можно создать конкретные темы , необходимые для категоризации вопросов в нашей базе знаний:

Скриншот пользовательской таксономии в панели инструментов WordPress.
Скриншот нашей пользовательской таксономии ( Темы ).

Как сортировать сообщения в таксономии

Теперь, когда у нас есть собственная таксономия и мы можем добавлять вопросы в каждую тему , которую мы создаем, как нам сортировать эти вопросы в рамках конкретной темы? Ну, боюсь, нам придется немного кодировать…

Взгляните на базу данных

WordPress и их отношения с вашими сообщениями хранятся в четырех разных таблицах базы данных:

  • wp_terms включает в себя все термины, которые вы создаете в рамках определенной таксономии. Например, в таксономии наших тем у нас есть такие термины, как «Общие вопросы », « Совместимость » и « Платежи и выставление счетов ».
  • wp_termmeta помогает нам хранить дополнительные данные, связанные с каждым термином. Это очень похоже на wp_postmeta .
  • wp_term_taxonomy связывает каждый термин в wp_terms с определяющей его таксономией. Таким образом, например, эта таблица отслеживает тот факт, что термин « Общий вопрос» фактически является экземпляром таксономии nab_topic .
  • wp_term_relationships связывает каждый термин ( term_taxonomy_id ) с постами, которые он «содержит» ( object_id ). Например, эта таблица говорит нам, что вопрос о том, что Nelio A/B Testing является общим вопросом (и именно поэтому вопрос появляется при просмотре темы).

WordPress 2.5 добавил новое числовое поле в таблицу wp_term_relationships , term_order , которое позволяет нам указать позицию, которую занимает определенный элемент в таксономии. Это похоже на кандидата, которого мы искали для сортировки наших вопросов в нашей теме… но есть одна загвоздка: насколько я могу судить, WordPress не предлагает стандартный механизм для (1) определения значения поля term_order и ( 2) использовать его для фактической сортировки сообщений в рамках определенной таксономии. Давайте исправим это!

Как использовать поле term_order для сортировки сообщений, включенных в таксономию

Предположим, нам удалось каким-то образом установить желаемые значения term_order , как вы можете видеть на следующем снимке экрана:

Скриншот заказа, который мы видим в базе данных, и тот, который на самом деле появляется в Интернете.
Порядок, который мы видим в базе данных (и это то, что мы ожидаем увидеть), отличается от того, который мы фактически получаем при просмотре нашего веб-сайта.

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

К счастью, мы можем легко указать WordPress использовать поле для сортировки сообщений по нему. Просто используйте фильтр posts_orderby , чтобы настроить запрос, извлекающий сообщения, принадлежащие определенному термину, чтобы он включал запрос на сортировку:

 add_filter( 'posts_orderby', 'nelio_sort_questions_in_topic', 99, 2 ); function nelio_sort_questions_in_topic( $orderby, $query ) { if ( ! nelio_is_topic_tax_query( $query ) ) return; global $wpdb; return "{$wpdb->term_relationships}.term_order ASC"; } function nelio_is_topic_tax_query( $query ) { if ( empty( $query->tax_query ) ) return; if ( empty( $query->tax_query->queries ) ) return; return in_array( $query->tax_query->queries[0]['taxonomy'], [ 'nab_topic' ], true ); }

Все, что делает предыдущий фрагмент, — это проверяет, что мы запрашиваем определенную таксономию (в нашем случае nab_topic ), и когда мы это делаем, мы добавляем в запрос предложение ORDER BY , чтобы окончательный результат был упорядочен по значению term_order находится в таблице $wpdb->term_relationships :

Скриншот результатов (упорядоченных) таксономии
Скриншот (отсортированных) сообщений в рамках таксономии.

Реализация пользовательского интерфейса для упорядочивания содержимого таксономии

Наконец, нам осталось сделать только одно. В предыдущем разделе мы предполагали, что каким-то образом смогли установить правильное значение в поле term_order . Но как? Есть ли где-нибудь экран или опция для установки этого значения в удобной для пользователя форме? Насколько я знаю, нет. Но мы можем создать его.

На мой взгляд, лучшим решением этой проблемы было бы иметь пользовательский интерфейс, который позволял бы мне выбирать таксономию, которую я хочу отсортировать, а затем перетаскивать все вопросы, чтобы установить их порядок. Что-то вроде этого, иди:

Пользовательский интерфейс для простой сортировки сообщений, которые у нас есть в определенной таксономии.
Пользовательский интерфейс для простой сортировки сообщений, которые у нас есть в определенной таксономии.

Для достижения желаемого эффекта мы должны выполнить следующие шаги:

  • Зарегистрируйте новую страницу на панели инструментов WordPress.
  • Реализуйте функцию, которая будет отображать селектор таксономии и сообщения в выбранной таксономии.
  • Добавьте крошечный фрагмент JavaScript, чтобы пользовательский интерфейс реагировал
  • Создайте новый обратный вызов, чтобы сохранить полученный порядок после того, как мы закончим сортировку сообщений.

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

Как я уже говорил, первым делом нужно зарегистрировать страницу, на которой мы будем размещать интерфейс. Мы можем легко сделать это с помощью функции add_submenu_page во время действия admin_menu :

 add_action( 'admin_menu', function() { add_submenu_page( 'edit.php?post_type=nab_help', 'Sort', 'Sort', 'edit_others_posts', 'nab-help-question-sorter', __NAMESPACE__ . '\render_question_sorter' ) } );

Метод render_question_sorter также чрезвычайно прост:

 function render_question_sorter() { echo '<div class="wrap"><h1>Sort Questions</h1>'; $terms = get_terms( 'nab_topic' ); render_select( $terms ); foreach ( $terms as $term ) { render_questions_in_term( $term ); } render_script(); echo '</div>'; }

Как видите, мы просто извлекаем все термины в таксономии nab_topic а затем полагаемся на три вспомогательные функции, чтобы (1) отобразить селектор, (2) отобразить вопросы в каждой категории и (3) добавить небольшой скрипт, чтобы сделать все динамичный.

Отображение селектора так же просто, как итерация по каждому термину и отображение option :

 function render_select( $terms ) { echo '<select>'; foreach ( $terms as $term ) { printf( '<option value="%s">%s</option>', esc_attr( $term->slug ), esc_html( $term->name ) ); } echo '</select>'; }

Чтобы отобразить вопросы, которые у нас есть в каждом термине таксономии, мы запускаем запрос к базе данных и перебираем их все:

 function render_questions_in_term( $term ) { printf( '<div class="term-block" data-term->', esc_attr( $term->slug ), esc_attr( $term->term_id ) ); echo '<div class="sortable">' $query = new WP_Query( array( 'post_type' => 'nab_help', 'post_per_page' => -1, 'tax_query' => array( array( 'taxonomy' => 'nab_topic', 'field' => 'term_id', 'terms' => $term->term_id, 'orderby' => 'term_order', ), ), ) ); while ( $query->have_posts() ) { $query->the_post(); global $post; printf( '<div class="question" data-question->%s</div>', esc_attr( $post->ID ), esc_html( $post->post_title ) ); } echo '</div>'; printf( '<input type="button" data-term- value="%s" />', esc_attr( $term->term_id ), esc_attr( 'Save' ) ); echo '</div>'; }

Обратите внимание, что эта функция отображает блок div , который использует slug текущего $term в качестве идентификатора, группирует вопросы в еще одном элементе div и, наконец, включает кнопку « Сохранить ». Все эти детали будут полезны, когда мы, наконец, отрендерим скрипт, связывающий наше приложение.

Когда все будет готово, нам просто нужно добавить немного магии JavaScript. В частности, этот быстрый и грязный пользовательский интерфейс требует двух скриптов ( jquery и jquery-ui ) для реализации функции перетаскивания, а затем следующий фрагмент JS:

 ( function() { // MAKE QUESTIONS SORTABLE $( '.sortable' ).sortable(); // TERM SELECTOR const select = document.getElementById( 'topic' ); const termBlocks = [ ...document.querySelectorAll( '.term-block' ) ]; select.addEventListener( 'change', showSelectedTermBlock ); // Helper function function showSelectedTermBlock() { /* ... */ } // SAVE BUTTONS [ ...document.querySelectorAll( '.term-block input[type="button"]' ) ].forEach( addButtonLogic ); // Helper function function addButtonLogic( button ) { const termId = button.getAttribute( 'data-term-id' ); button.addEventListener( 'click', () => { button.disabled = true; const selector = `div[data-term-] .question`; const ids = [ ...document.querySelectorAll( selector ) ] .map( ( question ) => question.getAttribute( 'data-question-id' ); $.ajax( { url: ajaxurl, method: 'POST', data: { action: 'nelio_save_tax_sorting', objectIds: ids, termId, }, } ).always( () => button.disable = false ); } } } )()

На первый взгляд предыдущий фрагмент может показаться сложным. Но я обещаю: это не так. Во-первых, мы делаем наши вопросы « sortable », используя функцию сортировки jQuery UI. Затем мы добавляем прослушиватель к нашему компоненту select , который показывает/скрывает соответствующий набор вопросов с помощью вспомогательной функции. Наконец, мы добавляем прослушиватели click к нашим кнопкам « Сохранить », чтобы, когда пользователь нажимал на них, порядок сортировки вопросов сохранялся в базе данных.

Как вы можете видеть, сохранение этого нового заказа выполняется через запрос AJAX, а это означает, что нам нужно реализовать аналог обратного вызова PHP:

 add_action( 'wp_ajax_nelio_save_tax_sorting', __NAMESPACE__ . '\save_tax_sorting' ); function save_tax_sorting() { $term_id = isset( $_POST['termId'] ) ? absint( $_POST['termId'] : 0 ); if ( ! $term_id ) die(); $object_ids = isset( $_POST['objectIds'] ) ? $_POST['objectIds'] : []; if ( ! is_array( $object_ids ) || empty( $object_ids ) ) die(); $object_ids = array_values( array_map( 'absint', $object_ids ) ); global $wpdb; foreach ( $object_ids as $order => $object_id ) { $wpdb->update( $wpdb->term_relationships, array( 'term_order' => $order + 1 ), array( 'object_id' => $object_id, 'term_taxonomy_id' => $term_id, ), ); } die(); }

Вот и все! Довольно легко, да?

В итоге

WordPress не предлагает механизм по умолчанию для сортировки сообщений в рамках определенной таксономии. Но это нормально! Мы можем реализовать его сами: все, что нам нужно сделать для сортировки сообщений в таксономии WordPress, — это установить поле term_order в таблице базы данных term_relationships , а затем расширить WordPress с помощью фильтра posts_orderby , чтобы он использовал указанное поле.

Наконец, мы наметили простой пользовательский интерфейс для простого заполнения поля term_order путем перетаскивания сообщений, включенных в определенную таксономию.

Надеюсь, вам понравился этот урок, и если да, поделитесь им с друзьями и коллегами. И, как всегда, если у вас есть какие-либо вопросы, оставьте их в разделе комментариев ниже, и я буду рад ответить на них.

Избранное изображение Стива Джонсона на Unsplash.