В этой статье мы разберемся как работают поля в Drupal, зачем они нужны и каким образом поля помогают быстро разрабатывать сайты на Drupal.
Мы уже работали с полями в прошлых статьях:
9.5. Верстаем блок services с колонками bootstrap
9.6. Галерея Isotope для Drupal 8
9.7. Блок с youtube видео Drupal 8
Сейчас мы подробнее разберемся какже это работает. Давайте зайдем в редактирование полей контент типа Article и добавим новое поле типа Link:
/admin/structure/types/manage/article/fields
Когда вы создадите новую статью, то для поля ссылки у вас будет два input'а URL и текст ссылки:
Каждый раз когда вы создаете новое поле через админку, то в базе данных создается две таблице:
{entity_type}__{field_name}
{entity_type}_revision__{field_name}
Drupal 8 поддерживает ривизии, поэтому все данные будут дублироваться по меньшей мере один раз, потому что единственная ревизия и будет текущей ревизией вашей статьи. Таким образом поля и весь Drupal Fields API нужен для упрощения работы с базой данных. Мы просто создаем поля через админку, а друпал уже создает таблицы в базах данных.
Из названия таблиц MySQL должно быть понятно, что одно и тоже поле может быть использованно для одного типа entity. Так например мы теперь можем использовать поле Link в контент типе Basic Page как уже существующее:
/admin/structure/types/manage/page/fields/add-field
Но если вы захотите создать поле Link в блоке, то вам нужно будет создавать новое поле. Использовать одно и то же поле в Entity разных типов не получится. Давайте создадим поле Link для типа блока Basic Block :
/admin/structure/block/block-content/manage/basic/fields/add-field
Как вы можете заметить при создание поля нет выбора поля Link из уже существующих полей, потому что Block и Node это разные типы Entity.
И также как и для нод у нас создадутся две таблицы для хранения данных поле Link для блоков:
block_content_revision__field_link и block_content__field_link.
Теперь давайте разберемся как друпал хранит данных одного поля для разных bundle нод. Мы создали поле Link для типа ноды Article, но потом переиспользовали это поле в типе ноды Basic Page. Для удобства лучше всего выгрузить конфигурацию сайта в папку и посмотреть файлы, но вы также можете найти нужную конфигурацию через adminer или phpmyadmin в таблице config:
Если поискать все конфиги в которых содержится слово link:
SELECT * FROM `config` WHERE CONVERT(`name` USING utf8mb4) LIKE '%link%' LIMIT 50
То мы найдем следующие конфиги для нашего поля field_link:
field.field.block_content.basic.field_link
field.field.node.article.field_link
field.field.node.page.field_link
field.storage.block_content.field_link
field.storage.node.field_link
Для каждого типа Entity каждое поле создает свой Field Storage конфиг. Этот конфиг отвечает как хранить данные в таблицах {entity_type}__{field_name}, {entity_type}_revision__{field_name}. В нашем случае это таблицы block_content__field_link, block_content_revision__field_link, node__field_link, node_revision__field_link. Дальше мы разберем как конкретно модуль Link хранит данные. Давайте откроем конфиг для Field Storage поля Link:
uuid: dba847ef-f4d6-4462-a2ee-f642a007fca6 langcode: en status: true dependencies: module: - block_content - link id: block_content.field_link field_name: field_link entity_type: block_content type: link settings: { } module: link locked: false cardinality: 1 translatable: true indexes: { } persist_with_no_fields: false custom_storage: false
Давайте разберем каждую из этих строчек, чтобы было понятно что конкретно хранится в Field Storage конфиге.
uuid: dba847ef-f4d6-4462-a2ee-f642a007fca6
Здесь хранится ID конфига, уникальное для каждого конфига. Не нужно создавать поле вручную на staging если вы создали поле локально и выгрузили конфиги. Поле будет автоматически создано. А вот если вы удалите поле и удалите конфиг локально, тем самым вы удалите и все данные после импорта на staging. Поэтому если вы создали какое-то поле на staging, то выгрузите конфиги и добавьте их в git, чтобы не потярять ваши изменения.
langcode: en
В мультиязычных сайтах для разных версий нод под разные языки в таблице полей храняться все данных для всех языков, с указанием какой именно язык использует те или иные данные:
У меня один язык, поэтому и в конфиге по умолчанию стоит один язык.
status: true
Общая для всех configuration entity поле статуса, которое показывает включенна или выключена эта Entity. Field Storage конфиг использует в друпале созданную configuration entity, смотрите подробнее класс FieldStorageConfig, который наследуется от ConfigEntityBase:
https://api.drupal.org/api/drupal/core!modules!field!src!Entity!FieldStorageConfig.php/class/FieldStorageConfig/8.2.x
dependencies: module: - block_content - link
Зависимость от дополнительных модулей. Так как мы использовали поле Link в блоках, то у нас обязательный модуль Block content.
id: block_content.field_link
Уникальное имя нашего конфига.
field_name: field_link
Машинное имя поля которые мы создали, его использует друпал, поэтому это машинное имя можно использовать например при обращение к объекту ноды $node->field_link->uri. Как обращаться к поля Entity мы еще разберемся подробнее в следующих статьях.
entity_type: block_content
К какому типу Entity относится конфиг нашего Field Storage.
type: link
Тип поля Drupal 8, мы будем создавать свой собственный тип поля, сейчас нам достаточно знать, что этот тип поля link создается модулем Link. Вы можете посмотреть класс
core/modules/link/src/Plugin/Field/FieldType/LinkItem.php
Которые используется в нашем конфиге.
settings: { }
Здесь у нас хранятся настройки для типа поля, сейчас они пустые, но например для поля body есть настройка отображать или нет тизер:
config/sync/field.field.block_content.basic.body.yml
settings: display_summary: false
module: link
Модуль который имеет тип поля link. Сейчас у нас совпадает название модуля link и тип поля link, но они могут быть разными, например в одном модуле может быть реализованно несколько типов полей, как это сделано в модуле DateTime:
core/modules/datetime/src/Plugin/Field/FieldType/DateTimeFieldItemList.php
core/modules/datetime/src/Plugin/Field/FieldType/DateTimeItem.php
locked: false
Показывает доступно ли поле для редактирования. Имеется в виду настройки поля. Некоторое время назад например поле Billing Information и Shipping Information в модуле Commerce было заблокировано, потому что существования этого поля было обязательно при использование доставки и расчета налогов.
cardinality: 1
Количество значение для одного Entity, которое можно ввести для этого поля. Мы выбрали одно значение, но здесь может другое число 2, 3, 5 и т.д. Для неограниченного количества значений используется cardinality: -1.
translatable: true
Является ли это поле переводимым на другие языки
indexes: { }
Дополнительные SQL индексы для лучше поиска по этому полю. Обычно это нужно для настройки и оптимизации запросов к БД.
persist_with_no_fields: false
Показывает нужно ли удалять Field Storage если поля были удалены из всех Entity. Например если мы удалим поле из Article и Basic Page, Field Storage не будет удален.
custom_storage: false
Кастомное хранилище подразумевает, что у нас особая таблица для хранения данных поля, не {entity_type}__{field_name}. Мы не будем использовать, что-то подобное, но иногда это удобно для интеграции с другими системами.
Теперь давайте откроем файл типа поля link:
core/modules/link/src/Plugin/Field/FieldType/LinkItem.php
И посмотрим какие данные хранит это поле в базе данных. Это можно посмотреть в методе propertyDefinitions():
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) { $properties['uri'] = DataDefinition::create('uri') ->setLabel(t('URI')); $properties['title'] = DataDefinition::create('string') ->setLabel(t('Link text')); $properties['options'] = MapDataDefinition::create() ->setLabel(t('Options')); return $properties; }
Как вы видите мы храним данные URI, Title, Options. Если вы откроете таблицу node__field_link, то увидете те же самые поля:
Теперь мы подобрались к смыслу Fields API в Drupal 8. Мы создаем тип поля через модуль в файле PHP с классом LinkItem. Это позволяет нам создать поле для Entity и потом мы можем использовать это для ввода данных и хранения этих данных в базе даннах. Это то что касается ввода данных. Fields API занимается также настройкой формы для ввода данных и выводам данных для наших полей.
Давайте вернемся к моменту создания полей к типам материалов Article и Basic Page. У нас есть один конфиг для Field Storage в Node: field.storage.node.field_link.yml, но также при создание поля, создается еще один конфиг для настроек поля для каждого bundle в Entity, так например у нас есть теперь три конфига настроек поля
field.field.node.article.field_link.yml
field.field.node.page.field_link.yml
field.field.block_content.basic.field_link.yml
Эти конфиги хранят данные с формы настроек поля:
Таким образом мы можем настроить форму ввода данных для поля разным способом для разных Bundles. Каждый отдельный конфиг для поля в Bundle называют в друпале Field Instance, таким образом мы сначала создаем поле Field Storage, который можно использовать в Field Instance отдельно в каждом бандле. В 8ом друпале в отличие от 7го друпала уже нет функций для работы с Field Instances, а сам функционал для работы с instances перекочевал в CRUD API:
https://www.drupal.org/node/2054619
Примеры работы с полями через код вы можете посмотреть в официальной документации:
https://www.drupal.org/node/2012896
Мы осмотрели только часть Fields API, которая касается хранение данных полей в базе данных. В следующих уроках мы разберем как Fields API работает с вводом данных и выводам данных полей, а также сделаем свой полноценный тип поля.
Добавить комментарий