block_editor-1.0.x-dev/src/Service/EntityManager.php
src/Service/EntityManager.php
<?php
namespace Drupal\block_editor\Service;
use Drupal\Core\Config\Entity\ConfigEntityInterface;
use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Symfony\Component\Yaml\Yaml;
/**
* Manager for Block Editor content types.
*
* @package Drupal\block_editor
*/
class EntityManager {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The entity field manager.
*
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
*/
protected $entityFieldManager;
/**
* The entity type bundle info service.
*
* @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
*/
protected $entityTypeBundleInfo;
/**
* The typed config manager.
*
* @var \Drupal\Core\Config\TypedConfigManagerInterface
*/
protected $typedConfigManager;
/**
* The current route match.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The logger factory.
*
* @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
*/
protected $loggerFactory;
/**
* Static cache for supported entity type mappings.
*
* @var array|null
*/
protected static $supportedMappingsCache = NULL;
/**
* Constructs a new EntityManager.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
* The entity field manager.
* @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info
* The entity type bundle info service.
* @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager
* The typed config manager.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The current route match.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler.
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory
* The logger factory.
*/
public function __construct(
EntityTypeManagerInterface $entity_type_manager,
EntityFieldManagerInterface $entity_field_manager,
EntityTypeBundleInfoInterface $entity_type_bundle_info,
TypedConfigManagerInterface $typed_config_manager,
RouteMatchInterface $route_match,
ModuleHandlerInterface $module_handler,
LoggerChannelFactoryInterface $logger_factory,
) {
$this->entityTypeManager = $entity_type_manager;
$this->entityFieldManager = $entity_field_manager;
$this->entityTypeBundleInfo = $entity_type_bundle_info;
$this->typedConfigManager = $typed_config_manager;
$this->routeMatch = $route_match;
$this->moduleHandler = $module_handler;
$this->loggerFactory = $logger_factory;
}
/**
* Checks if a config entity supports Block Editor settings.
*
* This checks if the entity type has been explicitly configured to support
* Block Editor by checking if it's in the supported mappings list.
*
* @param \Drupal\Core\Config\Entity\ConfigEntityInterface $entity
* The config entity to check.
*
* @return bool
* TRUE if the entity type supports Block Editor settings.
*/
public function supportsBlockEditorSettings(ConfigEntityInterface $entity) {
// Check if the entity supports third-party settings.
// Config entities that extend ThirdPartySettingsInterface support this.
if (!method_exists($entity, 'getThirdPartySetting')) {
return FALSE;
}
// Check if this entity type is in our supported mappings.
// This ensures we only support entity types with explicit schema
// definitions.
$entity_type_id = $entity->getEntityTypeId();
$supported_mappings = $this->getSupportedEntityTypeMappings();
return isset($supported_mappings[$entity_type_id]);
}
/**
* Checks if an entity type supports Block Editor settings by entity type.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type.
*
* @return bool
* TRUE if the entity type supports Block Editor settings.
*/
public function entityTypeSupportsBlockEditor($entity_type) {
$entity_type_id = $entity_type->id();
$bundle_of = $entity_type->getBundleOf();
if (!$bundle_of) {
return FALSE;
}
// Load a sample bundle to check if schema exists.
$bundles = $this->entityTypeBundleInfo->getBundleInfo($bundle_of);
if (empty($bundles)) {
return FALSE;
}
// Get the first bundle to check schema.
$bundle_id = key($bundles);
$bundle = $this->entityTypeManager
->getStorage($entity_type_id)
->load($bundle_id);
if (!$bundle instanceof ConfigEntityInterface) {
return FALSE;
}
return $this->supportsBlockEditorSettings($bundle);
}
/**
* Checks if Block Editor is enabled for an entity.
*
* @param \Drupal\Core\Config\Entity\ConfigEntityInterface $entity
* The config entity.
*
* @return bool
* TRUE if Block Editor is enabled for the entity.
*/
public function isBlockEditorEnabledForEntity(ConfigEntityInterface $entity) {
if (!$this->supportsBlockEditorSettings($entity)) {
return FALSE;
}
return (bool) $entity->getThirdPartySetting('block_editor', 'enabled', FALSE);
}
/**
* Checks if any bundles have Block Editor enabled for an entity type.
*
* @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
* The entity type.
*
* @return bool
* TRUE if any bundles have Block Editor enabled.
*/
public function hasBlockEditorEnabledBundles($entity_type) {
$entity_type_id = $entity_type->id();
$bundle_entity_type = $entity_type->getBundleOf();
if (!$bundle_entity_type) {
return FALSE;
}
$bundles = $this->entityTypeBundleInfo->getBundleInfo($bundle_entity_type);
foreach ($bundles as $bundle_id => $bundle_info) {
$bundle_entity = $this->entityTypeManager
->getStorage($entity_type_id)
->load($bundle_id);
if ($bundle_entity && $this->isBlockEditorEnabledForEntity($bundle_entity)) {
return TRUE;
}
}
return FALSE;
}
/**
* Checks if Block Editor is enabled for a content entity's bundle.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The content entity.
*
* @return bool
* TRUE if Block Editor is enabled for the bundle.
*/
public function isBlockEditorEnabledForContentEntity(ContentEntityInterface $entity): bool {
$entity_type = $entity->getEntityType();
$bundle_entity_type_id = $entity_type->getBundleEntityType();
if (!$bundle_entity_type_id) {
return FALSE;
}
$bundle = $this->entityTypeManager
->getStorage($bundle_entity_type_id)
->load($entity->bundle());
if (!$bundle instanceof ConfigEntityInterface) {
return FALSE;
}
return $this->isBlockEditorEnabledForEntity($bundle);
}
/**
* Gets all entity type mappings that support Block Editor.
*
* Returns an array mapping bundle entity types to their content entity types
* (e.g., 'node_type' => 'node', 'comment_type' => 'comment').
*
* Discovers entity types from *.block_editor.yml configuration files in
* enabled modules.
*
* @return array
* An associative array where keys are bundle entity type IDs and values
* are content entity type IDs.
*/
public function getSupportedEntityTypeMappings(): array {
// Return cached result if available.
if (static::$supportedMappingsCache !== NULL) {
return static::$supportedMappingsCache;
}
// Start with built-in supported entity types.
static::$supportedMappingsCache = [
'node_type' => 'node',
'taxonomy_vocabulary' => 'taxonomy_term',
'block_content_type' => 'block_content',
];
// Discover additional entity types from *.block_editor.yml files.
// @todo In the future, consider implementing schema-only detection to
// reduce to a single file requirement. This would require a more
// sophisticated schema introspection approach that doesn't rely on
// hasConfigSchema() which returns true for generic third_party patterns.
$this->discoverEntityTypesFromYamlFiles();
return static::$supportedMappingsCache;
}
/**
* Discovers entity types from *.block_editor.yml configuration files.
*
* Scans all enabled modules for files matching *.block_editor.yml pattern
* and merges their entity type mappings.
*/
protected function discoverEntityTypesFromYamlFiles(): void {
$modules = $this->moduleHandler->getModuleList();
foreach ($modules as $module_name => $module) {
$config_file = $module->getPath() . '/' . $module_name . '.block_editor.yml';
if (!file_exists($config_file)) {
continue;
}
try {
$config = Yaml::parseFile($config_file);
if (isset($config['entity_types']) && is_array($config['entity_types'])) {
foreach ($config['entity_types'] as $bundle_type => $content_type) {
// Don't override built-in mappings.
if (!isset(static::$supportedMappingsCache[$bundle_type])) {
static::$supportedMappingsCache[$bundle_type] = $content_type;
}
}
}
}
catch (\Exception $e) {
// Log error but don't break - skip this file.
$this->loggerFactory->get('block_editor')->error(
'Error parsing @file: @message',
['@file' => $config_file, '@message' => $e->getMessage()]
);
}
}
}
}
