Разработка кастомных типов контента (Content Types) Drupal
Типы контента в Drupal — это сущности с набором полей. Стандартные Article и Page заменяются или дополняются типами под конкретную задачу: Новость, Кейс, Вакансия, Продукт. Настраивается в UI, конфигурация экспортируется в YAML и коммитится в git.
Создание через UI
Путь: /admin/structure/types/add. Задаём имя (Label) и машинное имя (slug). Drupal создаёт тип с базовыми полями: Title, Body, Published.
После создания — добавляем кастомные поля: /admin/structure/types/manage/{type}/fields/add-field.
Экспорт конфигурации в YAML
После настройки в UI:
drush config:export
В config/sync/ появятся файлы:
node.type.news.yml # сам тип
field.storage.node.field_subtitle.yml # хранилище поля
field.field.node.news.field_subtitle.yml # поле для конкретного типа
core.entity_form_display.node.news.default.yml # форма редактирования
core.entity_view_display.node.news.default.yml # вид отображения
core.entity_view_display.node.news.teaser.yml # вид teaser
Пример node.type.news.yml:
langcode: en
status: true
dependencies: {}
name: News
type: news
description: 'Новости компании'
help: ''
new_revision: true
preview_mode: 1
display_submitted: false
Конфигурация поля
# field.field.node.news.field_subtitle.yml
langcode: en
status: true
dependencies:
config:
- field.storage.node.field_subtitle
- node.type.news
id: node.news.field_subtitle
field_name: field_subtitle
entity_type: node
bundle: news
label: 'Подзаголовок'
description: 'Краткий анонс для списков и карточек'
required: false
translatable: true
default_value: []
settings: {}
field_type: string
Программное создание типа (через install-хук)
Если тип создаётся модулем, а не вручную:
// my_module.install
function my_module_install(): void {
// Создаём тип контента
$node_type = \Drupal\node\Entity\NodeType::create([
'type' => 'vacancy',
'name' => 'Вакансия',
'description' => 'Открытые вакансии компании',
'display_submitted' => FALSE,
'new_revision' => TRUE,
]);
$node_type->save();
// Поле "Город"
$field_storage = \Drupal\field\Entity\FieldStorageConfig::create([
'field_name' => 'field_city',
'entity_type' => 'node',
'type' => 'string',
'cardinality' => 1,
]);
$field_storage->save();
\Drupal\field\Entity\FieldConfig::create([
'field_storage' => $field_storage,
'bundle' => 'vacancy',
'label' => 'Город',
'required' => TRUE,
'settings' => ['max_length' => 100],
])->save();
// Поле "Зарплатная вилка" — decimal
$salary_storage = \Drupal\field\Entity\FieldStorageConfig::create([
'field_name' => 'field_salary_min',
'entity_type' => 'node',
'type' => 'decimal',
'settings' => ['precision' => 10, 'scale' => 0],
]);
$salary_storage->save();
\Drupal\field\Entity\FieldConfig::create([
'field_storage' => $salary_storage,
'bundle' => 'vacancy',
'label' => 'Зарплата от',
])->save();
// Entity reference на таксономию "Направление"
$ref_storage = \Drupal\field\Entity\FieldStorageConfig::create([
'field_name' => 'field_direction',
'entity_type' => 'node',
'type' => 'entity_reference',
'settings' => ['target_type' => 'taxonomy_term'],
'cardinality' => \Drupal\Core\Field\FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
]);
$ref_storage->save();
\Drupal\field\Entity\FieldConfig::create([
'field_storage' => $ref_storage,
'bundle' => 'vacancy',
'label' => 'Направление',
'settings' => [
'handler' => 'default:taxonomy_term',
'handler_settings' => [
'target_bundles' => ['directions' => 'directions'],
'auto_create' => FALSE,
],
],
])->save();
// Настройка форм отображения
\Drupal\Core\Entity\Entity\EntityFormDisplay::load('node.vacancy.default')
->setComponent('field_city', [
'type' => 'string_textfield',
'weight' => 10,
'settings' => ['size' => 60],
])
->setComponent('field_direction', [
'type' => 'options_buttons',
'weight' => 20,
])
->save();
}
Типичные типы полей
| Ситуация | Тип поля |
|---|---|
| Короткий текст | string |
| Длинный текст / HTML | text_long или text_with_summary |
| Число целое | integer |
| Число дробное | decimal или float |
| Дата | datetime |
| Ссылка на другую сущность | entity_reference |
| Изображение | image |
| Файл | file |
| Булево | boolean |
| Список (select) | list_string или list_integer |
email |
|
| Ссылка (URL) | link |
Связанные типы и reference поля
Типичная структура: Кейс → Клиент (entity reference), Кейс → Услуги (entity reference multiple).
# field.storage.node.field_client.yml
field_name: field_client
entity_type: node
type: entity_reference
cardinality: 1
settings:
target_type: node
# field.field.node.case.field_client.yml
field_name: field_client
bundle: case
label: 'Клиент'
settings:
handler: 'default:node'
handler_settings:
target_bundles:
client: client
sort:
field: title
direction: ASC
auto_create: false
Ревизии
Для типов, где нужна история изменений, включаем new_revision: true. Работа с ревизиями:
// Загрузить все ревизии ноды
$storage = \Drupal::entityTypeManager()->getStorage('node');
$revision_ids = $storage->revisionIds($node);
$revisions = array_map(fn($rid) => $storage->loadRevision($rid), $revision_ids);
// Откатить к ревизии
$node->setNewRevision(FALSE);
$node = $storage->loadRevision($target_revision_id);
$node->isDefaultRevision(TRUE);
$node->save();
Сроки
Один тип контента с полями через UI + экспорт конфигурации: полдня. Несколько типов с relationships, настройкой форм отображения, Views: 1–2 дня.







