Начиная с Drupal 8.7.0 автоматические обновления схем сущностей и полей больше недоступны. Теперь, когда хранилище для данных или сущности нужно создать, обновить или удалить, необходимо прописать это явно, используя Update API и Entity Definition Update Manager.
Для примера, добавим к нодам новое базовое поле is_frz_tasks.
Файл MYMODULE.install
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); }
Теперь нужно сообщить системе информацию о новом поле
Файл MYMODULE.module
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 будет добавлено наше новое поле, также оно будет добавлено на форму редактирвоания ноды
Остается еще один нюанс - правильно обработать удаление модуля. Для этого добавляем в файл MYMODULE.install hook_uninstall.
Файл MYMODULE.install
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
Комментарии
Добавить комментарий