DEV Community

Андрей Викулов (VProger)
Андрей Викулов (VProger)

Posted on • Originally published at viku-lov.ru on

Сущности WordPress, свои поля и роль functions.php: нормальная модель данных без плагинов

Сущности WordPress, свои поля и роль 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'],

]);

});

Enter fullscreen mode Exit fullscreen mode

Лишних аргументов нет. Этого достаточно, чтобы в админке появилась сущность «Проекты», записи сохранялись в БД и отображались. Для фронта часто добавляют 'has_archive' => true и 'rewrite' => ['slug' => 'projects'] — тогда будет архив по адресу /projects/. Готовый вариант в ООП (PHP 8.1+) с таксономией: сниппет «CPT и таксономия (OOP)».


Таксономии — не только категории

Таксономия — это справочник , а не просто «категории».


add\_action('init', function () {

register\_taxonomy('project\_type', ['project'], [

'label' => 'Тип проекта',

'hierarchical' => true,

]);

});

Enter fullscreen mode Exit fullscreen mode

Теперь:

  • 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

}

Enter fullscreen mode Exit fullscreen mode

Сохраняем значение (с проверкой 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'])

);

});

Enter fullscreen mode Exit fullscreen mode

Это не 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)

);

}

Enter fullscreen mode Exit fullscreen mode

Третий параметр true у get_post_meta() возвращает одно значение (строку); без него вернётся массив всех значений по ключу — это важно, если у одного ключа может быть несколько записей. Хелпер для типобезопасного чтения/записи мета в ООП: сниппет «Post meta helper (OOP)».


Роль functions.php: почему это не «помойка»

Что такое functions.php на самом деле

functions.php — это bootstrap темы , а не место для логики.

Его задача:

  • подключить код;
  • зарегистрировать хуки;
  • ничего не «решать» самому.

Плохая практика


// 800 строк кода

// CPT

// AJAX

// SQL

// API

// cron

Enter fullscreen mode Exit fullscreen mode

Такой functions.php:

  • нельзя переиспользовать;
  • страшно трогать;
  • больно переносить на другую тему.

Хорошая практика


// functions.php

require\_once \_\_DIR\_\_ . '/inc/cpt.php';

require\_once \_\_DIR\_\_ . '/inc/taxonomies.php';

require\_once \_\_DIR\_\_ . '/inc/meta.php';

Enter fullscreen mode Exit fullscreen mode

Мини-гайд по структуре кода

Пример структуры темы


theme/

├── functions.php

├── inc/

│ ├── cpt.php

│ ├── taxonomies.php

│ ├── meta.php

│ └── hooks.php

Enter fullscreen mode Exit fullscreen mode
  • 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. Поэтому для «долгих» проектов регистрацию лучше выносить в плагин.


Полезные ссылки

Сниппеты по теме статьи (OOP, PHP 8+):

  • CPT и таксономия — регистрация (OOP)
  • Метабокс с безопасным сохранением (OOP)
  • Theme bootstrap — загрузка inc
  • Post meta helper (OOP)

Read more on viku-lov.ru

Top comments (0)