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