Разработка кастомных типов записей (Custom Post Types) WordPress
Представьте: клиент просит раздел «Портфолио» с фильтром по годам, а стандартные записи не дают ни своего URL, ни метаполей. Мы сталкиваемся с этим регулярно. WordPress из коробки работает с двумя типами контента — «Записи» и «Страницы». Когда сайту нужны Проекты, Вакансии, Недвижимость, Отзывы или Товары, под каждый создаётся собственный тип записей (CPT). CPT даёт отдельный раздел в /wp-admin, свои URL, архивные страницы и полный контроль над структурой данных. Регистрация CPT с базовыми настройками занимает несколько часов; полноценная настройка с ЧПУ, кастомными колонками и возможностями поиска — 1–2 дня. Наш опыт — более 50 настроенных CPT для разных проектов, от портфолио до интернет-магазинов.
Как регистрировать CPT с правильными настройками?
Регистрация начинается с хука init. Внутри вызываем register_post_type с обязательными параметрами. Рассмотрим пример для типа «Проекты»:
add_action('init', function () {
register_post_type('project', [
'labels' => [
'name' => 'Проекты',
'singular_name' => 'Проект',
'add_new' => 'Добавить проект',
'add_new_item' => 'Новый проект',
'edit_item' => 'Редактировать проект',
'new_item' => 'Новый проект',
'view_item' => 'Просмотр проекта',
'search_items' => 'Поиск проектов',
'not_found' => 'Проекты не найдены',
'not_found_in_trash' => 'Корзина пуста',
'menu_name' => 'Проекты',
],
'public' => true,
'publicly_queryable' => true,
'show_ui' => true,
'show_in_menu' => true,
'query_var' => true,
'rewrite' => ['slug' => 'projects', 'with_front' => false],
'capability_type' => 'post',
'has_archive' => 'projects',
'hierarchical' => false,
'menu_position' => 5,
'menu_icon' => 'dashicons-portfolio',
'supports' => ['title', 'editor', 'thumbnail', 'excerpt', 'custom-fields'],
'show_in_rest' => true,
'rest_base' => 'projects',
]);
});
has_archive => 'projects' создаёт страницу архива по адресу /projects/ — туда попадают все записи типа. show_in_rest => true делает тип доступным через REST API и в редакторе Gutenberg. После регистрации обязательно обновите правила ЧПУ: в админке «Настройки → Постоянные ссылки» нажмите «Сохранить» либо выполните flush_rewrite_rules() при активации.
Кастомные колонки в списке записей и сортировка
По умолчанию список проектов показывает только заголовок и дату. Добавим колонки «Клиент», «Год» и «На главной» — все они сортируемые. Вот компактный блок кода, включающий регистрацию колонок, вывод данных и сортировку:
// Регистрируем колонки и выводим данные
add_filter('manage_project_posts_columns', function (array $columns): array {
$new = [];
foreach ($columns as $key => $title) {
$new[$key] = $title;
if ($key === 'title') {
$new['client'] = 'Клиент';
$new['year'] = 'Год';
$new['featured'] = 'На главной';
}
}
unset($new['comments']);
return $new;
});
add_action('manage_project_posts_custom_column', function (string $column, int $post_id): void {
switch ($column) {
case 'client':
echo esc_html(get_post_meta($post_id, '_project_client', true) ?: '—');
break;
case 'year':
echo esc_html(get_post_meta($post_id, '_project_year', true) ?: '—');
break;
case 'featured':
$is_featured = get_post_meta($post_id, '_project_featured', true);
echo $is_featured ? '⭐' : '—';
break;
}
}, 10, 2);
add_filter('manage_edit-project_sortable_columns', function (array $columns): array {
$columns['year'] = 'year';
return $columns;
});
add_action('pre_get_posts', function (WP_Query $query): void {
if (!is_admin() || !$query->is_main_query()) return;
if ($query->get('orderby') === 'year') {
$query->set('meta_key', '_project_year');
$query->set('orderby', 'meta_value_num');
}
});
Включение CPT в поиск WordPress и фильтр по таксономии
По умолчанию стандартный поиск не включает кастомные CPT. Исправляем это через pre_get_posts:
add_action('pre_get_posts', function (WP_Query $query): void {
if ($query->is_search() && !is_admin() && $query->is_main_query()) {
$post_types = $query->get('post_type') ?: ['post'];
if (!is_array($post_types)) {
$post_types = [$post_types];
}
$query->set('post_type', array_merge($post_types, ['project', 'vacancy']));
}
});
А для фильтрации списка по таксономии (например, категория проекта) добавляем выпадающий список:
add_action('restrict_manage_posts', function (string $post_type): void {
if ($post_type !== 'project') return;
$taxonomy = get_taxonomy('project_category');
wp_dropdown_categories([
'taxonomy' => 'project_category',
'name' => 'project_category',
'show_option_all' => 'Все категории',
'selected' => $_GET['project_category'] ?? 0,
'value_field' => 'slug',
'hierarchical' => true,
]);
});
Сравнение: CPT vs таксономия vs кастомное поле
Частая ошибка — использовать CPT там, где достаточно таксономии или поля. Таблица ниже поможет выбрать правильный инструмент:
| Сущность | Когда использовать | Пример |
|---|---|---|
| Custom Post Type | Нужна отдельная страница, архив, редактор контента | Проект, отзыв, вакансия |
| Таксономия | Требуется группировка или фильтрация записей | Категория проекта, тег услуги |
| Кастомное поле | Храним атрибут существующей записи | Цена товара, год проекта, адрес офиса |
CPT лучше таксономии для содержательных сущностей — он даёт отдельный URL, редактор и полный контроль. Использование таксономии вместо CPT — это потеря SEO-потенциала и ограничение контента.
Какие шаблоны нужны для корректного отображения CPT?
WordPress автоматически ищет шаблоны в определённом порядке. Для типа project приоритет такой:
| Приоритет | Шаблон | Назначение |
|---|---|---|
| 1 | single-project.php |
Отдельная запись |
| 2 | single.php |
Фолбэк для записей |
| 3 | archive-project.php |
Архив (список) |
| 4 | archive.php |
Фолбэк для архивов |
Если нужного шаблона нет, WordPress падает на index.php. Создание single-project.php и archive-project.php — базовая практика для любого CPT.
Связанные записи между разными CPT
Стандартный способ связать два CPT — хранить ID связанных записей в post meta. Например, привязываем кейсы к проекту:
// Сохранение связей
update_post_meta($project_id, '_related_cases', array_map('absint', $case_ids));
// Получение связанных кейсов
$case_ids = get_post_meta($project_id, '_related_cases', true);
if (!empty($case_ids)) {
$cases = get_posts([
'post_type' => 'case',
'post__in' => $case_ids,
'orderby' => 'post__in',
'posts_per_page' => -1,
]);
}
Для двусторонних связей (многие-ко-многим) используйте плагин Posts 2 Posts или создайте промежуточную таблицу.
Что входит в настройку CPT под ключ
Наш опыт — более 5 лет разработки на WordPress, более 50 настроенных CPT для портфолио, интернет-магазинов и каталогов. В услугу входит:
- Регистрация CPT с полным набором параметров (ЧПУ, архив, иконка, колонки);
- Кастомные колонки с сортировкой и фильтрацией;
- Интеграция с поиском и REST API;
- Создание шаблонов single-{cpt}.php и archive-{cpt}.php;
- Документация и передача доступов;
- Обучение контент-менеджеров.
Гарантируем, что все CPT будут корректно работать с Gutenberg и W3C-валидатором. Свяжитесь, чтобы обсудить ваш проект — оценим задачу за один рабочий день. Получите консультацию: мы поможем выбрать оптимальную архитектуру и реализуем CPT быстрее, чем вы напишете spec.
Почему стоит доверить настройку CPT профессионалам?
Некорректная регистрация CPT приводит к ошибкам 404, неработающему поиску и падению производительности. Мы используем проверенные паттерны: registration_hook для сброса правил, кастомные колонки без лишних запросов, правильные capability_type. Ваш сайт не потеряет в скорости — гарантируем Core Web Vitals на уровне зелёной зоны.
Для глубокого понимания механизма регистрации обратитесь к официальной документации: register_post_type. Наши специалисты всегда готовы проконсультировать и подобрать решение под ваш проект.







