From 5eef18a77ba021cca57d71641bbfc0e7a268227a Mon Sep 17 00:00:00 2001 From: Mikael Meulle <21535-just_like_good_vibes@users.noreply.drupalcode.org> Date: Sun, 21 Jul 2024 13:47:48 +0000 Subject: [PATCH] Issue #3460800 by just_like_good_vibes: Reduce code complexity --- .phpmd.xml | 22 ++ .../FieldFormatter/ComponentFormatterBase.php | 132 ++++--- src/Element/ComponentElementBuilder.php | 21 +- src/Element/ComponentForm.php | 2 + src/Element/ComponentPropsForm.php | 2 + src/Element/ComponentSlotForm.php | 4 + src/Element/ComponentSlotsForm.php | 2 + src/Form/ComponentFormBuilderTrait.php | 2 + .../EntityFieldSourceDeriverBase.php | 347 ++++++++++++------ .../Source/DerivableContextSourceBase.php | 2 + src/SourcePluginManager.php | 35 +- tests/src/Kernel/SourcePluginManagerTest.php | 2 + 12 files changed, 400 insertions(+), 173 deletions(-) create mode 100644 .phpmd.xml diff --git a/.phpmd.xml b/.phpmd.xml new file mode 100644 index 000000000..3ebb89892 --- /dev/null +++ b/.phpmd.xml @@ -0,0 +1,22 @@ +<?xml version="1.0"?> +<ruleset name="UI Patterns PHPMD Rules" + xmlns="http://pmd.sf.net/ruleset/1.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 + http://pmd.sf.net/ruleset_xml_schema.xsd" + xsi:noNamespaceSchemaLocation=" + http://pmd.sf.net/ruleset_xml_schema.xsd"> + <description> + UI Patterns PHPMD Rules + </description> + + <!-- Import the entire unused code rule set --> + <rule ref="rulesets/unusedcode.xml" /> + + <!-- + Import the entire cyclomatic complexity rule and + customize the rule configuration. + --> + <rule ref="rulesets/codesize.xml/CyclomaticComplexity"> + </rule> +</ruleset> diff --git a/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentFormatterBase.php b/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentFormatterBase.php index e0a1b57b6..99adbc8c0 100644 --- a/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentFormatterBase.php +++ b/modules/ui_patterns_field_formatters/src/Plugin/Field/FieldFormatter/ComponentFormatterBase.php @@ -64,6 +64,13 @@ abstract class ComponentFormatterBase extends FormatterBase { */ protected $routeMatch; + /** + * The module handler. + * + * @var \Drupal\Core\Extension\ModuleHandlerInterface + */ + protected $moduleHandler; + /** * The provided plugin contexts. * @@ -96,6 +103,7 @@ abstract class ComponentFormatterBase extends FormatterBase { $instance->sampleEntityGenerator = $container->get('ui_patterns.sample_entity_generator'); $instance->entityFieldManager = $container->get('entity_field.manager'); $instance->routeMatch = $container->get('current_route_match'); + $instance->moduleHandler = $container->get('module_handler'); return $instance; } @@ -211,48 +219,14 @@ abstract class ComponentFormatterBase extends FormatterBase { $contexts = $this->context ?? []; $field_definition = $this->fieldDefinition; $entity_type_id = $field_definition->getTargetEntityTypeId(); - $entity = NULL; // When field items are available, we can get the entity directly. - if ($items) { - $entity = $items->getEntity(); + $entity = ($items) ? $items->getEntity() : NULL; + if (!$entity && ($layoutBuilderContext = $this->getLayoutBuilderEntityContext()) && $layoutBuilderContext["entity"]) { + $contexts = array_merge($contexts, $layoutBuilderContext["context"]); + $entity = $layoutBuilderContext["entity"]; } - if (!$entity) { - // Infer the entity context, when in layout builder. - $section_storage = $this->routeMatch->getParameter("section_storage"); - if ($section_storage instanceof SectionStorageInterface) { - $section_storage_contexts = $section_storage->getContexts(); - $contexts = array_merge($contexts, $section_storage_contexts); - if (!array_key_exists("entity", $contexts)) { - // Search for the entity in the context, with another name. - foreach ($section_storage_contexts as $section_storage_context) { - if ($section_storage_context instanceof EntityContext) { - // Now check if it is and entity view display. - $entity_in_context = $section_storage_context->getContextValue(); - if ($entity_in_context instanceof EntityViewDisplayInterface) { - $entity_type_id = $entity_in_context->getTargetEntityTypeId(); - $bundle = $entity_in_context->getTargetBundle(); - $entity = $this->sampleEntityGenerator->get($entity_type_id, $bundle); - break; - } - elseif ($entity_in_context instanceof EntityInterface) { - $entity = $section_storage_context->getContextValue(); - break; - } - - } - } - } - } - } - $contextFieldNameDefinition = ContextDefinition::create('string'); - $contextFieldNameDefinition->setLabel("field_name"); - $contextFieldName = new Context($contextFieldNameDefinition, $field_definition->getName()); - $contexts['field_name'] = $contextFieldName; - + $contexts['field_name'] = new Context(ContextDefinition::create('string'), $field_definition->getName()); $contexts = RequirementsContext::addToContext(["field_formatter"], $contexts); - - $contextBundleDefinition = ContextDefinition::create('string'); - $contextBundleDefinition->setLabel("bundle"); $bundle = ($entity) ? $entity->bundle() : $field_definition->getTargetBundle(); if (NULL === $bundle) { // Generate a default bundle when it is missing, @@ -261,22 +235,42 @@ abstract class ComponentFormatterBase extends FormatterBase { // @todo better implementation with service 'entity_type.bundle.info' $bundle = $this->findEntityBundleWithField($entity_type_id, $field_definition->getName()); } - $contextBundle = new Context($contextBundleDefinition, $bundle); - $contexts['bundle'] = $contextBundle; + $contexts['bundle'] = new Context(ContextDefinition::create('string'), $bundle); if (!$entity) { $entity = $this->sampleEntityGenerator->get($entity_type_id, $bundle); } $contexts['entity'] = EntityContext::fromEntity($entity); - if ((NULL === $contexts['entity']) || - (NULL === $contexts['entity']->getContextValue()) || + if (!$this->checkContextSanity($contexts, $entity_type_id, $bundle)) { + throw new \LogicException("The entity context is not properly set."); + } + return $contexts; + } + + /** + * Check the sanity of the context. + * + * @param array<string, \Drupal\Core\Plugin\Context\ContextInterface> $contexts + * The contexts. + * @param string $entity_type_id + * The entity type id. + * @param string $bundle + * The bundle. + * + * @return bool + * TRUE if the context is sane. + */ + protected function checkContextSanity(array $contexts, string $entity_type_id, string $bundle) : bool { + if (!isset($contexts['entity']) || !($contexts['entity'] instanceof EntityContext)) { + return FALSE; + } + if ((NULL === $contexts['entity']->getContextValue()) || !($contexts['entity']->getContextValue() instanceof EntityInterface) || ($contexts['entity']->getContextValue()->getEntityTypeId() !== $entity_type_id) || ($contexts['entity']->getContextValue()->bundle() !== $bundle) ) { - throw new \LogicException("The entity context is not properly set."); + return FALSE; } - - return $contexts; + return TRUE; } /** @@ -330,4 +324,50 @@ abstract class ComponentFormatterBase extends FormatterBase { return $dependencies; } + /** + * Get the entity context when in layout builder. + * + * @return array + * The entity and context. + * + * @see \Drupal\ui_patterns_field_formatters\Plugin\Field\FieldFormatter\ComponentFormatterBase::getComponentSourceContexts() + */ + protected function getLayoutBuilderEntityContext() : array { + $returned = [ + "entity" => NULL, + "context" => [], + ]; + if (!$this->moduleHandler->moduleExists("layout_builder")) { + return $returned; + } + // Infer the entity context, when in layout builder. + $section_storage = $this->routeMatch->getParameter("section_storage"); + if ($section_storage instanceof SectionStorageInterface) { + $section_storage_contexts = $section_storage->getContexts(); + $returned["context"] = $section_storage_contexts; + if (array_key_exists("entity", $section_storage_contexts) && ($entity = $section_storage_contexts["entity"]->getContextValue())) { + $returned["entity"] = $entity; + return $returned; + } + // Search for the entity in the context, with another name. + foreach ($section_storage_contexts as $section_storage_context) { + if ($section_storage_context instanceof EntityContext) { + // Now check if it is and entity view display. + $entity_in_context = $section_storage_context->getContextValue(); + if ($entity_in_context instanceof EntityViewDisplayInterface) { + $entity_type_id = $entity_in_context->getTargetEntityTypeId(); + $bundle = $entity_in_context->getTargetBundle(); + $returned["entity"] = $this->sampleEntityGenerator->get($entity_type_id, $bundle); + break; + } + elseif ($entity_in_context instanceof EntityInterface) { + $returned["entity"] = $section_storage_context->getContextValue(); + break; + } + } + } + } + return $returned; + } + } diff --git a/src/Element/ComponentElementBuilder.php b/src/Element/ComponentElementBuilder.php index 14397e02f..461f09d0c 100644 --- a/src/Element/ComponentElementBuilder.php +++ b/src/Element/ComponentElementBuilder.php @@ -199,36 +199,27 @@ class ComponentElementBuilder implements TrustedCallbackInterface { * @see \Drupal\Core\Config\Entity\ConfigDependencyManager */ public function calculateComponentDependencies(?string $component_id = NULL, array $configuration = [], array $contexts = []) : array { - if ($component_id && isset($configuration['component_id'])) { - $component_id = $configuration['component_id']; - } $dependencies = []; - $component = $this->componentPluginManager->find($component_id); + $component = $this->componentPluginManager->find($component_id ?? $configuration['component_id']); $props = $component->metadata->schema['properties'] ?? []; foreach ($props as $prop_id => $definition) { if ($prop_id === 'variant') { continue; } - $prop_configuration = $configuration['props'][$prop_id] ?? []; - $source = $this->getSource($prop_id, $definition, $prop_configuration, $contexts); - if (!$source) { - continue; + if ($source = $this->getSource($prop_id, $definition, $configuration['props'][$prop_id] ?? [], $contexts)) { + SourcePluginBase::mergeConfigDependencies($dependencies, $source->calculateDependencies()); } - SourcePluginBase::mergeConfigDependencies($dependencies, $source->calculateDependencies()); } $slots = $component->metadata->slots ?? []; foreach ($slots as $slot_id => $definition) { $slot_configuration = $configuration['slots'][$slot_id] ?? []; - if (!isset($slot_configuration["sources"])) { + if (!isset($slot_configuration["sources"]) || !is_array($slot_configuration["sources"])) { continue; } foreach ($slot_configuration["sources"] as $source_configuration) { - $source = $this->getSource($slot_id, $definition, $source_configuration, $contexts); - if (!$source) { - \Drupal::logger('ui_patterns')->warning(sprintf("source not found for slot %d", $slot_id)); - continue; + if ($source = $this->getSource($slot_id, $definition, $source_configuration, $contexts)) { + SourcePluginBase::mergeConfigDependencies($dependencies, $source->calculateDependencies()); } - SourcePluginBase::mergeConfigDependencies($dependencies, $source->calculateDependencies()); } } return $dependencies; diff --git a/src/Element/ComponentForm.php b/src/Element/ComponentForm.php index e4786974b..d26a0b5d9 100644 --- a/src/Element/ComponentForm.php +++ b/src/Element/ComponentForm.php @@ -100,6 +100,8 @@ class ComponentForm extends ComponentFormBase { /** * Processes the main form element including component selector. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public static function buildForm(array &$element, FormStateInterface $form_state) : array { diff --git a/src/Element/ComponentPropsForm.php b/src/Element/ComponentPropsForm.php index c0a8668a8..f1e9172b8 100644 --- a/src/Element/ComponentPropsForm.php +++ b/src/Element/ComponentPropsForm.php @@ -63,6 +63,8 @@ class ComponentPropsForm extends ComponentFormBase { /** * Build props forms. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public static function buildForm(array &$element, FormStateInterface $form_state): array { $component = static::getComponent($element); diff --git a/src/Element/ComponentSlotForm.php b/src/Element/ComponentSlotForm.php index 6d6f1547a..be859a586 100644 --- a/src/Element/ComponentSlotForm.php +++ b/src/Element/ComponentSlotForm.php @@ -343,6 +343,8 @@ class ComponentSlotForm extends ComponentFormBase { /** * Ajax submit handler: Add source. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public static function addSource(array $form, FormStateInterface $form_state) : void { $trigger_element = $form_state->getTriggeringElement(); @@ -368,6 +370,8 @@ class ComponentSlotForm extends ComponentFormBase { /** * Ajax submit handler: Remove source. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public static function removeSource(array $form, FormStateInterface $form_state): void { $trigger_element = $form_state->getTriggeringElement(); diff --git a/src/Element/ComponentSlotsForm.php b/src/Element/ComponentSlotsForm.php index 79d755f6d..bc07015ff 100644 --- a/src/Element/ComponentSlotsForm.php +++ b/src/Element/ComponentSlotsForm.php @@ -58,6 +58,8 @@ class ComponentSlotsForm extends ComponentFormBase { /** * Processes slots form element. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public static function buildForm(array &$element, FormStateInterface $form_state): array { diff --git a/src/Form/ComponentFormBuilderTrait.php b/src/Form/ComponentFormBuilderTrait.php index 4a566a790..a30a8ed18 100644 --- a/src/Form/ComponentFormBuilderTrait.php +++ b/src/Form/ComponentFormBuilderTrait.php @@ -71,6 +71,8 @@ trait ComponentFormBuilderTrait { * * @return \Drupal\Core\Url|null * The ajax url. + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ protected function getAjaxUrl(FormStateInterface $form_state): ?Url { return NULL; diff --git a/src/Plugin/Derivative/EntityFieldSourceDeriverBase.php b/src/Plugin/Derivative/EntityFieldSourceDeriverBase.php index 07e5cff0d..3bef1b62b 100644 --- a/src/Plugin/Derivative/EntityFieldSourceDeriverBase.php +++ b/src/Plugin/Derivative/EntityFieldSourceDeriverBase.php @@ -7,6 +7,7 @@ use Drupal\Core\Entity\EditorialContentEntityBase; use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityPublishedTrait; use Drupal\Core\Entity\EntityTypeBundleInfoInterface; +use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Field\BaseFieldDefinition; @@ -70,6 +71,232 @@ abstract class EntityFieldSourceDeriverBase extends DeriverBase implements Conta ); } + /** + * Get entity fields classification. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type_definition + * The entity type definition. + * + * @return array + * The classification of the fields. + * + * @throws \Drupal\Core\Entity\Exception\UnsupportedEntityTypeDefinitionException + */ + protected function getEntityFieldsClassification(EntityTypeInterface $entity_type_definition) { + // Location of field definitions for this entity. + $entity_type_class = $entity_type_definition->getClass(); + $entity_type_parent_class = get_parent_class($entity_type_class); + $fields_editorial = []; + $parents_base = []; + $fields_base = []; + if (is_subclass_of($entity_type_class, EditorialContentEntityBase::class)) { + $fields_editorial = array_merge($fields_editorial, array_keys($entity_type_class::revisionLogBaseFieldDefinitions($entity_type_definition) ?? [])); + } + if (is_subclass_of($entity_type_class, EntityPublishedTrait::class)) { + $fields_editorial = array_merge($fields_editorial, array_keys($entity_type_class::publishedBaseFieldDefinitions($entity_type_definition) ?? [])); + } + if (is_subclass_of($entity_type_parent_class, FieldableEntityInterface::class)) { + $parents_base = array_keys($entity_type_parent_class::baseFieldDefinitions($entity_type_definition) ?? []); + $fields_base = array_keys($entity_type_class::baseFieldDefinitions($entity_type_definition) ?? []); + } + return [ + "fields_editorial" => $fields_editorial, + "parents_base" => $parents_base, + "fields_base" => $fields_base, + ]; + } + + /** + * Get entity field storage metadata. + * + * @param array $field_storage_definitions + * The field storage definitions. + * @param array $entity_field_map + * The entity field map. + * @param array $entityFieldsClassification + * The classification of the fields. + * + * @return array + * The metadata for each field. + */ + protected function getEntityFieldStorageMetadata( + array $field_storage_definitions, + array $entity_field_map, + array $entityFieldsClassification, + ) { + $returned = []; + // Field storage definitions. + foreach ($entity_field_map as $field_name => $field_info) { + if (!array_key_exists($field_name, $field_storage_definitions)) { + continue; + } + $field_storage_definition = $field_storage_definitions[$field_name]; + $is_base = (in_array($field_name, $entityFieldsClassification["fields_base"]) || ($field_storage_definition instanceof BaseFieldDefinition)); + $returned[$field_name] = [ + "label" => $field_storage_definition->getLabel(), + "bundles" => array_values($field_info['bundles']), + "metadata" => [ + "configurable" => ($field_storage_definition instanceof FieldStorageConfig), + "editorial" => in_array($field_name, $entityFieldsClassification["fields_editorial"]), + "parent_base" => in_array($field_name, $entityFieldsClassification["parents_base"]), + "base" => $is_base, + ], + 'config_dependencies' => [], + 'properties' => [], + ]; + // Derive for each property, property information. + foreach ($field_storage_definition->getPropertyDefinitions() as $property_id => $property_definition) { + // Skip entity reference + // Description could have been more precise, + // but at the price of loading lot of stuff for nothing. + // @todo check UI/UX needs for improvement of this. + $description = $this->t('Property "@property" of field "@field', + [ + '@property' => $property_definition->getLabel(), + '@field' => $field_storage_definition->getLabel(), + ]); + $label = $this->t("Field property: @property", ["@property" => $property_definition->getLabel()]); + $returned[$field_name]["properties"][$property_id] = [ + "label" => $label, + "description" => $description, + "data_type" => $property_definition->getDataType(), + ]; + } + } + return $returned; + } + + /** + * Get entity bundle field metadata. + * + * @param string $field_name + * The field name. + * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition + * The field definition. + * @param \Drupal\Core\TypedData\DataDefinitionInterface $item_definition + * The item definition. + * @param array $entityFieldsClassification + * The classification of the fields. + * + * @return array + * The metadata for the field. + */ + protected function getEntityBundleFieldMetadata( + string $field_name, + $field_definition, + $item_definition, + array $entityFieldsClassification, + ) { + $main_property_name = (method_exists($item_definition, "getMainPropertyName")) ? $item_definition->getMainPropertyName() : NULL; + $returned = [ + "label" => $field_definition->getLabel(), + "config_dependencies" => [], + "metadata" => [ + "configurable" => ($field_definition instanceof FieldConfigInterface), + "editorial" => in_array($field_name, $entityFieldsClassification["fields_editorial"]), + "parent_base" => in_array($field_name, $entityFieldsClassification["parents_base"]), + "base" => in_array($field_name, $entityFieldsClassification["fields_base"]) || ($field_definition instanceof BaseFieldDefinition), + ], + "main_property" => $main_property_name, + ]; + // Config dependencies. + if ($field_definition instanceof FieldConfigInterface) { + $returned['config_dependencies'][$field_definition->getConfigDependencyKey()][] = + $field_definition->getConfigDependencyName(); + } + return $returned; + } + + /** + * Get entity bundle field metadata for entity reference. + * + * @param string|null $main_property_name + * The main property name. + * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition + * The field definition. + * @param \Drupal\Core\TypedData\DataDefinitionInterface $item_definition + * The item definition. + * @param array $entity_type_definitions + * The entity type definitions. + * + * @return array + * The metadata for the entity reference. + */ + protected function getEntityBundleFieldMetadataEntityReference( + ?string $main_property_name, + $field_definition, + $item_definition, + array $entity_type_definitions, + ) { + $returned = []; + $main_property_definition = ($main_property_name && method_exists($item_definition, "getPropertyDefinition")) ? $item_definition->getPropertyDefinition($main_property_name) : NULL; + // Entity reference. + if ($main_property_definition instanceof DataReferenceTargetDefinition) { + $target_entity_type_id = $item_definition->getSetting('target_type'); + $target_entity_type_definition = $entity_type_definitions[$target_entity_type_id]; + $returned = [ + "entity_type_id" => $target_entity_type_id, + "fieldable" => $target_entity_type_definition->entityClassImplements(FieldableEntityInterface::class), + "bundles" => [], + ]; + $target_type_to_bundles = NULL; + $item_class = $item_definition->getClass(); + if (is_subclass_of($item_class, EntityReferenceItemInterface::class)) { + $target_type_to_bundles = $item_class::getReferenceableBundles($field_definition); + } + if ($target_type_to_bundles && array_key_exists($target_entity_type_id, $target_type_to_bundles)) { + $target_bundles = array_values($target_type_to_bundles[$target_entity_type_id]); + $returned['bundles'] = $target_bundles; + } + } + return $returned; + } + + /** + * Get entity bundle fields metadata. + * + * @param string $entity_type_id + * The entity type id. + * @param array $field_storage_definitions + * The field storage definitions. + * @param array $entity_type_definitions + * The entity type definitions. + * @param array $entityFieldsClassification + * The classification of the fields. + * + * @return array + * The metadata for each bundle and each field. + */ + protected function getEntityBundleFieldsMetadata( + string $entity_type_id, + array $field_storage_definitions, + array $entity_type_definitions, + array $entityFieldsClassification, + ) : array { + $returned = []; + // Derive for each bundle, field information. + $bundle_list = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id); + foreach ($bundle_list as $bundle => $bundle_info) { + $field_definitions = $this->entityFieldManager->getFieldDefinitions($entity_type_id, $bundle); + // ------ + $returned[$bundle] = [ + "label" => $bundle_info["label"], + "fields" => [], + ]; + foreach ($field_definitions as $field_name => $field_definition) { + if (!array_key_exists($field_name, $field_storage_definitions)) { + continue; + } + $item_definition = $field_definition->getItemDefinition(); + $returned[$bundle]["fields"][$field_name] = + $this->getEntityBundleFieldMetadata($field_name, $field_definition, $item_definition, $entityFieldsClassification); + $returned[$bundle]["fields"][$field_name]["entity_reference"] = + $this->getEntityBundleFieldMetadataEntityReference($returned[$bundle]["fields"][$field_name]["main_property"], $field_definition, $item_definition, $entity_type_definitions); + } + } + return $returned; + } + /** * Get data about entity fields. * @@ -91,116 +318,18 @@ abstract class EntityFieldSourceDeriverBase extends DeriverBase implements Conta continue; } $entity_field_map = $all_entity_field_map[$entity_type_id]; + $entityFieldsClassification = $this->getEntityFieldsClassification($entity_type_definition); $field_storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($entity_type_id); - $fields_metadata[$entity_type_id]["bundles"] = []; - $fields_metadata[$entity_type_id]["field_storages"] = []; - // Location of field definitions for this entity. - $entity_type_class = $entity_type_definition->getClass(); - $entity_type_parent_class = get_parent_class($entity_type_class); - $fields_editorial = []; - $parents_base = []; - $fields_base = []; - if (is_subclass_of($entity_type_class, EditorialContentEntityBase::class)) { - $fields_editorial = array_merge($fields_editorial, array_keys($entity_type_class::revisionLogBaseFieldDefinitions($entity_type_definition) ?? [])); - } - if (is_subclass_of($entity_type_class, EntityPublishedTrait::class)) { - $fields_editorial = array_merge($fields_editorial, array_keys($entity_type_class::publishedBaseFieldDefinitions($entity_type_definition) ?? [])); - } - if (is_subclass_of($entity_type_parent_class, FieldableEntityInterface::class)) { - $parents_base = array_keys($entity_type_parent_class::baseFieldDefinitions($entity_type_definition) ?? []); - $fields_base = array_keys($entity_type_class::baseFieldDefinitions($entity_type_definition) ?? []); - } - // Field storage definitions. - foreach ($entity_field_map as $field_name => $field_info) { - if (!array_key_exists($field_name, $field_storage_definitions)) { - continue; - } - $field_storage_definition = $field_storage_definitions[$field_name]; - $is_base = (in_array($field_name, $fields_base) || ($field_storage_definition instanceof BaseFieldDefinition)); - $fields_metadata[$entity_type_id]["field_storages"][$field_name] = [ - "label" => $field_storage_definition->getLabel(), - "bundles" => array_values($field_info['bundles']), - "metadata" => [ - "configurable" => ($field_storage_definition instanceof FieldStorageConfig), - "editorial" => in_array($field_name, $fields_editorial), - "parent_base" => in_array($field_name, $parents_base), - "base" => $is_base, - ], - 'config_dependencies' => [], - 'properties' => [], - ]; - // Derive for each property, property information. - foreach ($field_storage_definition->getPropertyDefinitions() as $property_id => $property_definition) { - // Skip entity reference - // Description could have been more precise, - // but at the price of loading lot of stuff for nothing. - // @todo check UI/UX needs for improvement of this. - $description = $this->t('Property "@property" of field "@field', - [ - '@property' => $property_definition->getLabel(), - '@field' => $field_storage_definition->getLabel(), - ]); - $label = $this->t("Field property: @property", ["@property" => $property_definition->getLabel()]); - $fields_metadata[$entity_type_id]["field_storages"][$field_name]["properties"][$property_id] = [ - "label" => $label, - "description" => $description, - "data_type" => $property_definition->getDataType(), - ]; - } - } - // Derive for each bundle, field information. - $bundle_list = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id); - foreach ($bundle_list as $bundle => $bundle_info) { - $field_definitions = $this->entityFieldManager->getFieldDefinitions($entity_type_id, $bundle); - // ------ - $fields_metadata[$entity_type_id]["bundles"][$bundle] = [ - "label" => $bundle_info["label"], - "fields" => [], - ]; - foreach ($field_definitions as $field_name => $field_definition) { - if (!array_key_exists($field_name, $field_storage_definitions)) { - continue; - } - $item_definition = $field_definition->getItemDefinition(); - $main_property_name = (method_exists($item_definition, "getMainPropertyName")) ? $item_definition->getMainPropertyName() : NULL; - $main_property_definition = ($main_property_name && method_exists($item_definition, "getPropertyDefinition")) ? $item_definition->getPropertyDefinition($main_property_name) : NULL; - $is_base = in_array($field_name, $fields_base) || ($field_definition instanceof BaseFieldDefinition); - $fields_metadata[$entity_type_id]["bundles"][$bundle]["fields"][$field_name] = [ - "label" => $field_definition->getLabel(), - "config_dependencies" => [], - "metadata" => [ - "configurable" => ($field_definition instanceof FieldConfigInterface), - "editorial" => in_array($field_name, $fields_editorial), - "parent_base" => in_array($field_name, $parents_base), - "base" => $is_base, - ], - "entity_reference" => [], - "main_property" => $main_property_name, - ]; - // Config dependencies. - if ($field_definition instanceof FieldConfigInterface) { - $fields_metadata[$entity_type_id]["bundles"][$bundle]["fields"][$field_name]['config_dependencies'][$field_definition->getConfigDependencyKey()][] = - $field_definition->getConfigDependencyName(); - $fields_metadata[$entity_type_id]["field_storages"][$field_name]['config_dependencies'][$field_definition->getConfigDependencyKey()][] = - $field_definition->getConfigDependencyName(); - } - // Entity reference. - if ($main_property_definition instanceof DataReferenceTargetDefinition) { - $target_entity_type_id = $item_definition->getSetting('target_type'); - $target_entity_type_definition = $entity_type_definitions[$target_entity_type_id]; - $fields_metadata[$entity_type_id]["bundles"][$bundle]["fields"][$field_name]['entity_reference'] = [ - "entity_type_id" => $target_entity_type_id, - "fieldable" => $target_entity_type_definition->entityClassImplements(FieldableEntityInterface::class), - "bundles" => [], - ]; - $target_type_to_bundles = NULL; - $item_class = $item_definition->getClass(); - if (is_subclass_of($item_class, EntityReferenceItemInterface::class)) { - $target_type_to_bundles = $item_class::getReferenceableBundles($field_definition); - } - if ($target_type_to_bundles && array_key_exists($target_entity_type_id, $target_type_to_bundles)) { - $target_bundles = array_values($target_type_to_bundles[$target_entity_type_id]); - $fields_metadata[$entity_type_id]["bundles"][$bundle]["fields"][$field_name]['entity_reference']['bundles'] = $target_bundles; + $fields_metadata[$entity_type_id]["field_storages"] = $this->getEntityFieldStorageMetadata($field_storage_definitions, $entity_field_map, $entityFieldsClassification); + $fields_metadata[$entity_type_id]["bundles"] = $this->getEntityBundleFieldsMetadata($entity_type_id, $field_storage_definitions, $entity_type_definitions, $entityFieldsClassification); + foreach ($fields_metadata[$entity_type_id]["bundles"] as $bundle_data) { + foreach ($bundle_data["fields"] as $field_name => $field_data) { + if (isset($field_data["config_dependencies"])) { + foreach ($field_data["config_dependencies"] as $config_dependency_key => $config_dependency_names) { + if (!isset($fields_metadata[$entity_type_id]["field_storages"][$field_name]['config_dependencies'][$config_dependency_key])) { + $fields_metadata[$entity_type_id]["field_storages"][$field_name]['config_dependencies'][$config_dependency_key] = []; + } + $fields_metadata[$entity_type_id]["field_storages"][$field_name]['config_dependencies'][$config_dependency_key][] = array_values($config_dependency_names)[0]; } } } diff --git a/src/Plugin/UiPatterns/Source/DerivableContextSourceBase.php b/src/Plugin/UiPatterns/Source/DerivableContextSourceBase.php index a4e06a2d6..6712869b7 100644 --- a/src/Plugin/UiPatterns/Source/DerivableContextSourceBase.php +++ b/src/Plugin/UiPatterns/Source/DerivableContextSourceBase.php @@ -148,6 +148,8 @@ abstract class DerivableContextSourceBase extends SourcePluginBase { * Returned form * * @throws \Drupal\Component\Plugin\Exception\PluginException + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ private function buildDerivableContextSelectorForm(array &$form, FormStateInterface $form_state) : array { $derivableContexts = $this->listDerivableContexts(); diff --git a/src/SourcePluginManager.php b/src/SourcePluginManager.php index 2c7a4457c..e5521b1bf 100644 --- a/src/SourcePluginManager.php +++ b/src/SourcePluginManager.php @@ -114,12 +114,43 @@ class SourcePluginManager extends DefaultPluginManager implements ContextAwarePl * Source definitions, keyed by source id. */ public function getNativeDefinitionsForPropType(string $prop_type_id, ?array $contexts = [], array $tag_filter = []): array { + // Filter by context. $definitions = (NULL === $contexts) ? $this->getDefinitions() : $this->getDefinitionsForContextsRefined($contexts); - $definitions = array_filter($definitions, function ($definition) use ($prop_type_id, $tag_filter) { + // Filter by prop type. + $definitions = array_filter($definitions, static function ($definition) use ($prop_type_id) { $supported_prop_types = array_key_exists("prop_types", $definition) ? $definition['prop_types'] : []; if (is_array($supported_prop_types) && (count($supported_prop_types) > 0) && !in_array($prop_type_id, $supported_prop_types)) { return FALSE; } + return TRUE; + }); + // Filter by tags. + if (is_array($tag_filter) && count($tag_filter) > 0) { + $definitions = static::filterDefinitionsByTags($definitions, $tag_filter); + } + foreach ($definitions as &$definition) { + $definition["tags"] = array_merge(array_key_exists("tags", $definition) ? $definition["tags"] : [], ["prop_type_matched:" . $prop_type_id]); + } + unset($definition); + return $definitions; + } + + /** + * Filters definitions by tags. + * + * @param array $definitions + * The definitions. + * @param array<string, bool> $tag_filter + * Filter results by tags. + * The array keys are the tags, and the values are boolean. + * If the value is TRUE, the tag is required. + * If the value is FALSE, the tag is forbidden. + * + * @return array + * The filtered definitions. + */ + protected static function filterDefinitionsByTags(array $definitions, array $tag_filter): array { + return array_filter($definitions, static function ($definition) use ($tag_filter) { $tags = array_key_exists("tags", $definition) ? $definition['tags'] : []; if (count($tag_filter) > 0) { foreach ($tag_filter as $tag => $tag_required) { @@ -129,10 +160,8 @@ class SourcePluginManager extends DefaultPluginManager implements ContextAwarePl } } } - $definition["tags"] = array_merge($tags, ["prop_type_matched:" . $prop_type_id]); return TRUE; }); - return $definitions; } /** diff --git a/tests/src/Kernel/SourcePluginManagerTest.php b/tests/src/Kernel/SourcePluginManagerTest.php index 2001bc1a5..3d4dff365 100644 --- a/tests/src/Kernel/SourcePluginManagerTest.php +++ b/tests/src/Kernel/SourcePluginManagerTest.php @@ -125,6 +125,8 @@ final class SourcePluginManagerTest extends KernelTestBase { * Test getDefinitionsForPropType. * * @dataProvider providerPropTypeDefinitions + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function testGetPropTypeDefault($prop_type_id, $context_keys = [], $should_contains = [], $not_contains = []): void { $contexts = $this->getContextsByIds($context_keys); -- GitLab