mirror of
https://gitlab.unicamp.br/infimecc_drupal11_modules/structural_pages.git
synced 2026-05-05 05:50:40 -03:00
Refatora arquitetura de domínio: field_site_section DER, field_parent_page node-only
- field_site_section migrado de entity_reference para dynamic_entity_reference, permitindo referenciar taxonomy_term, user ou event como domínio raiz da página - field_parent_page restrito a content_page nodes apenas (removidos taxonomy_term/user) - Update hooks 10011–10013: migração de schema/config, limpeza de referências inválidas e correção do tipo do storage field_parent_page para DER - SectionBreadcrumbBuilder reescrito para usar field_site_section como raiz - StructuralPagesMenuBlock: queries de domínio via DB direto (evita bug do entity query com especificador target_type em campos DER) - Formulário content_page: pre-fill de field_site_section via query params ?domain_type/domain_id ou ?parent_section_type/parent_id; atualiza widget #default_value diretamente para refletir o valor na renderização inicial - Parâmetro de URL parent_type renomeado para parent_section_type Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -335,7 +335,7 @@ function structural_pages_update_10006(): string {
|
||||
->getEditable('field.field.node.content_page.field_parent_page');
|
||||
|
||||
if ($config->isNew()) {
|
||||
return t('Campo field_parent_page não encontrado — nada a corrigir.');
|
||||
return 'Campo field_parent_page não encontrado — nada a corrigir.';
|
||||
}
|
||||
|
||||
// Re-aplica as settings completas a partir do YAML de install.
|
||||
@@ -343,14 +343,14 @@ function structural_pages_update_10006(): string {
|
||||
$yaml_file = $module_path . '/config/install/field.field.node.content_page.field_parent_page.yml';
|
||||
|
||||
if (!file_exists($yaml_file)) {
|
||||
return t('Arquivo YAML de install não encontrado.');
|
||||
return 'Arquivo YAML de install não encontrado.';
|
||||
}
|
||||
|
||||
$yaml_settings = \Symfony\Component\Yaml\Yaml::parse(file_get_contents($yaml_file));
|
||||
$config->set('settings', $yaml_settings['settings']);
|
||||
$config->save(TRUE);
|
||||
|
||||
return t('Settings de field_parent_page re-sincronizadas a partir do YAML de install.');
|
||||
return 'Settings de field_parent_page re-sincronizadas a partir do YAML de install.';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -369,6 +369,446 @@ function structural_pages_update_10007(): string {
|
||||
return 'exclude_entity_types corrigido para FALSE em field_parent_page storage.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Adiciona coluna field_parent_page_target_type ausente no schema do banco.
|
||||
*
|
||||
* O campo foi originalmente criado como entity_reference simples e depois
|
||||
* migrado para dynamic_entity_reference no config, mas a coluna target_type
|
||||
* nunca foi adicionada ao banco. Popula o tipo de cada linha consultando as
|
||||
* tabelas node e taxonomy_term para determinar o tipo correto da referência.
|
||||
*/
|
||||
function structural_pages_update_10008(): string {
|
||||
$database = \Drupal::database();
|
||||
$schema = $database->schema();
|
||||
|
||||
$column_spec = [
|
||||
'type' => 'varchar_ascii',
|
||||
'length' => 128,
|
||||
'not null' => FALSE,
|
||||
'description' => 'The entity type of the field_parent_page reference.',
|
||||
];
|
||||
|
||||
$tables = [
|
||||
'node__field_parent_page',
|
||||
'node_revision__field_parent_page',
|
||||
];
|
||||
|
||||
$updated = [];
|
||||
|
||||
foreach ($tables as $table) {
|
||||
if (!$schema->tableExists($table)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$schema->fieldExists($table, 'field_parent_page_target_type')) {
|
||||
$schema->addField($table, 'field_parent_page_target_type', $column_spec);
|
||||
}
|
||||
|
||||
// Determina o tipo de cada referência existente comparando o target_id
|
||||
// com os IDs existentes em node e taxonomy_term.
|
||||
$rows = $database->select($table, 't')
|
||||
->fields('t', ['entity_id', 'revision_id', 'field_parent_page_target_id'])
|
||||
->isNull('field_parent_page_target_type')
|
||||
->execute()
|
||||
->fetchAll();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$target_id = $row->field_parent_page_target_id;
|
||||
|
||||
$is_node = (bool) $database->select('node', 'n')
|
||||
->fields('n', ['nid'])
|
||||
->condition('n.nid', $target_id)
|
||||
->countQuery()
|
||||
->execute()
|
||||
->fetchField();
|
||||
|
||||
$target_type = $is_node ? 'node' : 'taxonomy_term';
|
||||
|
||||
$database->update($table)
|
||||
->fields(['field_parent_page_target_type' => $target_type])
|
||||
->condition('entity_id', $row->entity_id)
|
||||
->condition('revision_id', $row->revision_id)
|
||||
->execute();
|
||||
}
|
||||
|
||||
$updated[] = $table;
|
||||
}
|
||||
|
||||
if (empty($updated)) {
|
||||
return 'Tabelas não encontradas — nada a corrigir.';
|
||||
}
|
||||
|
||||
return 'Coluna field_parent_page_target_type adicionada e populada em: ' . implode(', ', $updated) . '.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Define entity_type_ids no storage do field_parent_page para node e taxonomy_term.
|
||||
*
|
||||
* Com entity_type_ids vazio e exclude_entity_types FALSE, o widget DER não
|
||||
* conseguia determinar quais entity types exibir e deixava o campo em branco.
|
||||
*/
|
||||
function structural_pages_update_10009(): string {
|
||||
$config = \Drupal::configFactory()
|
||||
->getEditable('field.storage.node.field_parent_page');
|
||||
if ($config->isNew()) {
|
||||
return 'field.storage.node.field_parent_page não encontrado.';
|
||||
}
|
||||
$config->set('settings.entity_type_ids', ['node', 'taxonomy_term'])->save(TRUE);
|
||||
return 'entity_type_ids definido como [node, taxonomy_term] em field_parent_page storage.';
|
||||
}
|
||||
|
||||
/**
|
||||
* Adiciona suporte ao entity type user no campo field_parent_page.
|
||||
*
|
||||
* Atualiza o storage (entity_type_ids) e a instância do campo (handler settings
|
||||
* para user) para que o DER module possa referenciar usuários como pai de
|
||||
* content_page nodes.
|
||||
*/
|
||||
function structural_pages_update_10010(): string {
|
||||
$messages = [];
|
||||
|
||||
// 1. Storage: adiciona 'user' a entity_type_ids.
|
||||
$storage_config = \Drupal::configFactory()
|
||||
->getEditable('field.storage.node.field_parent_page');
|
||||
if (!$storage_config->isNew()) {
|
||||
$entity_type_ids = $storage_config->get('settings.entity_type_ids') ?? [];
|
||||
if (!in_array('user', $entity_type_ids, TRUE)) {
|
||||
$entity_type_ids[] = 'user';
|
||||
$storage_config->set('settings.entity_type_ids', $entity_type_ids)->save(TRUE);
|
||||
$messages[] = 'Storage atualizado: user adicionado a entity_type_ids.';
|
||||
}
|
||||
else {
|
||||
$messages[] = 'Storage: user já presente em entity_type_ids.';
|
||||
}
|
||||
}
|
||||
else {
|
||||
$messages[] = 'field.storage.node.field_parent_page não encontrado.';
|
||||
}
|
||||
|
||||
// 2. Instância: adiciona handler settings para user.
|
||||
$field_config = \Drupal::configFactory()
|
||||
->getEditable('field.field.node.content_page.field_parent_page');
|
||||
if (!$field_config->isNew()) {
|
||||
$settings = $field_config->get('settings') ?? [];
|
||||
$type_ids = $settings['entity_type_ids'] ?? [];
|
||||
if (!in_array('user', $type_ids, TRUE)) {
|
||||
$type_ids[] = 'user';
|
||||
$settings['entity_type_ids'] = $type_ids;
|
||||
}
|
||||
if (!isset($settings['user'])) {
|
||||
$settings['user'] = [
|
||||
'handler' => 'default:user',
|
||||
'handler_settings' => [
|
||||
'include_anonymous' => FALSE,
|
||||
'filter' => ['type' => '_none'],
|
||||
'target_bundles' => NULL,
|
||||
'sort' => ['field' => 'name', 'direction' => 'asc'],
|
||||
'auto_create' => FALSE,
|
||||
],
|
||||
];
|
||||
}
|
||||
$field_config->set('settings', $settings)->save(TRUE);
|
||||
$messages[] = 'Instância atualizada: handler settings de user adicionadas.';
|
||||
}
|
||||
else {
|
||||
$messages[] = 'field.field.node.content_page.field_parent_page não encontrado.';
|
||||
}
|
||||
|
||||
return implode(' ', $messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migra field_site_section de entity_reference para dynamic_entity_reference.
|
||||
*
|
||||
* O campo precisa armazenar o domínio raiz da página (taxonomy_term OU user),
|
||||
* por isso deve se tornar um campo DER em vez de entity_reference simples.
|
||||
*
|
||||
* Passos:
|
||||
* 1. Adiciona coluna target_type nas tabelas do campo.
|
||||
* 2. Preenche todas as linhas existentes com target_type = 'taxonomy_term'.
|
||||
* 3. Atualiza o storage config para dynamic_entity_reference.
|
||||
* 4. Atualiza as instâncias (content_page e section_page).
|
||||
*/
|
||||
function structural_pages_update_10011(): string {
|
||||
$database = \Drupal::database();
|
||||
$schema = $database->schema();
|
||||
$messages = [];
|
||||
|
||||
$column_spec = [
|
||||
'type' => 'varchar_ascii',
|
||||
'length' => 128,
|
||||
'not null' => FALSE,
|
||||
'description' => 'The entity type of the field_site_section reference.',
|
||||
];
|
||||
|
||||
foreach (['node__field_site_section', 'node_revision__field_site_section'] as $table) {
|
||||
if (!$schema->tableExists($table)) {
|
||||
continue;
|
||||
}
|
||||
if (!$schema->fieldExists($table, 'field_site_section_target_type')) {
|
||||
$schema->addField($table, 'field_site_section_target_type', $column_spec);
|
||||
// Populate existing rows as taxonomy_term.
|
||||
$database->update($table)
|
||||
->fields(['field_site_section_target_type' => 'taxonomy_term'])
|
||||
->isNull('field_site_section_target_type')
|
||||
->execute();
|
||||
$messages[] = "Coluna target_type adicionada em $table.";
|
||||
}
|
||||
else {
|
||||
$messages[] = "Coluna target_type já existe em $table.";
|
||||
}
|
||||
}
|
||||
|
||||
// Update storage config.
|
||||
$storage = \Drupal::configFactory()->getEditable('field.storage.node.field_site_section');
|
||||
if (!$storage->isNew()) {
|
||||
$storage->set('type', 'dynamic_entity_reference');
|
||||
$storage->set('module', 'dynamic_entity_reference');
|
||||
$storage->set('settings', [
|
||||
'exclude_entity_types' => FALSE,
|
||||
'entity_type_ids' => ['taxonomy_term', 'user'],
|
||||
]);
|
||||
$deps = $storage->get('dependencies') ?? [];
|
||||
$mods = $deps['module'] ?? [];
|
||||
if (!in_array('dynamic_entity_reference', $mods, TRUE)) {
|
||||
$mods[] = 'dynamic_entity_reference';
|
||||
}
|
||||
$deps['module'] = $mods;
|
||||
$storage->set('dependencies', $deps);
|
||||
$storage->save(TRUE);
|
||||
$messages[] = 'Storage config atualizado para dynamic_entity_reference.';
|
||||
}
|
||||
|
||||
// Update content_page instance config.
|
||||
$field_cp = \Drupal::configFactory()->getEditable('field.field.node.content_page.field_site_section');
|
||||
if (!$field_cp->isNew()) {
|
||||
$field_cp->set('field_type', 'dynamic_entity_reference');
|
||||
$field_cp->set('settings', [
|
||||
'entity_type_ids' => ['taxonomy_term', 'user'],
|
||||
'taxonomy_term' => [
|
||||
'handler' => 'default:taxonomy_term',
|
||||
'handler_settings' => [
|
||||
'target_bundles' => ['site_sections' => 'site_sections'],
|
||||
'sort' => ['field' => 'name', 'direction' => 'asc'],
|
||||
'auto_create' => FALSE,
|
||||
],
|
||||
],
|
||||
'user' => [
|
||||
'handler' => 'default:user',
|
||||
'handler_settings' => [
|
||||
'include_anonymous' => FALSE,
|
||||
'filter' => ['type' => '_none'],
|
||||
'target_bundles' => NULL,
|
||||
'sort' => ['field' => 'name', 'direction' => 'asc'],
|
||||
'auto_create' => FALSE,
|
||||
],
|
||||
],
|
||||
]);
|
||||
$deps = $field_cp->get('dependencies') ?? [];
|
||||
$mods = $deps['module'] ?? [];
|
||||
if (!in_array('dynamic_entity_reference', $mods, TRUE)) {
|
||||
$mods[] = 'dynamic_entity_reference';
|
||||
}
|
||||
$deps['module'] = $mods;
|
||||
$field_cp->set('dependencies', $deps);
|
||||
$field_cp->save(TRUE);
|
||||
$messages[] = 'Instância content_page atualizada.';
|
||||
}
|
||||
|
||||
// Update section_page instance config (taxonomy_term only).
|
||||
$field_sp = \Drupal::configFactory()->getEditable('field.field.node.section_page.field_site_section');
|
||||
if (!$field_sp->isNew()) {
|
||||
$field_sp->set('field_type', 'dynamic_entity_reference');
|
||||
$field_sp->set('settings', [
|
||||
'entity_type_ids' => ['taxonomy_term'],
|
||||
'taxonomy_term' => [
|
||||
'handler' => 'default:taxonomy_term',
|
||||
'handler_settings' => [
|
||||
'target_bundles' => ['site_sections' => 'site_sections'],
|
||||
'sort' => ['field' => 'name', 'direction' => 'asc'],
|
||||
'auto_create' => FALSE,
|
||||
],
|
||||
],
|
||||
]);
|
||||
$deps = $field_sp->get('dependencies') ?? [];
|
||||
$mods = $deps['module'] ?? [];
|
||||
if (!in_array('dynamic_entity_reference', $mods, TRUE)) {
|
||||
$mods[] = 'dynamic_entity_reference';
|
||||
}
|
||||
$deps['module'] = $mods;
|
||||
$field_sp->set('dependencies', $deps);
|
||||
$field_sp->save(TRUE);
|
||||
$messages[] = 'Instância section_page atualizada.';
|
||||
}
|
||||
|
||||
// Rebuild field definitions cache.
|
||||
\Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
|
||||
|
||||
return implode(' ', $messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restringe field_parent_page a content_page nodes e limpa referências inválidas.
|
||||
*
|
||||
* Na arquitetura anterior, field_parent_page podia apontar para taxonomy_term
|
||||
* ou user (como raiz do domínio). Na nova arquitetura, field_site_section é o
|
||||
* portador do domínio raiz, e field_parent_page aponta somente para
|
||||
* content_page nodes pai (ou é nulo para páginas raiz).
|
||||
*
|
||||
* Passos:
|
||||
* 1. Zera linhas onde target_type != 'node' (eram raízes de domínio).
|
||||
* 2. Atualiza storage e instância para entity_type_ids: [node].
|
||||
*/
|
||||
function structural_pages_update_10012(): string {
|
||||
$database = \Drupal::database();
|
||||
$schema = $database->schema();
|
||||
$messages = [];
|
||||
|
||||
foreach (['node__field_parent_page', 'node_revision__field_parent_page'] as $table) {
|
||||
if (!$schema->tableExists($table)) {
|
||||
continue;
|
||||
}
|
||||
// Clear rows that pointed to non-node entities (taxonomy_term, user, etc.).
|
||||
$deleted = $database->delete($table)
|
||||
->condition('field_parent_page_target_type', 'node', '<>')
|
||||
->execute();
|
||||
if ($deleted) {
|
||||
$messages[] = "Removidas $deleted linhas não-node em $table.";
|
||||
}
|
||||
}
|
||||
|
||||
// Update storage config.
|
||||
$storage = \Drupal::configFactory()->getEditable('field.storage.node.field_parent_page');
|
||||
if (!$storage->isNew()) {
|
||||
$storage->set('settings.entity_type_ids', ['node'])->save(TRUE);
|
||||
$messages[] = 'Storage field_parent_page restrito a [node].';
|
||||
}
|
||||
|
||||
// Update content_page instance config.
|
||||
$field_cp = \Drupal::configFactory()->getEditable('field.field.node.content_page.field_parent_page');
|
||||
if (!$field_cp->isNew()) {
|
||||
$field_cp->set('settings', [
|
||||
'entity_type_ids' => ['node'],
|
||||
'node' => [
|
||||
'handler' => 'default:node',
|
||||
'handler_settings' => [
|
||||
'target_bundles' => ['content_page' => 'content_page'],
|
||||
'sort' => ['field' => 'title', 'direction' => 'asc'],
|
||||
'auto_create' => FALSE,
|
||||
],
|
||||
],
|
||||
]);
|
||||
// Remove dependencies no longer needed (taxonomy, user).
|
||||
$deps = $field_cp->get('dependencies') ?? [];
|
||||
$config_deps = $deps['config'] ?? [];
|
||||
$config_deps = array_filter($config_deps, fn($c) => !str_contains($c, 'taxonomy.vocabulary') && !str_contains($c, 'node.type.section_page'));
|
||||
$deps['config'] = array_values($config_deps);
|
||||
$field_cp->set('dependencies', $deps);
|
||||
$field_cp->save(TRUE);
|
||||
$messages[] = 'Instância field_parent_page restrita a content_page.';
|
||||
}
|
||||
|
||||
\Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
|
||||
|
||||
return empty($messages) ? 'Nada a migrar.' : implode(' ', $messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Corrige o tipo do storage field_parent_page para dynamic_entity_reference.
|
||||
*
|
||||
* Os update hooks anteriores (10006–10012) alteraram apenas as settings do
|
||||
* campo (entity_type_ids, exclude_entity_types) sem mudar o campo 'type' de
|
||||
* 'entity_reference' para 'dynamic_entity_reference'. Isso fazia com que o
|
||||
* sistema de entity queries do Drupal rejeitasse o especificador 'target_type'
|
||||
* com "Invalid specifier 'target_type'".
|
||||
*
|
||||
* Além disso, se o módulo event_management estiver instalado, adiciona 'event'
|
||||
* ao entity_type_ids do field_site_section para que content_pages possam
|
||||
* referenciar eventos como domínio raiz.
|
||||
*/
|
||||
function structural_pages_update_10013(): string {
|
||||
$messages = [];
|
||||
|
||||
// 1. Migra field_parent_page de entity_reference para dynamic_entity_reference.
|
||||
$storage = \Drupal::configFactory()->getEditable('field.storage.node.field_parent_page');
|
||||
if (!$storage->isNew() && $storage->get('type') !== 'dynamic_entity_reference') {
|
||||
$database = \Drupal::database();
|
||||
$schema = $database->schema();
|
||||
$col_spec = [
|
||||
'type' => 'varchar_ascii',
|
||||
'length' => 128,
|
||||
'not null' => FALSE,
|
||||
'description' => 'Entity type of the field_parent_page reference.',
|
||||
];
|
||||
foreach (['node__field_parent_page', 'node_revision__field_parent_page'] as $table) {
|
||||
if ($schema->tableExists($table) && !$schema->fieldExists($table, 'field_parent_page_target_type')) {
|
||||
$schema->addField($table, 'field_parent_page_target_type', $col_spec);
|
||||
$database->update($table)
|
||||
->fields(['field_parent_page_target_type' => 'node'])
|
||||
->isNull('field_parent_page_target_type')
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
$storage->set('type', 'dynamic_entity_reference');
|
||||
$storage->set('module', 'dynamic_entity_reference');
|
||||
$deps = $storage->get('dependencies') ?? [];
|
||||
$mods = $deps['module'] ?? [];
|
||||
if (!in_array('dynamic_entity_reference', $mods, TRUE)) {
|
||||
$mods[] = 'dynamic_entity_reference';
|
||||
$deps['module'] = $mods;
|
||||
$storage->set('dependencies', $deps);
|
||||
}
|
||||
$storage->save(TRUE);
|
||||
$messages[] = 'field_parent_page migrado para dynamic_entity_reference.';
|
||||
}
|
||||
else {
|
||||
$messages[] = 'field_parent_page já é dynamic_entity_reference.';
|
||||
}
|
||||
|
||||
// 2. Adiciona 'event' ao field_site_section se event_management estiver instalado.
|
||||
if (\Drupal::moduleHandler()->moduleExists('event_management')) {
|
||||
$fss_storage = \Drupal::configFactory()->getEditable('field.storage.node.field_site_section');
|
||||
if (!$fss_storage->isNew() && $fss_storage->get('type') === 'dynamic_entity_reference') {
|
||||
$ids = $fss_storage->get('settings.entity_type_ids') ?? [];
|
||||
if (!in_array('event', $ids, TRUE)) {
|
||||
$ids[] = 'event';
|
||||
$fss_storage->set('settings.entity_type_ids', $ids)->save(TRUE);
|
||||
$messages[] = "'event' adicionado ao entity_type_ids de field_site_section.";
|
||||
}
|
||||
else {
|
||||
$messages[] = "'event' já presente no entity_type_ids de field_site_section.";
|
||||
}
|
||||
}
|
||||
|
||||
// Adiciona handler settings de event na instância content_page.
|
||||
$field_cp = \Drupal::configFactory()->getEditable('field.field.node.content_page.field_site_section');
|
||||
if (!$field_cp->isNew() && $field_cp->get('field_type') === 'dynamic_entity_reference') {
|
||||
$settings = $field_cp->get('settings') ?? [];
|
||||
$type_ids = $settings['entity_type_ids'] ?? [];
|
||||
if (!in_array('event', $type_ids, TRUE)) {
|
||||
$type_ids[] = 'event';
|
||||
$settings['entity_type_ids'] = $type_ids;
|
||||
}
|
||||
if (!isset($settings['event'])) {
|
||||
$settings['event'] = [
|
||||
'handler' => 'default:event',
|
||||
'handler_settings' => [
|
||||
'filter' => ['type' => '_none'],
|
||||
'sort' => ['field' => 'title', 'direction' => 'asc'],
|
||||
'auto_create' => FALSE,
|
||||
],
|
||||
];
|
||||
}
|
||||
$field_cp->set('settings', $settings)->save(TRUE);
|
||||
$messages[] = "Handler settings de event adicionadas ao field_site_section de content_page.";
|
||||
}
|
||||
}
|
||||
|
||||
\Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
|
||||
|
||||
return implode(' ', $messages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_requirements().
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user