Начиная с Drupal 8.7.0 автоматические обновления схем сущностей и полей больше недоступны. Теперь, когда хранилище для данных или сущности нужно создать, обновить или удалить, необходимо прописать это явно, используя Update API и Entity Definition Update Manager.
Для примера, добавим к нодам новое базовое поле is_frz_tasks.
use \Drupal\Core\Field\BaseFieldDefinition;
function MYMODULE_install() {
$field_storage_definition = BaseFieldDefinition::create('boolean')
->setLabel('Label is_frz_tasks')
->setDescription('is_frz_tasks')
->setRevisionable(FALSE)
->setTranslatable(FALSE)
->setDisplayOptions('form', [
'type' => 'boolean_checkbox',
'settings' => [
'display_label' => TRUE,
],
])
->setDisplayConfigurable('form', TRUE);
\Drupal::entityDefinitionUpdateManager()
->installFieldStorageDefinition('is_frz_tasks', 'node', 'node', $field_storage_definition);
}
Теперь нужно сообщить системе информацию о новом поле
use \Drupal\Core\Field\BaseFieldDefinition;
/**
* Implements hook_entity_base_field_info().
*/
function MYMODULE_entity_base_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type) {
$fields = [];
if ($entity_type->id() === 'node') {
$definition_update_manager = \Drupal::entityDefinitionUpdateManager();
if ($field_is_frz_tasks = $definition_update_manager->getFieldStorageDefinition('is_frz_tasks', 'node')) {
$fields['is_frz_tasks'] = $field_is_frz_tasks;
}
return $fields;
}
}
После включения модуля в таблице node_field_data будет добавлено наше новое поле, также оно будет добавлено на форму редактирвоания ноды.
Остается еще один нюанс - правильно обработать удаление модуля. Для этого используем hook_uninstall.
function MYMODULE_uninstall() {
$entityTypeId = 'node';
$fieldName = 'is_frz_tasks';
$entityDefinitionUpdateManager = \Drupal::entityDefinitionUpdateManager();
if ($field_is_frz_tasks = $entityDefinitionUpdateManager->getFieldStorageDefinition($fieldName, $entityTypeId)) {
$entityDefinitionUpdateManager->uninstallFieldStorageDefinition($field_is_frz_tasks);
}
}
И вот тут не все так просто. Проблема возникает, если в новом поле уже сохранены какие-то данные. Поле не удаляется сразу, а становится в очередь на удаление, и очищается только по крону. А сама процедура удаления вылетает с ошибкой:
The field is_frz_tasks has already been deleted and it is in the process of being purged.
В общем, пока обходным решением является предварительная очистка данных поля. Дополняем обработку в hook_uninstall():
function MYMODULE_uninstall() {
$entityTypeId = 'node';
$fieldName = 'is_frz_tasks';
$entityTypeManager = \Drupal::entityTypeManager();
$entityDefinitionUpdateManager = \Drupal::entityDefinitionUpdateManager();
$entityDefinition = $entityTypeManager->getDefinition($entityTypeId);
$entityStorage = $entityTypeManager->getStorage($entityTypeId);
$tableName = $entityStorage->getDataTable() ?: $entityStorage->getBaseTable();
$tableRevision = $entityStorage->getRevisionDataTable() ?: $entityStorage->getRevisionTable();
// clear data
$database = \Drupal::database();
if ($database->schema()->fieldExists($tableName, $fieldName)) {
$database->update($tableName)
->fields([$fieldName => NULL])
->execute();
}
if ($entityDefinition->isRevisionable() && $database->schema()->fieldExists($tableRevision, $fieldName)) {
$database = \Drupal::database();
$database->update($tableRevision)
->fields([$fieldName => NULL])
->execute();
}
if ($field_is_frz_tasks = $entityDefinitionUpdateManager->getFieldStorageDefinition($fieldName, $entityTypeId)) {
$entityDefinitionUpdateManager->uninstallFieldStorageDefinition($field_is_frz_tasks);
}
}
Теперь все отрабатывает корректно.
Для проверки повторно переустанавливал модуль
drush dre MYMODULE.
Заполнял поле, затем снова переустанавливал
Какие еще могут возникнуть проблемы:
Что бы обработать очередь полей на удаление:
drush cron
или:
drush php-eval 'field_purge_batch(1000);'
Если по какой-то причине из БД исчезли таблицы вида field_deleted_data_XXX, но информация в системе о них осталась, то вы получите ошибку БД:
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'field_deleted_data_XXX' doesn't exist: SELECT DISTINCT "t"."entity_id" AS "entity_id"
FROM "field_deleted_data_XXX" "t"
WHERE "bundle" = :db_condition_placeholder_0
Тут мне помогло только одно - в БД вручную необходимо создать таблицу:
CREATE TABLE `field_deleted_data_XXX` (
`bundle` varchar(128) CHARACTER SET ascii NOT NULL DEFAULT '',
`deleted` tinyint(4) NOT NULL DEFAULT '0',
`entity_id` int(10) unsigned NOT NULL,
`revision_id` int(10) unsigned NOT NULL,
`langcode` varchar(32) CHARACTER SET ascii NOT NULL DEFAULT '',
`delta` int(10) unsigned NOT NULL,
`content_translation_source_value` varchar(12) CHARACTER SET ascii NOT NULL,
PRIMARY KEY (`entity_id`,`deleted`,`delta`,`langcode`),
KEY `bundle` (`bundle`),
KEY `revision_id` (`revision_id`) );
и затем запустить очистку
drush php-eval 'field_purge_batch(1000);'
Вспомогательный класс для работы полями (рекомендую для ознакомления): error84
- 13.10.2021
- 142 просмотра