WordPress из коробки умеет работать с записями (Posts) и страницами (Pages). Но что, если на вашем сайте должны быть каталог услуг, портфолио проектов, база знаний или список сотрудников? Для этого в WordPress существует механизм Custom Post Type (CPT) — собственный тип записи, у которого есть свой раздел в админке, свои шаблоны вывода и при необходимости — своя таксономия. В этой статье — подробный разбор создания CPT с нуля: функция register_post_type(), все аргументы с примерами, где размещать код и типичные ошибки.
Зачем нужен Custom Post Type
Допустим, вы делаете сайт агентства. У агентства есть:
- блог — Posts (записи), выводятся в обратном хронологическом порядке;
- страницы «О компании», «Контакты» — Pages, иерархия и статичный контент;
- база знаний — Knowledge Base, нужны свой архив, шаблон статьи и набор полей (версия ПО, категория);
- услуги — Services, нужны карточки с иконками и ценами.
Запихивать кейсы в стандартные Posts — значит получить хаос в админке и невозможность сделать отдельные шаблоны. Создав для них отдельный CPT, вы получаете:
- свой пункт меню в админке;
- свои поля (title, editor, thumbnail и т.д.);
- свою таксономию (например, «Раздел» или «Метки»);
- свои URL-адреса (
/kb/,/kb/article-slug/); - свои шаблоны в теме (
single-knowledge_base.php,archive-knowledge_base.php).
Шаг 1. Функция register_post_type()
Регистрация CPT — это один вызов register_post_type(), помещённый внутрь хука init. Хук init срабатывает после загрузки WordPress, до того, как начинается вывод страницы. Это правильное место: к этому моменту уже известны все таксономии и установленные плагины, но маршрутизация ещё не началась.
/**
* Регистрация Custom Post Type "База знаний".
* Код размещается в functions.php темы ИЛИ в отдельном плагине.
*
* @see https://developer.wordpress.org/reference/functions/register_post_type/
*/
add_action('init', function () {
register_post_type('knowledge_base', array(
'labels' => /* массив лейблов */,
'public' => true,
'has_archive' => true,
'rewrite' => array('slug' => 'kb'),
'supports' => array('title', 'editor', 'thumbnail'),
));
});
Аргумент labels
Labels — это массив строк, которые WordPress подставляет в интерфейс админки: заголовки страниц, подписи кнопок, уведомления. Всего их больше двадцати, но обязательных — три. Остальные WordPress сгенерирует автоматически, взяв за основу значение singular_name.
'labels' => array(
/* ОБЯЗАТЕЛЬНЫЕ */
'name' => 'База знаний', // множественное число — в меню, заголовки архива
'singular_name' => 'Статья базы знаний', // единственное число — на странице редактирования
/* ОСНОВНЫЕ ДОПОЛНИТЕЛЬНЫЕ */
'add_new' => 'Добавить', // текст ссылки «Добавить» в меню
'add_new_item' => 'Добавить статью', // заголовок страницы создания
'edit_item' => 'Редактировать статью', // заголовок страницы редактирования
'new_item' => 'Новая статья', // заголовок после создания
'view_item' => 'Смотреть статью', // кнопка «Просмотреть» в админке
'view_items' => 'Смотреть статьи', // для экрана архива
'search_items' => 'Искать в базе знаний', // текст в поле поиска админки
'not_found' => 'Статьи не найдены', // пустой архив
'not_found_in_trash' => 'В корзине пусто', // пустая корзина
/* МЕНЮ */
'all_items' => 'Все статьи', // подпункт меню
'archives' => 'Архив базы знаний', // подпункт меню
'attributes' => 'Атрибуты статьи', // подпункт меню (появляется если есть supports 'page-attributes')
/* ПРОЧИЕ */
'featured_image' => 'Обложка статьи', // в настройках экрана
'set_featured_image' => 'Установить обложку',
'remove_featured_image' => 'Удалить обложку',
'use_featured_image' => 'Использовать как обложку',
),
name, singular_name, add_new — это ключи массива, они всегда на английском. Переводить нужно только строковые значения справа.
Аргумент public
public — главный выключатель CPT. Он определяет:
- видимость записей в админке (показывать в меню или скрыть);
- видимость на сайте (доступны ли по прямому URL);
- нужен ли отдельный шаблон в теме;
- участвует ли тип в поиске.
// Полная публичность — записи видны везде
'public' => true,
// CPT существует, но скрыт из админского меню и недоступен по прямым URL.
// Используется, когда записи создаются только программно (например, демо-контент).
'public' => false,
// Ярлык (true/false) — URL останется рабочим, но в админке пункт не появится.
'publicly_queryable' => true, // отвечать на ?post_type=projects
'show_ui' => true, // показывать интерфейс в админке
'show_in_nav_menus' => false, // не показывать в навигации
'public' => false, но 'show_ui' => true и 'show_in_menu' => true — это «скрытый» тип, который нужен только для админки. Например, виджеты или импортированные данные.
Аргумент has_archive
Если true — WordPress создаст страницу архива по адресу /kb/. На ней будут выводиться все записи этого CPT в обратном хронологическом порядке, как на странице блога.
// Архив будет доступен по адресу /kb/
'has_archive' => true,
// Архив будет доступен по адресу /portfolio/ (кастомный ярлык)
'has_archive' => 'portfolio',
// Без архива — например, для «лидогенераторов» или виджетов
'has_archive' => false,
Аргумент rewrite
rewrite определяет, по каким URL будут доступны одиночная запись и архив.
'rewrite' => array(
'slug' => 'kb', // префикс URL: /kb/%postname%
'with_front' => false, // не добавлять перед ярлык префикс темы (/blog/kb/)
'feeds' => true, // включать CPT в RSS-ленту сайта
'pages' => true, // поддержка нумерации страниц (/kb/page/2/)
'ep_mask' => EP_PERMALINK, // маска «конечной точки» (Endpoint mask). EP_PERMALINK означает,
// что правило сработает на том же уровне, что и обычные записи.
),
После изменения slug нужно перейти в Настройки → Постоянные ссылки и нажать «Сохранить изменения» — это сбросит правила перезаписи URL.
Аргумент supports
supports — массив фич, которые будут доступны на странице редактирования записи. Это то, что вы отмечаете галочками в настройках экрана.
'supports' => array(
'title', // заголовок записи (обязательно — без него WP не даст сохранить)
'editor', // визуальный редактор (TinyMCE или Гутенберг)
'thumbnail', // миниатюра записи (если тема поддерживает post-thumbnails)
'excerpt', // цитата / отрывок
'author', // выбор автора
'trackbacks', // устаревший pingbacks — лучше отключить
'custom-fields', // произвольные поля (CMB2 / ACF их заменяют)
'comments', // комментарии
'revisions', // хранить ревизии (версии) записи
'page-attributes', // атрибуты страницы: родитель и порядок (menu_order).
// Примечание: поле «Порядок» показывается ТОЛЬКО если
// 'page-attributes' добавлен в supports И 'hierarchical' => true.
// Для CPT с иерархией страниц это нормально, для постов — избыточно.
'post-formats', // форматы записи (aside, gallery, quote, etc.)
),
['title', 'editor']. Миниатюру добавляйте только если тема её выводит.
Аргумент menu_icon и menu_position
'menu_icon' => 'dashicons-portfolio', // иконка из набора Dashicons
// Полный список: https://developer.wordpress.org/resource/dashicons/
// Примеры: dashicons-book-alt, dashicons-building, dashicons-star-filled
// Все доступные позиции меню (числа примерные):
// 2 — Dashboard
// 4 — Separator
// 5 — Записи
// 10 — Медиафайлы
// 20 — Страницы
// 25 — Separator
// 59 — Комментарии
// 60 — Separator
// 65 — Плагины
// 70 — Пользователи
// 75 — Инструменты
// 80 — Настройки
// 99 — Separator
// Ставим после «Страницы» (20) и до комментариев (59)
'menu_position' => 21,
Аргумент capabilities
По умолчанию CPT наследует capabilities стандартной записи (edit_post, delete_post и т.д.). Это значит, что редактор сайта может редактировать ваш CPT «из коробки». Но если нужно разделить права — например, чтобы контент-менеджер видел статьи базы знаний, но не мог их удалять — используйте кастомные capabilities.
'capability_type' => 'kb_article', // базовое имя для роли (edit_kb_article, delete_kb_article, etc.)
// ИЛИ более гибкий вариант — задаём каждую capability отдельно:
'capabilities' => array(
'edit_post' => 'edit_kb_article',
'read_post' => 'read_kb_article',
'delete_post' => 'delete_kb_article',
'edit_posts' => 'edit_kb_articles',
'edit_others_posts' => 'edit_others_kb_articles',
'publish_posts' => 'publish_kb_articles',
'read_private_posts' => 'read_private_kb_articles',
// 'delete_posts', 'delete_others_posts', 'delete_private_posts' —
// WordPress сгенерирует автоматически по аналогии с edit_post
),
После добавления capabilities нужно добавить их к роли Редактор через add_cap(). Лучше делать это в хуке admin_init с проверкой, чтобы не добавлять повторно при каждой загрузке страницы.
add_action('admin_init', function () {
$role = get_role('editor');
if ($role && !$role->has_cap('edit_kb_articles')) {
$role->add_cap('edit_kb_articles');
$role->add_cap('edit_others_kb_articles');
$role->add_cap('publish_kb_articles');
$role->add_cap('delete_kb_articles');
$role->add_cap('delete_others_kb_articles');
}
});
Аргумент taxonomies
Сюда передаётся массив ярлыков таксономий, которые должны быть привязаны к CPT. Это не регистрирует таксономии — они уже должны быть зарегистрированы раньше (на том же или более раннем хуке init).
'taxonomies' => array('kb_section', 'kb_tag'),
Если таксономия ещё не существует, WordPress просто проигнорирует ярлык. Привязка «на лету» во время регистрации CPT — это просто удобная короткая запись. То же самое можно сделать после регистрации:
// Привязать таксономию к CPT после регистрации CPT
add_action('init', function () {
register_taxonomy_for_object_type('kb_section', 'knowledge_base');
}, 20); // приоритет 20 — после регистрации таксономии и CPT
Аргумент show_in_rest и REST API
Начиная с WordPress 5.0, REST API включён по умолчанию. Чтобы CPT был доступен через /wp-json/wp/v2/kb, нужно:
'show_in_rest' => true, // добавить эндпоинты в REST API (обязательно для Гутенберга!)
'rest_base' => 'kb', // базовый ярлык в URL: /wp/v2/kb
'rest_namespace' => 'wp/v2', // неймспейс. wp/v2 — стандартный; свой — для кастомных эндпоинтов
Шаг 2. Полный рабочий пример
Ниже — законченный код регистрации CPT «База знаний» с комментариями. Его можно вставить в functions.php дочерней темы или в свой плагин.
/**
* Регистрация Custom Post Type «База знаний» (knowledge_base).
*
* Код должен быть подключён на хуке init.
* Все строки лейблов — на русском, ключи массива — на английском.
*/
add_action('init', function () {
register_post_type('knowledge_base', array(
/* === ЛЕЙБЛЫ === */
'labels' => array(
'name' => 'База знаний',
'singular_name' => 'Статья базы знаний',
'add_new' => 'Добавить',
'add_new_item' => 'Новая статья',
'edit_item' => 'Редактировать статью',
'new_item' => 'Новая статья',
'view_item' => 'Смотреть статью',
'search_items' => 'Искать в базе знаний',
'not_found' => 'Статьи не найдены',
'not_found_in_trash' => 'В корзине пусто',
'all_items' => 'Все статьи',
'archives' => 'Архив базы знаний',
),
/* === ПУБЛИЧНОСТЬ === */
// public — главный выключатель: видимость в админке, доступность по URL,
// участие в поиске, генерация шаблонов темы.
'public' => true,
// Показывать ли пункт в навигационном меню сайта (wp_nav_menu()).
// true — CPT появится в настройках меню как «База знаний».
'show_in_nav_menus' => true,
// Показывать ли в меню админки (список «Записи», «Страницы» и т.д.).
// По умолчанию равно значению public, если не задано явно.
'show_ui' => true,
// Позволять ли обращаться к CPT через ?post_type=knowledge_base в URL.
// Отключение = CPT недоступен по прямой ссылке, но виден в админке.
'publicly_queryable' => true,
// Исключить из результатов поиска сайта (если false — CPT не ищется).
'exclude_from_search' => false,
/* === АДРЕСА === */
// Архив: /kb/
'has_archive' => true,
// Ярлык в URL: /kb/nastrojka-pochty/
'rewrite' => array(
'slug' => 'kb',
// with_front: не добавлять префикс темы (/blog/kb/) —
// актуально если для записей настроен префикс /blog/
'with_front' => false,
// feeds: включить RSS для архива CPT
'feeds' => true,
// pages: поддержка /kb/page/2/
'pages' => true,
),
/* === ИНТЕРФЕЙС РЕДАКТИРОВАНИЯ === */
// Фичи, доступные на странице редактирования записи.
// Минимум — ['title'], иначе WP не даст сохранить запись.
'supports' => array(
'title', // заголовок (обязательно)
'editor', // текстовый редактор
'thumbnail', // миниатюра
'excerpt', // цитата
'author', // выбор автора
// 'page-attributes' — нужен только если hierarchical => true (иерархия как у страниц)
// 'revisions' — хранить ревизии (полезно для длинных статей)
),
// Иконка в меню админки — из набора Dashicons.
// Полный список: https://developer.wordpress.org/resource/dashicons/
'menu_icon' => 'dashicons-book-alt',
// Позиция в меню. 21 = после «Страницы» (20).
// Варианты: 5 — после «Консоли», 10 — после «Медиа», 20 — после «Страниц», 59 — после «Комментариев», 65 — после «Плагинов»
'menu_position' => 21,
/* === REST API === */
// show_in_rest = true делает CPT видимым в Гутенберге и в /wp-json/wp/v2/kb
'show_in_rest' => true,
// База в URL REST: /wp/v2/kb
'rest_base' => 'kb',
/* === ИЕРАРХИЯ === */
// hierarchical = false — как Записи (Post): без вложенности, дата вместо меню.
// hierarchical = true — как Страницы (Page): можно выбрать родителя.
// Важно: если hierarchical => true, в supports полезно добавить 'page-attributes',
// чтобы появилось поле «Родитель» и «Порядок».
'hierarchical' => false,
/* === ЯЗЫК === */
// Позволяет плагинам локализации (Polylang, WPML) корректно переводить CPT.
// Для Polylang это обязательно.
'language' => 'ru_RU',
));
}, 5); // приоритет 5 — регистрируем рано, чтобы другие таксономии успели подключиться
Шаг 3. Где размещать код
Есть три варианта:
1. functions.php темы
Самый простой способ. Код добавляется прямо в functions.php активной темы. Если тема простая и не обновляется — этого достаточно.
Вариант А: код напрямую
// functions.php темы
add_action('init', function () {
register_post_type('projects', array(
// ...
));
});
Вариант Б: подключение через include
Если код большой или вы хотите держать его в отдельном файле (удобнее для редактирования, можно версионировать), создайте файл и подключите его:
// В functions.php темы — одна строка:
require_once get_template_directory() . '/inc/cpt-knowledge-base.php';
// Создайте файл: /wp-content/themes/ВАША_ТЕМА/inc/cpt-knowledge-base.php
// В нём — только регистрация CPT и таксономий, без add_action wrapper:
<?php
if (!defined('ABSPATH')) { exit; }
/**
* CPT «База знаний» и таксономия «Раздел»
*/
add_action('init', 'dk_register_kb_cpt', 5);
function dk_register_kb_cpt() {
register_post_type('knowledge_base', array(
// ...
));
}
Нюансы: когда это безопасно, а когда — нет
| Ситуация | Рекомендация |
|---|---|
| Тема из репозитория WordPress.org (бесплатная) | Никогда не добавляйте код напрямую. При обновлении темы из админки всё потеряется. Используйте дочернюю тему. |
| Коммерческая тема (ThemeForest, TemplateMonster и т.д.) | Обновляется через панель хостинга или личный кабинет. При обновлении — код в functions.php потеряется. Дочерняя тема — обязательно. |
| Своя кастомная тема, которую никто не обновляет | Можно добавлять напрямую. Но если проект перейдёт другому разработчику — он может не знать о ваших изменениях. Лучше сразу структурировать. |
| Сайт-визитка клиента, без планов на развитие | Допустимо, если вы уверены, что тема не обновится. Но предупредите клиента, что при обновлении темы код нужно будет вернуть. |
functions.php тесно связан с темой. Если тема обновляется — ваш код исчезает. Если тема меняется — ваш код перестаёт работать. Для любого проекта, который должен жить больше месяца, используйте дочернюю тему или плагин.
2. Дочерняя тема (рекомендуется для клиентских сайтов)
Создайте дочернюю тему и добавьте код в её functions.php. При обновлении родительской темы ваши изменения сохранятся.
// functions.php дочерней темы
add_action('init', 'dk_register_kb_cpt');
function dk_register_kb_cpt() {
register_post_type('knowledge_base', array(
// ... массив аргументов
));
}
3. Собственный плагин (рекомендуется для переиспользуемого кода)
Если CPT логически не привязан к теме (например, вы разрабатываете плагин базы знаний, который будет установлен на разные сайты), создайте плагин:
<?php
/**
* Plugin Name: DK Knowledge Base
* Description: Регистрирует CPT «База знаний» и таксономию «Раздел»
* Version: 1.0.0
* Author: Денис К.
* License: GPL v2
*/
// Защита от прямого доступа
if (!defined('ABSPATH')) { exit; }
add_action('init', 'dk_register_kb_cpt', 5);
function dk_register_kb_cpt() {
register_post_type('knowledge_base', array(
// ...
));
}
Шаг 4. Регистрация таксономии для CPT
Таксономия — это способ группировать записи внутри CPT. Различают иерархическую (как рубрики: «Настройка сервера», «Ошибки», «Интеграции», с возможностью вложенности) и неиерархическую (как метки: «Ubuntu», «PHP 8», «Битрикс24»).
add_action('init', function () {
/* ИЕРАРХИЧЕСКАЯ ТАКСОНОМИЯ — рубрики */
register_taxonomy('kb_section', 'knowledge_base', array(
'labels' => array(
'name' => 'Разделы',
'singular_name' => 'Раздел',
'add_new_item' => 'Добавить раздел',
'edit_item' => 'Редактировать раздел',
'search_items' => 'Искать разделы',
'not_found' => 'Разделы не найдены',
),
// Иерархическая: как рубрики, с вложенностью
'hierarchical' => true,
// Показывать в REST API
'show_in_rest' => true,
// Ярлык в URL: /section/nastrojka-servera/
'rewrite' => array('slug' => 'section'),
// CPT, к которым привязана таксономия. Можно указать несколько: array('knowledge_base', 'services')
'object_type' => array('knowledge_base'),
));
/* НЕИЕРАРХИЧЕСКАЯ ТАКСОНОМИЯ — метки */
register_taxonomy('kb_tag', 'knowledge_base', array(
'labels' => array(
'name' => 'Метки',
'singular_name' => 'Метка',
),
// Неиерархическая: как метки, без вложенности
'hierarchical' => false,
'show_in_rest' => true,
'rewrite' => array('slug' => 'kb-tag'),
'object_type' => array('knowledge_base'),
));
}, 10); // приоритет 10 — после CPT (CPT зарегистрирован с 5)
Шаг 5. Шаблоны темы
WordPress автоматически ищет шаблоны для CPT по цепочке:
single-{post_type}.php— одиночная запись. Например,single-knowledge_base.php.single.php— общий шаблон одиночной записи.singular.php— любой единичный тип.
Для архива:
archive-{post_type}.php— архив. Например,archive-knowledge_base.php.archive.php— общий архив.index.php— главная.
Пример минимального single-knowledge_base.php:
<?php
/**
* Шаблон одиночной записи CPT «knowledge_base».
* Файл: single-knowledge_base.php
*/
get_header();
while (have_posts()) {
the_post();
// Получаем данные
$version = get_post_meta(get_the_ID(), 'kb_version', true);
$section = get_the_terms(get_the_ID(), 'kb_section');
?>
<article class="kb-article">
<header class="kb-article__header">
<?php the_post_thumbnail('large', array('class' => 'kb-article__thumb')); ?>
<h1><?php the_title(); ?></h1>
</header>
<div class="kb-article__meta">
<?php if ($version) : ?>
<span>Версия: <?php echo esc_html($version); ?></span>
<?php endif; ?>
<?php if ($section && !is_wp_error($section)) : ?>
<span>Раздел: <?php echo esc_html($section[0]->name); ?></span>
<?php endif; ?>
</div>
<div class="kb-article__content">
<?php the_content(); ?>
</div>
<?php
// Выводим метки
$tags = get_the_terms(get_the_ID(), 'kb_tag');
if ($tags && !is_wp_error($tags)) {
echo '<ul class="kb-article__tags">';
foreach ($tags as $tag) {
echo '<li><a href="' . esc_url(get_term_link($tag)) . '">'
. esc_html($tag->name) . '</a></li>';
}
echo '</ul>';
}
?>
</article>
<?php
}
get_footer();
Шаг 6. Вывод CPT в запросе (WP_Query)
Чтобы вывести записи CPT на странице темы (не в шаблоне архива, а в произвольном месте), используйте WP_Query:
/**
* Вывод 6 последних статей базы знаний в сетке.
*/
$args = array(
'post_type' => 'knowledge_base', // наш CPT
'posts_per_page' => 6, // сколько показать (-1 = все)
'orderby' => 'date', // по дате
'order' => 'DESC', // от новых к старым
// Альтернативные варианты сортировки:
// 'orderby' => 'title', 'order' => 'ASC' — по алфавиту
// 'orderby' => 'meta_value_num', 'meta_key' => 'kb_version', 'order' => 'DESC' — по числовому мета-полю
// 'orderby' => 'rand' — случайный порядок
// Получить только статьи из раздела «Настройка сервера»
// 'tax_query' => array(
// array(
// 'taxonomy' => 'kb_section',
// 'field' => 'slug',
// 'terms' => 'nastrojka-servera',
// ),
// ),
);
$query = new WP_Query($args);
if ($query->have_posts()) {
echo '<div class="kb-grid">';
while ($query->have_posts()) {
$query->the_post();
?>
<article class="kb-card">
<a href="<?php the_permalink(); ?>">
<?php the_post_thumbnail('medium', array('class' => 'kb-card__img')); ?>
<h3><?php the_title(); ?></h3>
</a>
<?php the_excerpt(); ?>
</article>
<?php
}
echo '</div>';
// Пагинация (для архивных страниц)
echo paginate_links(array(
'total' => $query->max_num_pages,
'current' => max(1, get_query_var('paged')),
));
} else {
echo '<p>Статьи скоро появятся.</p>';
}
// Обязательно восстановить оригинальный запрос!
wp_reset_postdata();
Типичные ошибки
Ошибка 1. CPT не появляется в меню
Причина — public установлен в false или show_ui в false. Проверьте:
'public' => true,
'show_ui' => true,
Ошибка 2. CPT не открывается по URL
Причина — не сброшены постоянные ссылки. Перейдите в Настройки → Постоянные ссылки и нажмите «Сохранить».
Ошибка 3. After registration, taxonomy not attached
Таксономия регистрируется после CPT — в итоге taxonomies в CPT не находит её ярлык. Решение: регистрируйте таксономию на более позднем хуке, либо вызывайте register_taxonomy_for_object_type() после регистрации обоих.
// Правильный порядок: таксономия — раньше (ниже приоритет), CPT — позже
add_action('init', 'dk_register_kb_section_tax', 3); // приоритет 3
add_action('init', 'dk_register_kb_cpt', 5); // приоритет 5
Ошибка 4. Имя CPT совпадает с зарезервированным
WordPress резервирует ряд ярлыков: post, page, attachment, revision, nav_menu_item, custom_css, customize_changeset, action, author, order, theme. Использование любого из них вызовет ошибку.
Ошибка 5. CPT виден, но шаблон не подключается
Убедитесь, что имя файла точно соответствует ярлыку CPT: single-knowledge_base.php, не single-kb.php. Также проверьте, что файл лежит в корне темы (не в подпапке).
Шаг 7. Расширенные возможности
CPT с иерархией (как страницы)
'hierarchical' => true, // можно выбирать родителя
'supports' => array('title', 'editor', 'page-attributes', 'thumbnail'),
// page-attributes даёт:
// 1) «Родитель» — выпадающий список всех записей этого CPT
// 2) «Порядок» — числовое поле menu_order (удобно для ручной сортировки без плагинов)
CPT только для REST (без публичной части)
'public' => false,
'show_ui' => true,
'show_in_menu' => true,
'publicly_queryable' => false, // не доступен по URL
'show_in_rest' => true, // но доступен через REST API
// Подходит для хранения данных, которые создаются и редактируются в админке,
// а на сайт выводятся через API или шорткод.
CPT со своей колонкой в админке
// Добавляем колонку «Версия ПО» в список записей CPT
add_filter('manage_knowledge_base_posts_columns', function ($columns) {
// Вставляем колонку после «Заголовок»
$new = array();
foreach ($columns as $key => $value) {
$new[$key] = $value;
if ($key === 'title') {
$new['kb_version'] = 'Версия';
}
}
return $new;
});
// Заполняем колонку данными
add_action('manage_knowledge_base_posts_custom_column', function ($column, $post_id) {
if ($column === 'kb_version') {
$version = get_post_meta($post_id, 'kb_version', true);
echo $version ? esc_html($version) : '—';
}
}, 10, 2);
Шаг 8. Rewrite Rules: как работают URL
Когда вы регистрируете CPT с 'rewrite' => array('slug' => 'kb'), WordPress автоматически добавляет правила перезаписи URL. Иногда нужно добавить свои — например, для пагинации вложенных таксономий или для «красивых» URL вида /kb/tag/php8/.
Что добавляет WordPress автоматически
Для CPT knowledge_base с rewrite => array('slug' => 'kb') WordPress создаёт примерно такие правила:
// Архив: /kb/
/kb/?$ -> index.php?post_type=knowledge_base
// Одиночная запись: /kb/nastrojka-php8/
/kb/([^/]+)/?$ -> index.php?post_type=knowledge_base&name=$matches[1]
// Пагинация архива: /kb/page/2/
/kb/page/([0-9]{1,})/?$ -> index.php?post_type=knowledge_base&paged=$matches[1]
// Фид: /kb/feed/
/kb/feed/(feed|rdf|rss|rss2|atom)/?$ -> index.php?post_type=knowledge_base&feed=$matches[1]
/kb/feed/?$ -> index.php?post_type=knowledge_base&feed=rss2
Добавление своего правила
Если нужен URL вида /kb/tag/php8/ (таксономия в URL), используйте хук generate_rewrite_rules:
/**
* Добавляем правило /kb/tag/%term%/ для таксономии kb_tag.
* URL: /kb/tag/php8/
*/
add_filter('generate_rewrite_rules', function ($wp_rewrite) {
// Правило для архива таксономии
$wp_rewrite->rules = array(
// Наш паттерн: /kb/tag/ИМЯ_МЕТКИ/
'kb/tag/([^/]+)/?$' => 'index.php?kb_tag=$matches[1]',
// Пагинация: /kb/tag/php8/page/2/
'kb/tag/([^/]+)/page/([0-9]{1,})/?$' => 'index.php?kb_tag=$matches[1]&paged=$matches[2]',
) + $wp_rewrite->rules;
});
Проверка правил
Чтобы посмотреть все активные правила перезаписи, добавьте временный код:
add_action('init', function () {
global $wp_rewrite;
// Выведет все правила в debug-лог
error_log(print_r($wp_rewrite->rules, true));
}, 999);
Или используйте плагин Rewrite Rules Inspector — он покажет все правила в админке и позволит отфильтровать их по CPT.
Заключение
Custom Post Type — это один из самых мощных инструментов WordPress. Он позволяет выйти за рамки «блог-платформы» и построить на WordPress каталог, портфолио, базу знаний, список вакансий или что угодно ещё.
Ключевые моменты, которые стоит запомнить:
- Регистрация CPT — одна функция
register_post_type()на хукеinitс приоритетом 5–10. - Обязательные элементы массива аргументов —
labels(хотя быnameиsingular_name),publicиsupports. - Не забывайте сбрасывать постоянные ссылки после изменения
slugилиhas_archive. - Для REST API и Гутенберга обязательно
'show_in_rest' => true. - Таксономии регистрируйте раньше CPT (на более низком приоритете хука) или привязывайте после через
register_taxonomy_for_object_type(). - Код CPT лучше держать в дочерней теме или собственном плагине — не в родительской теме.
Полный рабочий код из этой статьи можно взять за основу для любого проекта. Замените ярлыки, slug, supports и таксономии на свои — и через пять минут у вас в админке появится новый раздел, готовый к наполнению.
Комментарии
Оставить комментарий
ававпп
парарапрар