mirror of
https://gitlab.unicamp.br/infimecc_drupal11_modules/structural_pages.git
synced 2026-05-05 21:05:30 -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:
@@ -91,17 +91,15 @@ class SectionBreadcrumbBuilder implements BreadcrumbBuilderInterface
|
||||
// Add "Home" as first item.
|
||||
$breadcrumb->addLink(Link::createFromRoute($this->t('Home'), '<front>'));
|
||||
|
||||
// Determine the context type for content_page.
|
||||
$context = $this->getParentContext($node);
|
||||
// Resolve domain root from field_site_section of this node or its ancestors.
|
||||
$domain = $this->resolveDomain($node);
|
||||
|
||||
if ($context) {
|
||||
// Use the handler manager to build breadcrumbs for the context entity.
|
||||
$this->handlerManager->buildBreadcrumbForEntity($breadcrumb, $context['entity']);
|
||||
} else {
|
||||
// No parent context, check for site section directly.
|
||||
if ($node->hasField('field_site_section') && !$node->get('field_site_section')->isEmpty()) {
|
||||
$term_id = $node->get('field_site_section')->target_id;
|
||||
$this->addTaxonomyBreadcrumbs($breadcrumb, $term_id);
|
||||
if ($domain) {
|
||||
if ($domain['type'] === 'taxonomy_term') {
|
||||
$this->addTaxonomyBreadcrumbs($breadcrumb, $domain['entity']->id());
|
||||
}
|
||||
else {
|
||||
$this->handlerManager->buildBreadcrumbForEntity($breadcrumb, $domain['entity']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,85 +118,88 @@ class SectionBreadcrumbBuilder implements BreadcrumbBuilderInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the root parent context for a content_page.
|
||||
* Resolves the domain root for the given node.
|
||||
*
|
||||
* Traverses up the parent chain to find the root context (user, group, or taxonomy).
|
||||
* Walks up the field_parent_page chain to the topmost content_page, then
|
||||
* reads field_site_section (DER) to get the domain entity (taxonomy_term,
|
||||
* user, event, …).
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The content_page node.
|
||||
* Any node (section_page or content_page).
|
||||
*
|
||||
* @return array|null
|
||||
* An array with 'type' and 'entity' keys, or NULL if no context.
|
||||
* @return array{type: string, entity: \Drupal\Core\Entity\EntityInterface}|null
|
||||
* Array with 'type' and 'entity', or NULL if domain cannot be resolved.
|
||||
*/
|
||||
protected function getParentContext(NodeInterface $node): ?array
|
||||
protected function resolveDomain(NodeInterface $node): ?array
|
||||
{
|
||||
if ($node->bundle() !== 'content_page') {
|
||||
return NULL;
|
||||
}
|
||||
// For section_page or any node with field_site_section, read it directly.
|
||||
if ($node->hasField('field_site_section') && !$node->get('field_site_section')->isEmpty()) {
|
||||
$root = $node;
|
||||
|
||||
$visited = [];
|
||||
$current = $node;
|
||||
|
||||
while (
|
||||
$current instanceof NodeInterface &&
|
||||
$current->hasField('field_parent_page') &&
|
||||
!$current->get('field_parent_page')->isEmpty()
|
||||
) {
|
||||
|
||||
$parent_field = $current->get('field_parent_page')->first();
|
||||
if (!$parent_field) {
|
||||
break;
|
||||
// For content_page, walk up to the topmost node to get the canonical
|
||||
// field_site_section (which should be the same on all nodes in the chain).
|
||||
if ($node->bundle() === 'content_page') {
|
||||
$root = $this->getRootContentPage($node);
|
||||
}
|
||||
|
||||
$parent_entity_type = $parent_field->target_type ?? NULL;
|
||||
$parent_id = $parent_field->target_id ?? NULL;
|
||||
if ($root->hasField('field_site_section') && !$root->get('field_site_section')->isEmpty()) {
|
||||
$fss = $root->get('field_site_section')->first();
|
||||
$domain_type = $fss->target_type ?? 'taxonomy_term';
|
||||
$domain_id = $fss->target_id;
|
||||
|
||||
if (!$parent_entity_type || !$parent_id) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Avoid infinite loops.
|
||||
$visit_key = $parent_entity_type . ':' . $parent_id;
|
||||
if (isset($visited[$visit_key])) {
|
||||
break;
|
||||
}
|
||||
$visited[$visit_key] = TRUE;
|
||||
|
||||
$parent = $this->entityTypeManager
|
||||
->getStorage($parent_entity_type)
|
||||
->load($parent_id);
|
||||
|
||||
if (!$parent) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If parent is user or group, that's our context.
|
||||
if (in_array($parent_entity_type, ['user', 'group'])) {
|
||||
return [
|
||||
'type' => $parent_entity_type,
|
||||
'entity' => $parent,
|
||||
];
|
||||
}
|
||||
|
||||
// If parent is taxonomy_term, that's our context (handled via configured vocabulary).
|
||||
if ($parent_entity_type === 'taxonomy_term') {
|
||||
return [
|
||||
'type' => 'taxonomy_term',
|
||||
'entity' => $parent,
|
||||
];
|
||||
}
|
||||
|
||||
// If parent is a node, continue traversing.
|
||||
if ($parent instanceof NodeInterface) {
|
||||
$current = $parent;
|
||||
} else {
|
||||
break;
|
||||
$domain_entity = $this->entityTypeManager->getStorage($domain_type)->load($domain_id);
|
||||
if ($domain_entity) {
|
||||
$breadcrumb_arg = &$this; // used only to add cache dependency below.
|
||||
return ['type' => $domain_type, 'entity' => $domain_entity];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks up field_parent_page to find the topmost content_page.
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The starting node.
|
||||
*
|
||||
* @return \Drupal\node\NodeInterface
|
||||
* The root content_page (or $node itself if no parent chain found).
|
||||
*/
|
||||
protected function getRootContentPage(NodeInterface $node): NodeInterface
|
||||
{
|
||||
$visited = [];
|
||||
$current = $node;
|
||||
|
||||
while (TRUE) {
|
||||
if (isset($visited[$current->id()])) {
|
||||
break;
|
||||
}
|
||||
$visited[$current->id()] = TRUE;
|
||||
|
||||
if (!$current->hasField('field_parent_page') || $current->get('field_parent_page')->isEmpty()) {
|
||||
return $current;
|
||||
}
|
||||
|
||||
$pf = $current->get('field_parent_page')->first();
|
||||
$parent_id = $pf->target_id ?? NULL;
|
||||
|
||||
if (!$parent_id) {
|
||||
return $current;
|
||||
}
|
||||
|
||||
$parent = $this->entityTypeManager->getStorage('node')->load($parent_id);
|
||||
if (!$parent instanceof NodeInterface) {
|
||||
return $current;
|
||||
}
|
||||
|
||||
$current = $parent;
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds breadcrumbs based on taxonomy hierarchy.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user