Опубликовано Ivan Abramenko от Вт, 11/19/2019 - 11:36

mysql

В этой статье мы разберемся как работают поля в Drupal, зачем они нужны и каким образом поля помогают быстро разрабатывать сайты на Drupal.

Мы уже работали с полями в прошлых статьях:

9.5. Верстаем блок services с колонками bootstrap
9.6. Галерея Isotope для Drupal 8
9.7. Блок с youtube видео Drupal 8

Сейчас мы подробнее разберемся какже это работает. Давайте зайдем в редактирование полей контент типа Article и добавим новое поле типа Link:

/admin/structure/types/manage/article/fields

add field

Когда вы создадите новую статью, то для поля ссылки у вас будет два input'а URL и текст ссылки:

link

Каждый раз когда вы создаете новое поле через админку, то в базе данных создается две таблице:

{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

add field

Но если вы захотите создать поле Link в блоке, то вам нужно будет создавать новое поле. Использовать одно и то же поле в Entity разных типов не получится. Давайте создадим поле Link для типа блока Basic Block :

/admin/structure/block/block-content/manage/basic/fields/add-field

Как вы можете заметить при создание поля нет выбора поля Link из уже существующих полей, потому что Block и Node это разные типы Entity.

И также как и для нод у нас создадутся две таблицы для хранения данных поле Link для блоков:

tables and views

block_content_revision__field_link и block_content__field_link.

Теперь давайте разберемся как друпал хранит данных одного поля для разных bundle нод. Мы создали поле Link для типа ноды Article, но потом переиспользовали это поле в типе ноды Basic Page. Для удобства лучше всего выгрузить конфигурацию сайта в папку и посмотреть файлы, но вы также можете найти нужную конфигурацию через adminer или phpmyadmin в таблице config:

select 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

В мультиязычных сайтах для разных версий нод под разные языки в таблице полей храняться все данных для всех языков, с указанием какой именно язык использует те или иные данные:

select

У меня один язык, поэтому и в конфиге по умолчанию стоит один язык.

status: true

Общая для всех configuration entity поле статуса, которое показывает включенна или выключена эта Entity. Field Storage конфиг использует в друпале созданную configuration entity, смотрите подробнее класс FieldStorageConfig, который наследуется от ConfigEntityBase:

https://api.drupal.org/api/drupal/core!modules!field!src!Entity!FieldSto...

 

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, то увидете те же самые поля:

select

Теперь мы подобрались к смыслу 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

Эти конфиги хранят данные с формы настроек поля:

link

Таким образом мы можем настроить форму ввода данных для поля разным способом для разных 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 работает с вводом данных и выводам данных полей, а также сделаем свой полноценный тип поля.