Implement ParentEntityHandler plugin system for extensible entity support

Replace hardcoded entity type checks with a plugin-based architecture using
PHP 8 attributes. This allows adding new parent entity types without modifying
core module files.

Changes:
- Add ParentEntityHandler attribute, interface, base class, and manager
- Create built-in handlers for taxonomy_term, user, and node entities
- Move Group support to site_structure_group submodule (fixes class not found
  error when Group module is not installed)
- Refactor SiteStructureSettingsForm to use handler manager
- Refactor SiteStructureMenuBlock to use handler manager
- Refactor SectionBreadcrumbBuilder to use handler manager
- Update site_structure.module to use handler manager for clearsSiteSection
- Update documentation and translations

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-04 08:35:04 -03:00
parent 43fa9208a9
commit 0c8f0fc778
17 changed files with 1153 additions and 117 deletions

View File

@@ -7,9 +7,9 @@ namespace Drupal\site_structure\Form;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
@@ -32,23 +32,11 @@ class SiteStructureSettingsForm extends ConfigFormBase {
protected EntityTypeBundleInfoInterface $entityTypeBundleInfo;
/**
* The module handler.
* The parent entity handler manager.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
* @var \Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerManagerInterface
*/
protected ModuleHandlerInterface $moduleHandler;
/**
* Entity types that can be used as parents.
*
* @var array
*/
protected array $supportedEntityTypes = [
'node' => 'Content Types (node)',
'taxonomy_term' => 'Taxonomy Vocabularies (taxonomy_term)',
'user' => 'Users (user)',
'group' => 'Groups (group)',
];
protected ParentEntityHandlerManagerInterface $handlerManager;
/**
* Constructs a SiteStructureSettingsForm object.
@@ -59,19 +47,19 @@ class SiteStructureSettingsForm extends ConfigFormBase {
* The entity type manager.
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
* The entity type bundle info service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerManagerInterface $handler_manager
* The parent entity handler manager.
*/
public function __construct(
ConfigFactoryInterface $config_factory,
EntityTypeManagerInterface $entity_type_manager,
EntityTypeBundleInfoInterface $entity_type_bundle_info,
ModuleHandlerInterface $module_handler,
ParentEntityHandlerManagerInterface $handler_manager,
) {
parent::__construct($config_factory);
$this->entityTypeManager = $entity_type_manager;
$this->entityTypeBundleInfo = $entity_type_bundle_info;
$this->moduleHandler = $module_handler;
$this->handlerManager = $handler_manager;
}
/**
@@ -82,7 +70,7 @@ class SiteStructureSettingsForm extends ConfigFormBase {
$container->get('config.factory'),
$container->get('entity_type.manager'),
$container->get('entity_type.bundle.info'),
$container->get('module_handler'),
$container->get('plugin.manager.parent_entity_handler'),
);
}
@@ -137,20 +125,12 @@ class SiteStructureSettingsForm extends ConfigFormBase {
'#tree' => TRUE,
];
foreach ($this->supportedEntityTypes as $entity_type => $label) {
// Check if entity type exists.
if (!$this->entityTypeManager->hasDefinition($entity_type)) {
continue;
}
// Special handling for 'group' - check if module is enabled.
if ($entity_type === 'group' && !$this->moduleHandler->moduleExists('group')) {
continue;
}
$supportedEntityTypes = $this->handlerManager->getSupportedEntityTypes();
foreach ($supportedEntityTypes as $entity_type => $label) {
$form['allowed_parent_targets'][$entity_type] = [
'#type' => 'details',
'#title' => $this->t($label),
'#title' => $label,
'#open' => TRUE,
];
@@ -207,8 +187,9 @@ class SiteStructureSettingsForm extends ConfigFormBase {
public function submitForm(array &$form, FormStateInterface $form_state): void {
$targets = [];
$values = $form_state->getValue('allowed_parent_targets');
$supportedEntityTypes = $this->handlerManager->getSupportedEntityTypes();
foreach ($this->supportedEntityTypes as $entity_type => $label) {
foreach ($supportedEntityTypes as $entity_type => $label) {
if (empty($values[$entity_type])) {
continue;
}
@@ -276,12 +257,7 @@ class SiteStructureSettingsForm extends ConfigFormBase {
}
if (!isset($entity_type_settings[$entity_type])) {
$sort_field = match($entity_type) {
'taxonomy_term' => 'name',
'user' => 'name',
'group' => 'label',
default => 'title',
};
$sort_field = $this->handlerManager->getSortField($entity_type);
$entity_type_settings[$entity_type] = [
'handler' => 'default:' . $entity_type,