В WordPress стандартно реализована иерархия страниц, но для обычных записей (постов) такой функционал отсутствует. Многие разработчики и владельцы сайтов сталкиваются с необходимостью создать структуру, в которой записи будут связаны между собой в виде родитель-потомок, то есть иерархично. В этой статье мы подробно разберём, как реализовать динамическую иерархию постов с помощью пользовательских типов записей, метаполей и таксономий, а также приведём пример кода, который поможет вам быстро внедрить такую функциональность на вашем сайте.
Почему стандартные посты в WordPress не поддерживают иерархию
По умолчанию записи в WordPress не имеют свойства "родитель" — они плоские, что упрощает организацию блога, но ограничивает возможности структурирования контента. При этом страницы поддерживают иерархию, что позволяет создавать вложенные страницы и строить дерево контента. Однако иногда требуется иерархия именно для постов, например, для новостных лент с подтемами, каталогов или обучающих материалов с разделами и подразделами.
Обычное решение — создавать кастомные типы записей с поддержкой иерархии. Но это не всегда удобно, если контент уже в стандартных постах. Поэтому мы рассмотрим способ динамического построения иерархии постов с помощью кастомных полей и мета-запросов.
Создание пользовательского поля для указания родительского поста
Первый шаг — добавить метаполе, где можно выбрать родительскую запись для каждого поста. Для этого можно использовать плагин Advanced Custom Fields (ACF) или создать метабокс вручную.
Пример создания метабокса через код
Добавим метабокс с выпадающим списком постов:
function wphierarchy_add_parent_post_metabox() {
add_meta_box(
'wphierarchy_parent_post',
'Родительский пост',
'wphierarchy_parent_post_callback',
'post',
'side',
'default'
);
}
add_action('add_meta_boxes', 'wphierarchy_add_parent_post_metabox');
function wphierarchy_parent_post_callback($post) {
wp_nonce_field('wphierarchy_save_parent_post', 'wphierarchy_parent_post_nonce');
$parent_id = get_post_meta($post->ID, '_wphierarchy_parent_post', true);
$args = [
'post_type' => 'post',
'post_status' => 'publish',
'posts_per_page' => -1,
'post__not_in' => [$post->ID], // исключаем текущий пост
];
$posts = get_posts($args);
echo '<select name="wphierarchy_parent_post" id="wphierarchy_parent_post">';
echo '<option value="">Без родителя</option>';
foreach ($posts as $p) {
$selected = $parent_id == $p->ID ? 'selected' : '';
echo "<option value=\"{$p->ID}\" $selected>{$p->post_title}</option>";
}
echo '</select>';
}
function wphierarchy_save_parent_post_meta($post_id) {
if (!isset($_POST['wphierarchy_parent_post_nonce']) || !wp_verify_nonce($_POST['wphierarchy_parent_post_nonce'], 'wphierarchy_save_parent_post')) {
return;
}
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
if (isset($_POST['wphierarchy_parent_post'])) {
$parent_post = intval($_POST['wphierarchy_parent_post']);
// Предотвращение циклов
if ($parent_post !== $post_id) {
update_post_meta($post_id, '_wphierarchy_parent_post', $parent_post);
}
}
}
add_action('save_post', 'wphierarchy_save_parent_post_meta');Данный код добавляет на страницу редактирования поста поле для выбора родителя из существующих записей. При сохранении значение сохраняется в мета-поле _wphierarchy_parent_post.
Вывод иерархии постов на фронтенде
Чтобы вывести иерархию, нужно получить все посты и построить дерево на основании сохранённых связей. Для этого проще всего сначала загрузить все посты с мета-полем, а затем рекурсивно сформировать вложенный список.
Функция для получения дочерних постов
function wphierarchy_get_children_posts($parent_id = 0, $all_posts = []) {
$children = [];
foreach ($all_posts as $post) {
$post_parent = get_post_meta($post->ID, '_wphierarchy_parent_post', true);
if (!$post_parent) $post_parent = 0;
if (intval($post_parent) === intval($parent_id)) {
$children[] = $post;
}
}
return $children;
}Функция для построения дерева
function wphierarchy_build_tree($parent_id = 0, $all_posts = []) {
$tree = '';
$children = wphierarchy_get_children_posts($parent_id, $all_posts);
if ($children) {
$tree .= '<ul>';
foreach ($children as $child) {
$tree .= '<li><a href="' . get_permalink($child->ID) . '">' . esc_html($child->post_title) . '</a>';
$tree .= wphierarchy_build_tree($child->ID, $all_posts);
$tree .= '</li>';
}
$tree .= '</ul>';
}
return $tree;
}Пример вывода иерархии в шаблоне
$args = [
'post_type' => 'post',
'posts_per_page' => -1,
'post_status' => 'publish',
];
$all_posts = get_posts($args);
echo wphierarchy_build_tree(0, $all_posts);Такой подход позволяет вывести вложенный список постов, где каждый уровень соответствует уровню иерархии. Можно стилизовать список через CSS для удобства восприятия.
Обработка циклических ссылок и другие нюансы
При создании иерархии важно учитывать, что пользователь может по ошибке указать в качестве родителя самого себя или создать циклы (например, пост А - родитель поста В, а пост В - родитель поста А). Для предотвращения таких ситуаций в функции сохранения метаполя мы проверяем, что родитель не совпадает с самим постом.
Для более сложной проверки, чтобы избежать циклов глубже, можно реализовать обход дерева при сохранении и проверять, что родитель не является потомком текущей записи. Это можно сделать, добавив рекурсивную функцию проверки.
Интеграция с плагинами WPShop.ru
Если вы хотите расширить возможности иерархии, например, добавить визуальный редактор структуры или улучшить SEO, обратите внимание на плагины с WPShop.ru, например, Clearfy Pro для оптимизации и управления метаданными, а также ABC Pagination для удобной навигации по вложенным спискам постов.
Кроме того, если на вашем сайте много контента, используйте кэширование и фильтрацию запросов, чтобы не перегружать базу данных при построении иерархии.
Заключение
Создание динамической иерархии постов в WordPress — задача, которую можно решить с помощью метаполей, кастомных функций и аккуратного построения дерева. Такой подход позволяет строить гибкие структуры контента без необходимости перехода на кастомные типы записей.
Используйте предложенный код как основу и адаптируйте под свои задачи. При необходимости расширяйте функционал, интегрируя полезные плагины и оптимизируя вывод.