Как создать и настроить пользовательские типы записей (Custom Post Types) в WordPress: полное руководство для начинающих

Введение: почему стандартных записей и страниц недостаточно
Стандартные «Записи» и «Страницы» WordPress отлично подходят для блогов и простых сайтов, но что если вы создаете сайт с портфолио, каталогом недвижимости, базой знаний или интернет-магазином? В этом случае вам нужны отдельные типы контента со своими полями, категориями и шаблонами отображения. Именно здесь на помощь приходят пользовательские типы записей (Custom Post Types). В этой статье вы узнаете, как создавать и настраивать их правильно.

Краткое содержание

1. Что такое пользовательские типы записей и зачем они нужны

1.1 Понимание концепции

Пользовательские типы записей (Custom Post Types, CPT) — это способ расширить стандартную типологию контента WordPress, добавив собственные сущности, такие как:

  • Портфолио (работы, проекты)
  • Товары (для интернет-магазина, даже без WooCommerce)
  • Недвижимость (квартиры, дома, участки)
  • Сотрудники (карточки команды)
  • Отзывы клиентов
  • Мероприятия (события, вебинары)
  • Рецепты (кулинарные сайты)
  • Кейсы (примеры работ)

1.2 Преимущества использования CPT

  • Организация контента: Все элементы одного типа хранятся отдельно
  • Удобство управления: Отдельный раздел в админ-панели
  • Гибкость отображения: Свои шаблоны для каждого типа
  • Чистота базы данных: Не смешивается с обычными записями
  • SEO-оптимизация: Четкая структура URL и логика сайта

1.3 Чем CPT отличается от таксономий

  • CPT: Отдельные сущности (например, «Книги»)
  • Таксономии: Способ группировки этих сущностей (например, «Жанры книг» — категории, «Авторы» — метки)

2. Способы создания пользовательских типов записей

2.1 Способ 1: Через плагин (для новичков)

Custom Post Type UI (бесплатно)
Самый популярный плагин для создания CPT без программирования.

Установка:

Плагины → Добавить новый → Custom Post Type UI → Установить → Активировать

Создание нового типа записи:

CPT UI → Добавить/изменить типы записей

Основные поля для заполнения:

  • Название типа записи (slug): portfolio (только латиница, нижний регистр)
  • Множественное название: Портфолио
  • Единственное название: Работа

Дополнительные настройки:

  • Поддержка: заголовок, редактор, миниатюра, комментарии
  • Таксономии: категории, метки (или свои)
  • Показывать в меню: Да
  • Иерархический: Нет (как записи) / Да (как страницы)

Podcasts (бесплатно + PRO)
Более мощный инструмент, позволяющий создавать не только CPT, но и пользовательские поля, таксономии и шаблоны.

Преимущества:

  • Визуальный конструктор
  • Свои шаблоны отображения
  • Интеграция с ACF
  • Экспорт/импорт настроек

2.2 Способ 2: Через functions.php (для разработчиков)

Добавьте следующий код в файл functions.php вашей темы (лучше дочерней):

php
function create_portfolio_post_type() { $labels = array( 'name' => 'Портфолио', 'singular_name' => 'Работа', 'menu_name' => 'Портфолио', 'add_new' => 'Добавить работу', 'add_new_item' => 'Добавить новую работу', 'edit_item' => 'Редактировать работу', 'new_item' => 'Новая работа', 'view_item' => 'Просмотреть работу', 'search_items' => 'Искать работы', 'not_found' => 'Работ не найдено', 'not_found_in_trash' => 'В корзине работ не найдено' ); $args = array( 'labels' => $labels, 'public' => true, 'publicly_queryable' => true, 'show_ui' => true, 'show_in_menu' => true, 'query_var' => true, 'rewrite' => array('slug' => 'portfolio'), 'capability_type' => 'post', 'has_archive' => true, 'hierarchical' => false, 'menu_position' => 5, 'menu_icon' => 'dashicons-portfolio', 'supports' => array('title', 'editor', 'thumbnail', 'excerpt'), 'taxonomies' => array('category', 'post_tag'), 'show_in_rest' => true, // Для Gutenberg ); register_post_type('portfolio', $args); } add_action('init', 'create_portfolio_post_type');

2.3 Создание пользовательских таксономий

Таксономии нужны для группировки элементов CPT (категории, метки, типы, жанры).

Через плагин CPT UI:

text
CPT UI → Добавить/изменить таксономии
  • Название таксономии (slug): project_type
  • Множественное название: Типы проектов
  • Единственное название: Тип проекта
  • Прикрепить к типу записи: portfolio

Через functions.php:

php
function create_project_taxonomy() { $labels = array( 'name' => 'Типы проектов', 'singular_name' => 'Тип проекта', 'search_items' => 'Искать типы', 'all_items' => 'Все типы', 'parent_item' => 'Родительский тип', 'parent_item_colon' => 'Родительский тип:', 'edit_item' => 'Редактировать тип', 'update_item' => 'Обновить тип', 'add_new_item' => 'Добавить новый тип', 'new_item_name' => 'Название нового типа', 'menu_name' => 'Типы проектов', ); $args = array( 'labels' => $labels, 'hierarchical' => true, // true = как категории, false = как метки 'public' => true, 'show_ui' => true, 'show_admin_column' => true, 'query_var' => true, 'rewrite' => array('slug' => 'project-type'), 'show_in_rest' => true, ); register_taxonomy('project_type', array('portfolio'), $args); } add_action('init', 'create_project_taxonomy');

3. Настройка отображения пользовательских типов записей

3.1 Шаблоны для CPT

WordPress использует иерархию шаблонов для отображения CPT:

Архив (список всех записей):

  • archive-portfolio.php — для типа записи «portfolio»
  • archive.php — общий архив
  • index.php

Одиночная запись:

  • single-portfolio.php — для типа записи «portfolio»
  • single.php
  • index.php

Таксономия:

  • taxonomy-project_type.php — для таксономии «project_type»
  • taxonomy.php
  • archive.php
  • index.php

3.2 Пример шаблона archive-portfolio.php

php
<?php get_header(); ?> <div class="portfolio-archive"> <h1>Наши работы</h1> <?php if (have_posts()) : ?> <div class="portfolio-grid"> <?php while (have_posts()) : the_post(); ?> <article class="portfolio-item"> <?php if (has_post_thumbnail()) : ?> <a href="<?php the_permalink(); ?>"> <?php the_post_thumbnail('medium'); ?> </a> <?php endif; ?> <h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2> <?php  // Вывод таксономии $terms = get_the_terms(get_the_ID(), 'project_type'); if ($terms && !is_wp_error($terms)) : $term_list = array(); foreach ($terms as $term) { $term_list[] = '<a href="' . get_term_link($term) . '">' . $term->name . '</a>'; } echo '<div class="project-types">' . implode(', ', $term_list) . '</div>'; endif; ?> <div class="excerpt"> <?php the_excerpt(); ?> </div> <a href="<?php the_permalink(); ?>" class="read-more">Подробнее</a> </article> <?php endwhile; ?> </div> <?php the_posts_pagination(); ?> <?php else : ?> <p>Работ пока нет.</p> <?php endif; ?> </div> <?php get_footer(); ?>

3.3 Пример шаблона single-portfolio.php

php
<?php get_header(); ?> <?php while (have_posts()) : the_post(); ?> <article class="portfolio-single"> <h1><?php the_title(); ?></h1> <?php if (has_post_thumbnail()) : ?> <div class="featured-image"> <?php the_post_thumbnail('large'); ?> </div> <?php endif; ?> <div class="portfolio-meta"> <?php  $terms = get_the_terms(get_the_ID(), 'project_type'); if ($terms && !is_wp_error($terms)) : echo '<div class="types">Тип: '; $term_list = array(); foreach ($terms as $term) { $term_list[] = $term->name; } echo implode(', ', $term_list); echo '</div>'; endif; ?> <div class="date">Дата: <?php echo get_the_date(); ?></div> </div> <div class="portfolio-content"> <?php the_content(); ?> </div> <?php  // Навигация по соседним записям the_post_navigation(array( 'prev_text' => '← Предыдущая работа', 'next_text' => 'Следующая работа →', )); ?> <?php comments_template(); ?> </article> <?php endwhile; ?> <?php get_footer(); ?>

4. Добавление пользовательских полей (Custom Fields)

4.1 Advanced Custom Fields (ACF) — лучший плагин для полей

Установка ACF:

text
Плагины → Добавить новый → Advanced Custom Fields → Установить → Активировать

Создание группы полей:

text
ACF → Группы полей → Добавить новую
  • Название группы: Данные для портфолио
  • Правила локации: Тип записи = Портфолио

Добавление полей:

  1. Текст: Ссылка на проект
  2. Изображение: Логотип клиента
  3. Число: Стоимость проекта
  4. Выбор: Статус (завершен, в работе)
  5. WYSIWYG: Дополнительное описание

Вывод полей в шаблоне:

php
<?php  // В single-portfolio.php $project_url = get_field('project_url'); $client_logo = get_field('client_logo'); $project_cost = get_field('project_cost'); $project_status = get_field('project_status'); ?> <?php if ($project_url) : ?> <div class="project-link"> <a href="<?php echo esc_url($project_url); ?>" target="_blank">Посмотреть проект</a> </div> <?php endif; ?> <?php if ($client_logo) : ?> <div class="client-logo"> <img src="<?php echo esc_url($client_logo['url']); ?>" alt="<?php echo esc_attr($client_logo['alt']); ?>"> </div> <?php endif; ?> <?php if ($project_cost) : ?> <div class="project-cost">Бюджет: <?php echo number_format($project_cost); ?></div> <?php endif; ?>

4.2 Поля без плагина (для разработчиков)

Добавление метабокса:

php
function add_portfolio_metabox() {
    add_meta_box(
        'portfolio_details',
        'Детали проекта',
        'render_portfolio_metabox',
        'portfolio',
        'normal',
        'default'
    );
}
add_action('add_meta_boxes', 'add_portfolio_metabox');

function render_portfolio_metabox($post) {
    wp_nonce_field('portfolio_details_nonce', 'portfolio_nonce');
    
    $project_url = get_post_meta($post->ID, '_project_url', true);
    ?>
    <p> <label for="project_url">Ссылка на проект:</label> <input type="url" id="project_url" name="project_url" value="<?php echo esc_attr($project_url); ?>" class="widefat"> </p> <?php } function save_portfolio_metabox($post_id) { if (!isset($_POST['portfolio_nonce']) || !wp_verify_nonce($_POST['portfolio_nonce'], 'portfolio_details_nonce')) { return; } if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return; } if (!current_user_can('edit_post', $post_id)) { return; } if (isset($_POST['project_url'])) { update_post_meta($post_id, '_project_url', esc_url_raw($_POST['project_url'])); } } add_action('save_post', 'save_portfolio_metabox');

5. Интеграция с виджетами и меню

5.1 Добавление CPT в виджеты

Последние записи для CPT:

php
class Recent_Portfolio_Widget extends WP_Widget {
    public function __construct() {
        parent::__construct(
            'recent_portfolio',
            'Последние работы портфолио',
            array('description' => 'Показывает последние работы из портфолио')
        );
    }
    
    public function widget($args, $instance) {
        echo $args['before_widget'];
        echo $args['before_title'] . $instance['title'] . $args['after_title'];
        
        $query = new WP_Query(array(
            'post_type' => 'portfolio',
            'posts_per_page' => $instance['count'],
            'orderby' => 'date',
            'order' => 'DESC'
        ));
        
        if ($query->have_posts()) : ?>
            <ul class="recent-portfolio"> <?php while ($query->have_posts()) : $query->the_post(); ?> <li> <a href="<?php the_permalink(); ?>"> <?php if (has_post_thumbnail()) : ?> <?php the_post_thumbnail('thumbnail'); ?> <?php endif; ?> <?php the_title(); ?> </a> </li> <?php endwhile; ?> </ul> <?php endif; wp_reset_postdata(); echo $args['after_widget']; } public function form($instance) { $title = !empty($instance['title']) ? $instance['title'] : ''; $count = !empty($instance['count']) ? $instance['count'] : 5; ?> <p> <label for="<?php echo $this->get_field_id('title'); ?>">Заголовок:</label> <input type="text" id="<?php echo $this->get_field_id('title'); ?>"  name="<?php echo $this->get_field_name('title'); ?>" value="<?php echo esc_attr($title); ?>" class="widefat"> </p> <p> <label for="<?php echo $this->get_field_id('count'); ?>">Количество работ:</label> <input type="number" id="<?php echo $this->get_field_id('count'); ?>"  name="<?php echo $this->get_field_name('count'); ?>" value="<?php echo esc_attr($count); ?>" min="1" max="20"> </p> <?php } } add_action('widgets_init', function() { register_widget('Recent_Portfolio_Widget'); });

5.2 Добавление CPT в меню

В настройках меню включите отображение CPT:

text
Внешний вид → Меню → Настройки экрана (вверху) → Отметить "Портфолио"

6. CPT и SEO

6.1 Настройка ЧПУ

При регистрации CPT укажите структуру URL:

php
'rewrite' => array( 'slug' => 'portfolio', // Базовая часть URL 'with_front' => false, // Игнорировать базовый префикс в настройках ЧПУ 'pages' => true, // Поддержка пагинации 'feeds' => true, // Поддержка RSS-лент )

6.2 Интеграция с SEO-плагинами

Yoast SEO автоматически определяет CPT и позволяет настраивать:

  • Заголовки архивов
  • Мета-описания
  • Хлебные крошки
  • XML-карту сайта

Настройка в Yoast:

text
SEO → Настройки → Типы контента
- Выберите ваш CPT
- Настройте шаблоны для заголовков и мета-описаний
- Включите в XML-карту

6.3 Канонические URL и дубли

Убедитесь, что для архивов CPT установлены правильные canonical URL:

php
add_action('wp_head', 'fix_cpt_canonical'); function fix_cpt_canonical() { if (is_post_type_archive('portfolio')) { echo '<link rel="canonical" href="' . get_post_type_archive_link('portfolio') . '" />'; } }

7. Поиск по CPT

7.1 Включение CPT в поиск

Через плагин Custom Search by BestWebSoft:

text
Плагины → Добавить новый → Custom Search → Активировать
Настройки → Custom Search → Выбрать CPT для включения в поиск

Через код functions.php:

php
function include_cpt_in_search($query) { if ($query->is_search && !is_admin()) { $query->set('post_type', array('post', 'page', 'portfolio')); } return $query; } add_action('pre_get_posts', 'include_cpt_in_search');

7.2 Расширенный поиск с фильтрами

Search Anything Anywhere — мощный плагин для поиска по CPT, таксономиям и ACF-полям :

  • Поддержка ACF-полей
  • Чистые URL для поиска
  • Фильтрация по таксономиям
  • Гибкие настройки сортировки

Установка:

text
Плагины → Добавить новый → Search Anything Anywhere → Активировать

Настройки:

text
Настройки → Search → General
- ✅ ACF Search
- ✅ Taxonomy Search
- ✅ Friendly URLs

8. CPT и REST API (для головных и мобильных приложений)

8.1 Включение в REST API

При регистрации CPT добавьте параметр:

php
'show_in_rest' => true, 'rest_base' => 'portfolio', // База для REST-маршрутов 'rest_controller_class' => 'WP_REST_Posts_Controller',

8.2 Добавление ACF-полей в REST

Через ACF к REST API:

php
add_filter('acf/rest_api/portfolio/get_fields', function($data, $request, $response) { return $data; }, 10, 3);

8.3 Создание кастомных эндпоинтов

php
add_action('rest_api_init', function() { register_rest_route('custom/v1', '/portfolio/featured', array( 'methods' => 'GET', 'callback' => 'get_featured_portfolio', 'permission_callback' => '__return_true', )); }); function get_featured_portfolio($request) { $query = new WP_Query(array( 'post_type' => 'portfolio', 'posts_per_page' => 5, 'meta_key' => '_featured', 'meta_value' => 'yes' )); $data = array(); while ($query->have_posts()) { $query->the_post(); $data[] = array( 'id' => get_the_ID(), 'title' => get_the_title(), 'link' => get_permalink(), 'thumbnail' => get_the_post_thumbnail_url('large'), ); } return rest_ensure_response($data); }

9. Импорт и экспорт CPT

9.1 Экспорт настроек CPT

Для плагина CPT UI:

text
CPT UI → Инструменты импорта/экспорта
- Выберите типы записей и таксономии
- Скопируйте код для functions.php

9.2 Импорт контента CPT

WP All Import — мощный плагин для импорта любых типов записей из CSV/XML:

text
Плагины → Добавить новый → WP All Import → Активировать
All Import → Новый импорт → Выбрать CPT

9.3 Миграция между сайтами

С помощью WP-CLI:

bash
# Экспорт только CPT
wp export --post_type=portfolio --dir=/path/to/export/

# Импорт
wp import /path/to/export/portfolio.xml --authors=create

10. Производительность CPT

10.1 Оптимизация запросов

Используйте WP_Query с умом:

php
// Плохо $query = new WP_Query(array( 'post_type' => 'portfolio', 'posts_per_page' => -1 // Загружает все записи! )); // Хорошо $query = new WP_Query(array( 'post_type' => 'portfolio', 'posts_per_page' => 12, 'no_found_rows' => true, // Отключает подсчет общего количества для пагинации 'update_post_meta_cache' => false, // Отключает кеширование мета-полей 'update_post_term_cache' => false, // Отключает кеширование таксономий ));

10.2 Кеширование

Для архивов CPT:

php
function cache_cpt_archives() { if (is_post_type_archive('portfolio')) { add_filter('posts_pre_query', function($posts, $query) { $cache_key = 'portfolio_archive_' . md5(serialize($query->query_vars)); $cached = wp_cache_get($cache_key, 'cpt_queries'); if ($cached !== false) { return $cached; } return $posts; }, 10, 2); } } add_action('pre_get_posts', 'cache_cpt_archives');

10.3 Индексация в базе данных

Для ускорения поиска добавьте индексы:

sql
ALTER TABLE wp_postmeta ADD INDEX post_id_meta_key (post_id, meta_key(100)); ALTER TABLE wp_posts ADD INDEX post_type_post_date (post_type, post_date);

11. Примеры для разных типов сайтов

11.1 Сайт-портфолио

php
// CPT: Проекты // Таксономии: Категории проектов (веб-дизайн, брендинг, иллюстрации) // Поля ACF: Ссылка на проект, клиент, год, использованные технологии

11.2 Сайт недвижимости

php
// CPT: Объекты недвижимости // Таксономии: Тип недвижимости (квартира, дом, участок), район // Поля ACF: Цена, площадь, этаж, количество комнат, фото

11.3 Каталог рецептов

php
// CPT: Рецепты // Таксономии: Кухня (итальянская, японская), тип блюда (суп, салат) // Поля ACF: Ингредиенты, время приготовления, калорийность, пошаговые фото

11.4 База знаний

php
// CPT: Статьи базы знаний // Таксономии: Категории (установка, настройка, ошибки) // Поля ACF: Версия ПО, уровень сложности, связанные статьи

11.5 Каталог сотрудников

php
// CPT: Сотрудники // Таксономии: Отделы, должности // Поля ACF: Фото, телефон, email, соцсети, биография

12. Типичные ошибки и их решение

Ошибка 1: Страницы 404 после создания CPT

Причина: Не обновлены правила перезаписи URL
Решение:

text
Настройки → Постоянные ссылки → Нажать "Сохранить изменения"

Ошибка 2: CPT не появляется в меню

Причина: Не настроены параметры видимости
Решение: Проверьте 'public' => true и 'show_ui' => true

Ошибка 3: Не работают шаблоны

Причина: Неправильное имя файла или иерархия
Решение: Убедитесь, что файл называется single-{cpt}.php (например, single-portfolio.php)

Ошибка 4: Медленные запросы

Причина: Отсутствие индексов или неправильные запросы
Решение: Используйте индексы в БД и оптимизируйте WP_Query

Ошибка 5: Конфликты с плагинами

Причина: Одинаковые названия типов записей
Решение: Используйте уникальные префиксы (например, my_portfolio вместо portfolio)

13. Плагины для работы с CPT (сравнение)

Плагин Цена Особенности Когда выбрать
Custom Post Type UI Бесплатно Простое создание CPT и таксономий Новичкам, быстрый старт
Podcasts Бесплатно / $99 Поля, шаблоны, экспорт Комплексные проекты
Advanced Custom Fields Бесплатно / $49 Мощные поля для CPT Любые проекты с кастомными полями
Toolset Types $69 Полный набор инструментов Сложные сайты без кода
JetEngine от $43 CPT + динамический контент Сайты на Elementor

14. Чек-лист создания идеального CPT

📋 Планирование:

  • Определите цель CPT (какой контент будет хранить)
  • Продумайте структуру URL (slug)
  • Решите, нужны ли иерархия и архивы
  • Определите необходимые таксономии
  • Составьте список пользовательских полей

📋 Создание:

  • Выберите способ (плагин или код)
  • Зарегистрируйте CPT с правильными параметрами
  • Создайте связанные таксономии
  • Добавьте пользовательские поля (ACF)

📋 Отображение:

  • Создайте шаблон архива (archive-{cpt}.php)
  • Создайте шаблон записи (single-{cpt}.php)
  • Добавьте поддержку в теме
  • Настройте CSS для новых шаблонов

📋 Функциональность:

  • Включите CPT в поиск
  • Настройте SEO (заголовки, мета-описания)
  • Добавьте в XML-карту сайта
  • Настройте REST API (если нужно)

📋 Тестирование:

  • Проверьте создание и редактирование записей
  • Проверьте отображение на сайте
  • Проверьте работу фильтров и поиска
  • Проверьте мобильную версию
  • Проверьте скорость загрузки

Заключение

Пользовательские типы записей (CPT) — это фундаментальный инструмент для создания сложных, структурированных сайтов на WordPress. Они позволяют организовать любой тип контента логично и удобно, как для администраторов, так и для посетителей.

Главный совет: Начинайте с малого — создайте один CPT для самого важного типа контента на вашем сайте. Освойте базовые принципы, а затем постепенно расширяйте функциональность с помощью таксономий, пользовательских полей и кастомных шаблонов.


*Примечание: При использовании CPT избегайте слишком большого количества типов записей на одном сайте (более 5-7), так как это может усложнить управление контентом и замедлить работу админ-панели.*