Сущности 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_meta
- user_meta
- term_meta
- comment_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(
'[Репозиторий](%s)',
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+):
- CPT и таксономия — регистрация (OOP)
- Метабокс с безопасным сохранением (OOP)
- Theme bootstrap — загрузка inc
- Post meta helper (OOP)

Top comments (0)