В современных проектах на WordPress часто возникает необходимость организовать сложную структуру из пользовательских типов постов (Custom Post Types, CPT) с иерархией, которая должна динамически подгружаться без перезагрузки страницы. В этой статье мы рассмотрим, как создать такую динамическую иерархию с поддержкой AJAX, что позволит улучшить UX и оптимизировать работу сайта.
Почему нужна динамическая иерархия типов постов с AJAX
Стандартный подход — выводить иерархию полностью на странице — плохо масштабируется при большом количестве записей. Загрузка всех элементов сразу замедляет страницу и усложняет интерфейс. AJAX позволяет подгружать только нужные уровни и ветви иерархии по запросу пользователя, например, при клике на раскрывающийся элемент.
Такой подход особенно полезен для каталогов, баз знаний, сайтов с большим количеством вложенных CPT, где удобство навигации и скорость работы критичны.
Создание иерархии CPT: подготовительный этап
Для примера создадим два пользовательских типа постов — product_category (категории продуктов) и product (продукты). Важно, чтобы product_category был иерархическим, а product — связанным с категориями.
Регистрируем CPT в файле functions.php или в плагине:
function wphierarchy_register_cpts() {
register_post_type('product_category', [
'label' => 'Категории продуктов',
'public' => true,
'hierarchical' => true,
'show_ui' => true,
'supports' => ['title', 'editor', 'page-attributes'],
'rewrite' => ['slug' => 'product-category'],
]);
register_post_type('product', [
'label' => 'Продукты',
'public' => true,
'hierarchical' => false,
'show_ui' => true,
'supports' => ['title', 'editor', 'thumbnail'],
'taxonomies' => ['product_category'],
'rewrite' => ['slug' => 'product'],
]);
}
add_action('init', 'wphierarchy_register_cpts');Здесь product_category — иерархический тип, что позволяет создавать вложенные категории, а product — тип с таксономией категорий.
Реализация AJAX-подгрузки иерархии
Основная идея — при клике по категории отправлять AJAX-запрос и получать дочерние категории и продукты, которые будут отображены под выбранным элементом.
Подключение скриптов и локализация
Добавим скрипт в админку или фронтенд (например, в шаблон):
function wphierarchy_enqueue_scripts() {
wp_enqueue_script('wphierarchy-ajax', get_template_directory_uri() . '/js/wphierarchy-ajax.js', ['jquery'], null, true);
wp_localize_script('wphierarchy-ajax', 'wphierarchy_ajax', [
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('wphierarchy_nonce'),
]);
}
add_action('wp_enqueue_scripts', 'wphierarchy_enqueue_scripts');JavaScript для обработки кликов и AJAX-запросов
В файле js/wphierarchy-ajax.js пишем следующий код:
jQuery(document).ready(function($) {
$(document).on('click', '.wphierarchy-toggle', function(e) {
e.preventDefault();
var parentId = $(this).data('parent');
var container = $('#wphierarchy-children-' + parentId);
if(container.is(':empty')) {
$.ajax({
url: wphierarchy_ajax.ajax_url,
type: 'POST',
data: {
action: 'wphierarchy_load_children',
parent_id: parentId,
nonce: wphierarchy_ajax.nonce
},
success: function(response) {
if(response.success) {
container.html(response.data.html);
container.slideDown();
} else {
alert('Ошибка загрузки данных');
}
}
});
} else {
container.slideToggle();
}
});
});Обработка AJAX-запроса на сервере
В functions.php добавляем обработчик:
function wphierarchy_ajax_load_children() {
check_ajax_referer('wphierarchy_nonce', 'nonce');
$parent_id = intval($_POST['parent_id']);
// Получаем дочерние категории
$child_categories = get_posts([
'post_type' => 'product_category',
'post_parent' => $parent_id,
'numberposts' => -1,
'orderby' => 'title',
'order' => 'ASC',
]);
// Получаем продукты, привязанные к категории
$products = get_posts([
'post_type' => 'product',
'tax_query' => [[
'taxonomy' => 'product_category',
'field' => 'term_id',
'terms' => get_post_meta($parent_id, '_term_id', true),
]],
'numberposts' => -1,
'orderby' => 'title',
'order' => 'ASC',
]);
ob_start();
if($child_categories) {
echo '<ul class="wphierarchy-category-list">';
foreach($child_categories as $cat) {
echo '<li>' . esc_html($cat->post_title) . ' <a href="#" class="wphierarchy-toggle" data-parent="' . $cat->ID . '">+</a>';
echo '<div id="wphierarchy-children-' . $cat->ID . '" class="wphierarchy-children" style="display:none;"></div>';
echo '</li>';
}
echo '</ul>';
}
if($products) {
echo '<ul class="wphierarchy-product-list">';
foreach($products as $prod) {
echo '<li>' . esc_html($prod->post_title) . '</li>';
}
echo '</ul>';
}
$html = ob_get_clean();
wp_send_json_success(['html' => $html]);
}
add_action('wp_ajax_wphierarchy_load_children', 'wphierarchy_ajax_load_children');
add_action('wp_ajax_nopriv_wphierarchy_load_children', 'wphierarchy_ajax_load_children');Обратите внимание, что для корректной работы запроса к продуктам по таксономии нужно хранить ID терма таксономии в мета-поле категории или сделать дополнительный запрос для получения term_id.
Оптимизация и дополнительные улучшения
Для больших иерархий стоит применять кеширование результатов AJAX-запросов, например, с помощью transient API WordPress или внешних кешей.
Также можно внедрить индикаторы загрузки, анимации для раскрытия списков и улучшить UX с помощью дополнительных JS-библиотек.
Для удобства администрирования можно использовать плагин Clearfy Pro, который оптимизирует AJAX-запросы и повышает безопасность сайта.
Выводы
Реализация динамической иерархии типов постов с AJAX позволяет создавать быстрые и удобные интерфейсы для сложных структур контента в WordPress. Приведённый пример — основа, которую можно расширять под свои задачи, добавляя фильтры, пагинацию, поиск и другие возможности.