Сущности WordPress, свои поля и роль functions.php: нормальная модель данных без плагинов
Практический разбор сущностей WordPress: post, custom post types, таксономии и метаполя. Как хранить данные правильно и не превращать functions.php в свалку.
Требования
- WordPress 6.x
- Базовое понимание PHP
- Доступ к functions.php темы
Сущности WordPress, свои поля и роль functions.php
Как собрать нормальную модель данных без зоопарка плагинов
WordPress часто воспринимают как «блог с плагинами» — и потом удивляются, почему данные размазаны по полям, таблицам и шорткодам.
Если смотреть на WordPress как на систему хранения данных, а не только как на движок для страниц, всё становится проще и предсказуемее.
Разбираемся по порядку.
Какие сущности есть в WordPress «из коробки»
WordPress — это не только post и page. В ядре есть чёткая модель данных:
Основные сущности
- Post — базовая запись (
post,page,attachment) - Custom Post Type (CPT) — пользовательский тип записи
- Taxonomy — классификатор (категория, тег, любая своя)
- Term — конкретное значение таксономии
- Meta — произвольные данные, привязанные к сущности
Главное: WordPress не запрещает проектировать модель данных — он просто не заставляет это делать. Не думаешь об этом — получается хаос; думаешь — получается нормальный бэкенд.
Custom Post Types и таксономии в коде
Когда нужен Custom Post Type
Если сущность:
- имеет свой жизненный цикл;
- имеет отдельный набор полей;
- должна жить независимо от темы;
значит, это CPT, а не «страница с полями».
Минимальная регистрация CPT
// functions.php
add_action('init', function () {
register_post_type('project', [
'label' => 'Проекты',
'public' => true,
'supports' => ['title', 'editor'],
]);
});
Лишних аргументов нет. Этого достаточно, чтобы в админке появилась сущность «Проекты», записи сохранялись в БД и отображались. Для фронта часто добавляют 'has_archive' => true и 'rewrite' => ['slug' => 'projects'] — тогда будет архив по адресу /projects/. Готовый вариант в ООП (PHP 8.1+) с таксономией: сниппет «CPT и таксономия (OOP)».
Таксономии — не только категории
Таксономия — это справочник, а не просто «категории».
add_action('init', function () {
register_taxonomy('project_type', ['project'], [
'label' => 'Тип проекта',
'hierarchical' => true,
]);
});
Теперь:
project— сущность;project_type— классификатор;term— конкретное значение («Сайт», «Сервис», «Интеграция»).
Это нормальная реляционная модель, просто поверх MySQL.
Пользовательские поля (meta)
Что такое meta в WordPress
Meta — это ключ → значение, привязанное к сущности.
Есть:
post_metauser_metaterm_metacomment_meta
Учти: meta не заменяет поля сущности; meta — это дополнение, а не помойка для всего подряд.
Пример: мета-поле для CPT
Допустим, у проекта есть URL репозитория. Ключ метаполя начинается с подчёркивания (_repo_url) — так WordPress не показывает его в блоке «Произвольные поля» в редакторе (рекомендация в документации).
Добавляем метабокс
add_action('add_meta_boxes', function () {
add_meta_box(
'project_repo',
'Репозиторий',
'render_project_repo_box',
'project'
);
});
function render_project_repo_box($post) {
wp_nonce_field('project_repo_save', 'project_repo_nonce');
$value = get_post_meta($post->ID, '_repo_url', true);
?>
<input
type="url"
name="project_repo_url"
value="<?= esc_attr($value); ?>"
style="width:100%;"
/>
<?php
}
Сохраняем значение (с проверкой nonce и прав)
По документации WordPress при сохранении метаполей нужно: проверить nonce, пропустить автосохранение и убедиться, что у пользователя есть право редактировать запись.
add_action('save_post_project', function ($post_id) {
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return;
}
if (!isset($_POST['project_repo_nonce'])
|| !wp_verify_nonce($_POST['project_repo_nonce'], 'project_repo_save')) {
return;
}
if (!current_user_can('edit_post', $post_id)) {
return;
}
if (!isset($_POST['project_repo_url'])) {
return;
}
update_post_meta(
$post_id,
'_repo_url',
esc_url_raw($_POST['project_repo_url'])
);
});
Это не ACF и не магия — чистый WordPress Core API: get_post_meta(), update_post_meta(). Вариант в ООП с nonce и проверкой прав: сниппет «Метабокс с безопасным сохранением (OOP)».
Вывод в шаблоне
В цикле или на single-странице проекта URL репозитория можно вывести так:
$repo_url = get_post_meta(get_the_ID(), '_repo_url', true);
if ($repo_url) {
printf(
'<a href="%s" target="_blank" rel="noopener">Репозиторий</a>',
esc_url($repo_url)
);
}
Третий параметр true у get_post_meta() возвращает одно значение (строку); без него вернётся массив всех значений по ключу — это важно, если у одного ключа может быть несколько записей. Хелпер для типобезопасного чтения/записи мета в ООП: сниппет «Post meta helper (OOP)».
Роль functions.php: почему это не «помойка»
Что такое functions.php на самом деле
functions.php — это bootstrap темы, а не место для логики.
Его задача:
- подключить код;
- зарегистрировать хуки;
- ничего не «решать» самому.
Плохая практика
// 800 строк кода
// CPT
// AJAX
// SQL
// API
// cron
Такой functions.php:
- нельзя переиспользовать;
- страшно трогать;
- больно переносить на другую тему.
Хорошая практика
// functions.php
require_once __DIR__ . '/inc/cpt.php';
require_once __DIR__ . '/inc/taxonomies.php';
require_once __DIR__ . '/inc/meta.php';
Мини-гайд по структуре кода
Пример структуры темы
theme/
├── functions.php
├── inc/
│ ├── cpt.php
│ ├── taxonomies.php
│ ├── meta.php
│ └── hooks.php
functions.php— точка входаinc/*— логика- данные не привязаны к шаблонам
Готовый загрузчик inc-модулей в ООП: сниппет «Theme bootstrap (inc loader)».
Подготовка к смене темы
Если:
- CPT,
- таксономии,
- бизнес-логика
живут в теме — это технический долг. В документации прямо сказано: при смене темы зарегистрированные в ней типы записей пропадут из админки. Данные в БД останутся, но управлять ими будет нечем.
Дальше: вынести это в отдельный плагин или mu-plugin и оставить в теме только отображение.
Где грань: тема или плагин
Можно оставить в теме, если:
- это учебный проект;
- данные не критичны;
- тема не будет меняться.
Нужно выносить в плагин, если:
- данные — бизнес-ценность;
- проект живёт годами;
- возможна смена темы.
WordPress это допускает. Ответственность за выбор — на разработчике.
Итоговый чек-лист для разработчика
Перед тем как писать код, задай себе вопросы:
- Что здесь является отдельной сущностью?
- Это
pageили нужен CPT? - Это поле или справочник (taxonomies)?
- Meta — действительно лучший вариант?
- Где лежит бизнес-логика?
- Смогу ли я сменить тему без потери данных?
Если на эти вопросы есть ответы — ты используешь WordPress как платформу, а не как конструктор.
Коротко
WordPress не мешает делать правильно — он просто не заставляет. Дальше либо выстраиваешь нормальную модель данных, либо плодишь плагины «на всё подряд». Выбор за разработчиком.
FAQ
Чем CPT отличается от обычной страницы с произвольными полями?
CPT — отдельный тип записи со своей таблицей в БД, своими правами доступа и своим URL (например, /project/slug/). Страница с полями — это одна запись типа page; для десятков «проектов» придётся плодить страницы и метаполя, без нормальной архивации и выборок.
Зачем префикс подчёркивания у ключа метаполя (_repo_url)?
Ключи, начинающиеся с _, WordPress не показывает в блоке «Произвольные поля» в редакторе. Так свои служебные поля не путаются с теми, что редактор может случайно править.
Обязательно ли проверять nonce и права при сохранении мета?
Да. Иначе любой запрос с подставленным $_POST может изменить мета любой записи. В документации add_meta_box явно рекомендуют nonce и current_user_can('edit_post', $post_id).
Пропадут ли данные при смене темы, если CPT зарегистрирован в теме?
Данные в БД останутся, но в админке тип записей исчезнет — управлять ими будет нечем, пока не подключишь плагин или mu-plugin с той же регистрацией CPT. Поэтому для «долгих» проектов регистрацию лучше выносить в плагин.
Полезные ссылки
- Post Types и Taxonomies — Developer.WordPress.org
- Custom Fields / Meta — get_post_meta, update_post_meta, add_meta_box
- Документация и кодекс WordPress — функции, хуки, примеры на русском (wp-kama.ru)
Сниппеты по теме статьи (OOP, PHP 8+):



Комментарии