From f78bb6e7f3826581056a886d0c18ffaef534696b Mon Sep 17 00:00:00 2001
From: git <git@3644742.no-reply.drupal.org>
Date: Thu, 2 Jul 2020 15:54:01 -0400
Subject: [PATCH] 1161314-242

---
 conditional_fields.api.php                  | 111 ++++++++
 conditional_fields.module                   |  75 ++----
 config/schema/conditional_fields.schema.yml |  13 +
 src/ConditionalFieldsFormHelper.php         |   1 +
 src/DependencyHelper.php                    | 277 ++++++++++++++++++++
 src/Form/ConditionalFieldEditForm.php       |  46 +++-
 src/Form/ConditionalFieldForm.php           | 132 +++++++---
 7 files changed, 559 insertions(+), 96 deletions(-)
 create mode 100644 conditional_fields.api.php
 create mode 100644 src/DependencyHelper.php

diff --git a/conditional_fields.api.php b/conditional_fields.api.php
new file mode 100644
index 0000000..dd8e8b2
--- /dev/null
+++ b/conditional_fields.api.php
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * @file
+ * Hooks for the conditional_fields module.
+ */
+
+/**
+ * @addtogroup hooks
+ * @{
+ */
+
+/**
+ * Build a list of available fields.
+ *
+ * Fields that use the Field API should be available to Conditional Fields
+ * automatically. This hook provides a mechanism to register pseudo-fields
+ * (such as those provided by Field Group.)
+ *
+ * @param string $entity_type
+ *   Name of the entity type being configured.
+ * @param string $bundle_name
+ *   Name of the entity bundle being configured.
+ *
+ * @return array Fields provided by this module.
+ *   Keyed by machine name, with field labels as values.
+ *
+ * @see ConditionalFieldForm::getFields().
+ * @see hook_conditional_fields_alter().
+ * @see conditional_fields_conditional_fields().
+ */
+function hook_conditional_fields($entity_type, $bundle_name) {
+  $fields = [];
+  $groups = field_group_info_groups($entity_type, $bundle_name, 'form', 'default');
+  foreach ($groups as $name => $group) {
+    $fields[$name] = $group->label;
+  }
+  return $fields;
+}
+
+/**
+ * Alter the list of available fields.
+ *
+ * @param string &$fields
+ *   Fields provided by hook_conditional_fields().
+ * @param string $entity_type
+ *   Name of the entity type being configured.
+ * @param string $bundle_name
+ *   Name of the entity bundle being configured.
+ * @param string $fields
+ *   Fields available to this module, provided by modules that implement
+ *   hook_conditional_fields().
+ *
+ * @see ConditionalFieldForm::getFields().
+ * @see hook_conditional_fields().
+ * @see conditional_fields_conditional_fields_alter().
+ */
+function hook_conditional_fields_alter(&$fields, $entity_type, $bundle_name) {
+  asort($fields);
+}
+
+/**
+ * Return a list of fields contained within a given field.
+ *
+ * Various modules provide fields that themselves contain other fields (e.g.,
+ * Field Group, Paragraphs, etc.) This hook allows those modules to provide the
+ * logic necessary to determine which fields are contained within such a field.
+ *
+ * @param string $entity_type
+ *   Name of the entity type being configured.
+ * @param string $bundle_name
+ *   Name of the entity bundle being configured.
+ *
+ * @return array List of Arrays, themselves listing children.
+ *   Keys are parent fields, values are llists of children.
+ *
+ * @see DependencyHelper::getInheritingFieldNames().
+ * @see hook_conditional_fields_children_alter().
+ * @see field_group_conditional_fields_children().
+ */
+function hook_conditional_fields_children($entity_type, $bundle_name) {
+  $groups = [];
+  $group_info = field_group_info_groups($entity_type, $bundle_name, 'form', 'default');
+  foreach ($group_info as $name => $info) {
+    $groups[$name] = $info->children;
+  }
+  return $groups;
+}
+
+/**
+ * Alter the list of fields contained within a given field.
+ *
+ * @param string &$fields
+ *   Fields provided by hook_conditional_fields_children().
+ * @param string $entity_type
+ *   Name of the entity type being configured.
+ * @param string $bundle_name
+ *   Name of the entity bundle being configured.
+ * @param string $field
+ *   Name of the parent field to check for children fields.
+ *
+ * @see DependencyHelper::getInheritingFieldNames().
+ * @see hook_conditional_fields_children().
+ */
+function hook_conditional_fields_children_alter(&$fields, $entity_type, $bundle_name, $field) {
+  // Do something with the child fields.
+}
+
+/**
+ * @} End of "addtogroup hooks".
+ */
diff --git a/conditional_fields.module b/conditional_fields.module
index 1ffda49..9532f26 100644
--- a/conditional_fields.module
+++ b/conditional_fields.module
@@ -12,6 +12,7 @@ use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Field\WidgetInterface;
 use Drupal\Core\Url;
 use Drupal\conditional_fields\ConditionalFieldsFormHelper;
+use Drupal\conditional_fields\DependencyHelper;
 
 /**
  * Implements hook_help().
@@ -73,6 +74,28 @@ function conditional_fields_element_info_alter(array &$types) {
   }
 }
 
+/**
+ * Implements hook_conditional_fields().
+ *
+ * We implement this on behalf of the core Field module.
+ */
+function conditional_fields_conditional_fields($entity_type, $bundle_name) {
+  $fields = [];
+  $instances = \Drupal::getContainer()->get("entity_field.manager")
+    ->getFieldDefinitions($entity_type, $bundle_name);
+  foreach ($instances as $field) {
+    $fields[$field->getName()] = $field->getLabel();
+  }
+  return $fields;
+}
+
+/**
+ * Implements hook_conditional_fields_alter().
+ */
+function conditional_fields_conditional_fields_alter(&$fields, $entity_type, $bundle_name) {
+  asort($fields);
+}
+
 /**
  * Processes form elements with dependencies.
  *
@@ -151,56 +174,14 @@ function conditional_fields_element_after_build($element, FormStateInterface &$f
 }
 
 /**
- * Loads all dependencies from the database.
- *
- * The result can be filtered by providing an entity type and a bundle name.
+ * Loads all dependencies from the database for a given bundle.
  */
 function conditional_fields_load_dependencies($entity_type, $bundle) {
-  // Use the advanced drupal_static() pattern.
-  static $dependencies;
-
-  if (!isset($dependencies[$entity_type][$bundle])) {
-    if (!empty($entity_type) && !empty($bundle)) {
-      $dependencies[$entity_type][$bundle] = [];
-    }
-
-    /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $entity */
-    $entity = \Drupal::entityTypeManager()
-      ->getStorage('entity_form_display')
-      ->load($entity_type . '.' . $bundle . '.default');
-
-    if (!$entity) {
-      return $dependencies;
-    }
-
-    $fields = $entity->getComponents();
-    foreach ($fields as $field_name => $field) {
-      if (empty($field['third_party_settings']['conditional_fields'])) {
-        continue;
-      }
-      foreach ($field['third_party_settings']['conditional_fields'] as $uuid => $conditional_field) {
-        $dependencies[$entity_type][$bundle]['dependents'][$field_name][$uuid] = [
-          'dependee' => $conditional_field['dependee'],
-          'options' => $conditional_field['settings'],
-        ];
-
-        $dependencies[$entity_type][$bundle]['dependees'][$conditional_field['dependee']][$uuid] = [
-          'dependent' => $field_name,
-          'options' => $conditional_field['settings'],
-        ];
-      }
-    }
+  static $dependency_helper;
+  if (!isset($dependency_helper)) {
+    $dependency_helper = new DependencyHelper($entity_type, $bundle);
   }
-
-  if ($entity_type && isset($dependencies[$entity_type])) {
-    if ($bundle && isset($dependencies[$entity_type][$bundle])) {
-      return $dependencies[$entity_type][$bundle];
-    }
-
-    return $dependencies[$entity_type];
-  }
-
-  return $dependencies;
+  return $dependency_helper->getBundleDependencies();
 }
 
 /**
diff --git a/config/schema/conditional_fields.schema.yml b/config/schema/conditional_fields.schema.yml
index 1832878..df5a334 100644
--- a/config/schema/conditional_fields.schema.yml
+++ b/config/schema/conditional_fields.schema.yml
@@ -56,3 +56,16 @@ conditional_fields.settings:
     selector:
       type: string
       label: 'Custom jQuery selector'
+    inheritance:
+      type: mapping
+      label: 'Inheritance'
+      mapping:
+        propagate:
+          type: string
+          label: 'Propagate settings to fields contained within this one.'
+        apply_to_parent:
+          type: string
+          label: 'Apply these settings to the this (parent) field also.'
+        recurse:
+          type: string
+          label: 'Apply these settings to group fields contained within this one.'
diff --git a/src/ConditionalFieldsFormHelper.php b/src/ConditionalFieldsFormHelper.php
index f284ed8..c1d9fe9 100644
--- a/src/ConditionalFieldsFormHelper.php
+++ b/src/ConditionalFieldsFormHelper.php
@@ -150,6 +150,7 @@ class ConditionalFieldsFormHelper {
         'effects' => $this->effects,
       ];
     }
+
     return $this;
   }
 
diff --git a/src/DependencyHelper.php b/src/DependencyHelper.php
new file mode 100644
index 0000000..789f971
--- /dev/null
+++ b/src/DependencyHelper.php
@@ -0,0 +1,277 @@
+<?php
+
+namespace Drupal\conditional_fields;
+
+/**
+ * Resolve conditional field's dependencies.
+ */
+class DependencyHelper {
+
+  /* The current entity type. */
+  protected $entity_type;
+
+  /* The current bundle name. */
+  protected $bundle;
+
+  /* Name of the current dependent field. */
+  protected $dependent;
+
+  /* The current dependent field. */
+  protected $dependent_field;
+
+  /* Full list of dependencies. */
+  protected $dependencies;
+
+  /* Dependent field definitions. */
+  protected $dependent_fields;
+
+  /* UUID of the current dependency. */
+  protected $uuid;
+
+  /* Name of the current dependee field. */
+  protected $dependee;
+
+  /* Options that define the current dependency. */
+  protected $settings;
+
+  /* Fields attached to a bundle's default form display. */
+  protected $form_fields;
+
+  /* Fields that support inheritance. */
+  protected $inheriting_fields;
+
+  /**
+   * Constructor method.
+   *
+   * @param string $entity_type;
+   *   An entity type name.
+   * @param string $bundle;
+   *   A bundle name.
+   */
+  public function __construct(string $entity_type, string $bundle) {
+    $this->entity_type = $entity_type;
+    $this->bundle = $bundle;
+    $this->module_handler = \Drupal::moduleHandler();
+  }
+
+  /**
+   * Return a list of names of available fields.
+   */
+  public function getAvailableConditionalFields() {
+    $hook = 'conditional_fields';
+    $fields = $this->module_handler
+      ->invokeAll($hook, [$this->entity_type, $this->bundle]);
+    $this->module_handler
+      ->alter($hook,$fields, $this->entity_type, $this->bundle);
+    return $fields;
+  }
+
+  /**
+   * Return dependencies for a given bundle.
+   */
+  public function getBundleDependencies() {
+    if (!isset($this->dependencies[$this->entity_type][$this->bundle])) {
+      $this->resolveBundleDependencies($this->getBundleDependentFields());
+    }
+    return $this->dependencies[$this->entity_type][$this->bundle];
+  }
+
+  /**
+   * Determine whether a field contains other fields.
+   */
+  public function fieldHasChildren($field) {
+    return (bool) count($this->getInheritingFieldNames($field));
+  }
+
+  /**
+   * Resolve a bundle's dependencies.
+   */
+  protected function resolveBundleDependencies($dependent_fields) {
+    foreach ($dependent_fields as $dependent => $field) {
+      $this->dependent = $dependent;
+      $this->dependent_field = $field;
+      $this->resolveFieldDependencies();
+    }
+  }
+
+  /**
+   * Resolve a field's dependencies.
+   */
+  protected function resolveFieldDependencies() {
+    foreach ($this->dependent_field['third_party_settings']['conditional_fields'] as $uuid => $conditional_field) {
+      $this->uuid = $uuid;
+      $this->dependee = $conditional_field['dependee'];
+      $this->settings = $conditional_field['settings'];
+      if ($this->fieldDependencyShouldPropagate()) {
+        if ($this->fieldDependencyShouldApplyToParent()) {
+          $this->registerFieldDependency();
+        }
+        $this->resolveBundleDependencies($this->getInheritingFields());
+        continue;
+      }
+      $this->registerFieldDependency();
+    }
+  }
+
+  /**
+   * Determine whether a field dependency should be inherited.
+   */
+  protected function fieldDependencyShouldPropagate() {
+    if (!isset($this->settings['inheritance']['propagate'])) return FALSE;
+    return (bool) $this->settings['inheritance']['propagate'];
+  }
+
+  /**
+   * Determine whether a field dependency should be apply to the parent field.
+   */
+  protected function fieldDependencyShouldApplyToParent() {
+    if (!isset($this->settings['inheritance']['apply_to_parent'])) return FALSE;
+    return (bool) $this->settings['inheritance']['apply_to_parent'];
+  }
+
+  /**
+   * Determine whether a field dependency should be apply to the parent field.
+   */
+  protected function fieldDependencyShouldRecurse() {
+    if (!isset($this->settings['inheritance']['recurse'])) return FALSE;
+    return (bool) $this->settings['inheritance']['recurse'];
+  }
+
+  /**
+   * Return fields with conditional settings to inherit.
+   */
+  protected function getInheritingFields() {
+    if (empty($this->dependent_field['third_party_settings']['conditional_fields'][$this->uuid])) {
+      return [];
+    }
+
+    $propagating_settings = $this->dependent_field['third_party_settings']['conditional_fields'][$this->uuid];
+    $inheriting_fields = [];
+    foreach ($this->getInheritingFieldNames($this->dependent) as $field_name) {
+      $inheriting_field = $this->getBundleFormField($field_name);
+      $new_id = "{$this->uuid}+{$field_name}";
+      $inheriting_field['third_party_settings']['conditional_fields'][$new_id] = $propagating_settings;
+      if (!$this->fieldHasChildren($field_name) || !$this->fieldDependencyShouldRecurse()) {
+        unset($inheriting_field['third_party_settings']['conditional_fields'][$new_id]['settings']['inheritance']);
+      }
+      $inheriting_fields[$field_name] = $inheriting_field;
+    }
+    return $inheriting_fields;
+  }
+
+  /**
+   * Return a list of fields to inherit conditional settings.
+   */
+  protected function getInheritingFieldNames($parent_field) {
+    if (!isset($this->inheriting_fields)) {
+      $this->inheriting_fields = $this->getInheritingChildren();
+    }
+    if (!isset($this->inheriting_fields[$parent_field])) return [];
+    return $this->inheriting_fields[$parent_field];
+  }
+
+  /**
+   * Determine all fields that support inheritence, and their children.
+   */
+  protected function getInheritingChildren() {
+    $hook = 'conditional_fields_children';
+    $inheriting_fields = $this->module_handler
+      ->invokeAll($hook, [$this->entity_type, $this->bundle]);
+    $this->module_handler
+      ->alter($hook, $inheriting_fields, $this->entity_type, $this->bundle);
+    return $inheriting_fields;
+  }
+
+  /**
+   * Register a specific conditional field dependency.
+   */
+  protected function registerFieldDependency() {
+    $this->registerDependent();
+    $this->registerDependee();
+  }
+
+  /**
+   * Add a dependent field to the list of dependencies.
+   */
+  protected function registerDependent() {
+    $this->dependencies[$this->entity_type][$this->bundle]['dependents'][$this->dependent][$this->uuid] = [
+      'dependee' => $this->dependee,
+      'options' => $this->settings,
+    ];
+  }
+
+  /**
+   * Add a dependee field to the list of dependencies.
+   */
+  protected function registerDependee() {
+    $this->dependencies[$this->entity_type][$this->bundle]['dependees'][$this->dependee][$this->uuid] = [
+      'dependent' => $this->dependent,
+      'options' => $this->settings,
+    ];
+  }
+
+  /**
+   * Return all dependent fields attached to a bundle.
+   */
+  protected function getBundleDependentFields() {
+    if (!$this->bundleHasRegisteredDependentFields()) {
+      $this->registerBundleDependentFields();
+    }
+    return $this->dependent_fields[$this->entity_type][$this->bundle];
+  }
+
+  /**
+   * Determine whether a bundle has registered any dependent fields.
+   */
+  protected function bundleHasRegisteredDependentFields() {
+    if (!isset($this->dependent_fields[$this->entity_type][$this->bundle])) return FALSE;
+    if (empty($this->dependent_fields[$this->entity_type][$this->bundle])) return FALSE;
+    return TRUE;
+  }
+
+  /**
+   * Register all dependent fields attached to a bundle.
+   */
+  protected function registerBundleDependentFields() {
+    $this->dependent_fields[$this->entity_type][$this->bundle] = [];
+    foreach ($this->getBundleFormFields() as $name => $field) {
+      if (!$this->hasConditionalFields($field)) continue;
+      $this->dependent_fields[$this->entity_type][$this->bundle][$name] = $field;
+    }
+  }
+
+  /**
+   * Return a field attached to a bundle.
+   */
+  protected function getBundleFormField($field_name) {
+    if (!isset($this->form_fields)) {
+      $this->form_fields = $this->getBundleFormFields();
+    }
+    if (!isset($this->form_fields[$field_name])) return [];
+    return $this->form_fields[$field_name];
+  }
+
+  /**
+   * Return all fields attached to a bundle.
+   */
+  protected function getBundleFormFields() {
+    /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $entity */
+    $entity = \Drupal::entityTypeManager()
+      ->getStorage('entity_form_display')
+      ->load($this->entity_type . '.' . $this->bundle . '.default');
+
+    if (!$entity) {
+      return [];
+    }
+
+    return $entity->getComponents();
+  }
+
+  /**
+   * Determine whether a field has any conditional fields defined.
+   */
+  protected function hasConditionalFields($field) {
+    return !empty($field['third_party_settings']['conditional_fields']);
+  }
+
+}
diff --git a/src/Form/ConditionalFieldEditForm.php b/src/Form/ConditionalFieldEditForm.php
index f27874b..433b69c 100644
--- a/src/Form/ConditionalFieldEditForm.php
+++ b/src/Form/ConditionalFieldEditForm.php
@@ -2,19 +2,20 @@
 
 namespace Drupal\conditional_fields\Form;
 
-use Drupal\Core\Datetime\DrupalDateTime;
-use Drupal\Core\Entity\Entity\EntityFormDisplay;
-use Drupal\Core\Form\FormBase;
-use Drupal\Core\Form\FormState;
-use Drupal\Core\Form\FormStateInterface;
 use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
 use Drupal\conditional_fields\ConditionalFieldsInterface;
-use Drupal\Core\Render\Element;
 use Drupal\conditional_fields\Conditions;
+use Drupal\conditional_fields\DependencyHelper;
+use Drupal\Core\Datetime\DrupalDateTime;
+use Drupal\Core\Entity\Entity\EntityFormDisplay;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormBuilderInterface;
+use Drupal\Core\Form\FormState;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Render\Element;
 use Drupal\datetime\Plugin\Field\FieldType\DateTimeItemInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
-use Drupal\Core\Entity\EntityTypeManagerInterface;
-use Drupal\Core\Form\FormBuilderInterface;
 
 /**
  * Class ConditionalFieldEditForm.
@@ -251,6 +252,27 @@ class ConditionalFieldEditForm extends FormBase {
       '#required' => TRUE,
     ];
 
+    if ($this->fieldSupportsInheritance($entity_type, $bundle, $field_name)) {
+      $form['inheritance'] = [
+        '#type' => 'checkboxes',
+        '#title' => $this->t('Inheritance'),
+        '#description' => $this->t('This element contains other fields. Apply the settings in this form to the contained fields instead of this one.'),
+        '#options' => [
+          'propagate' => $this->t('Propagate settings to fields contained within this one.'),
+          'apply_to_parent' => $this->t('Apply these settings to the this (parent) field also. Requires the "Propagate" setting, above.'),
+          'recurse' => $this->t('Apply these settings to group fields contained within this one. Requires the "Propagate" setting, above.'),
+        ],
+        '#default_value' => array_key_exists('inheritance', $settings) ? $settings['inheritance'] : [],
+      ];
+      $form['inheritance']['apply_to_parent'] = [
+        '#states' => [
+          'disabled' => [
+            ':input[name="inheritance[propagate]"]' => ['checked' => FALSE],
+          ],
+        ],
+      ];
+      $form['inheritance']['recurse']['#states'] = $form['inheritance']['apply_to_parent']['#states'];
+    }
     $form['entity_edit'] = [
       '#type' => 'details',
       '#title' => $this->t('Edit context settings'),
@@ -561,4 +583,12 @@ class ConditionalFieldEditForm extends FormBase {
     }
   }
 
+  /**
+   * Determine whether a field supports inheritance.
+   */
+  protected function fieldSupportsInheritance($entity_type, $bundle, $field_name) {
+    $dependency_helper = new DependencyHelper($entity_type, $bundle);
+    return $dependency_helper->fieldHasChildren($field_name);
+  }
+
 }
diff --git a/src/Form/ConditionalFieldForm.php b/src/Form/ConditionalFieldForm.php
index ae3c334..2232e3a 100644
--- a/src/Form/ConditionalFieldForm.php
+++ b/src/Form/ConditionalFieldForm.php
@@ -3,13 +3,16 @@
 namespace Drupal\conditional_fields\Form;
 
 use Drupal\Component\Uuid\UuidInterface;
+use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Url;
 use Drupal\conditional_fields\Conditions;
+use Drupal\conditional_fields\DependencyHelper;
 use Symfony\Component\DependencyInjection\ContainerInterface;
-use Drupal\Core\Entity\EntityFieldManagerInterface;
-use Drupal\Core\Entity\EntityTypeManagerInterface;
 
 /**
  * Class ConditionalFieldForm.
@@ -50,6 +53,26 @@ class ConditionalFieldForm extends FormBase {
    */
   protected $list;
 
+  /**
+   * Form array.
+   */
+  protected $form;
+
+  /**
+   * FormState object.
+   */
+  protected $form_state;
+
+  /**
+   * Name of the entity type being configured.
+   */
+  protected $entity_type;
+
+  /**
+   * Name of the entity bundle being configured.
+   */
+  protected $bundle_name;
+
   /**
    * Class constructor.
    *
@@ -62,7 +85,12 @@ class ConditionalFieldForm extends FormBase {
    * @param \Drupal\Component\Uuid\UuidInterface $uuid
    *   Uuid generator.
    */
-  public function __construct(Conditions $list, EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager, UuidInterface $uuid) {
+  public function __construct(
+    Conditions $list,
+    EntityFieldManagerInterface $entity_field_manager,
+    EntityTypeManagerInterface $entity_type_manager,
+    UuidInterface $uuid
+  ) {
     $this->entityFieldManager = $entity_field_manager;
     $this->entityTypeManager = $entity_type_manager;
     $this->list = $list;
@@ -94,20 +122,24 @@ class ConditionalFieldForm extends FormBase {
    * {@inheritdoc}
    */
   public function buildForm(array $form, FormStateInterface $form_state, $entity_type = NULL, $bundle = NULL) {
+    $this->form = $form;
+    $this->form_state = $form_state;
+    $this->entity_type = $entity_type;
+    $this->bundle_name = $bundle;
 
-    $form['entity_type'] = [
+    $this->form['entity_type'] = [
       '#type' => 'hidden',
-      '#value' => $entity_type,
+      '#value' => $this->entity_type,
     ];
 
-    $form['bundle'] = [
+    $this->form['bundle'] = [
       '#type' => 'hidden',
-      '#value' => $bundle,
+      '#value' => $this->bundle_name,
     ];
 
-    $form['conditional_fields_wrapper']['table'] = $this->buildTable($form, $form_state, $entity_type, $bundle);
+    $this->buildTable();
 
-    return $form;
+    return $this->form;
   }
 
   /**
@@ -119,39 +151,52 @@ class ConditionalFieldForm extends FormBase {
       parent::validateForm($form, $form_state);
     }
     $conditional_values = $table['add_new_dependency'];
-    // Check dependency.
+
     if (array_key_exists('dependee', $conditional_values) &&
       array_key_exists('dependent', $conditional_values)
     ) {
       $dependent = $conditional_values['dependent'];
       $state = isset($conditional_values['state']) ? $conditional_values['state'] : NULL;
-      $all_states = $this->list->conditionalFieldsStates();
-      $entity_type = $form_state->getValue('entity_type');
-      $bundle = $form_state->getValue('bundle');
       $instances = $this->entityFieldManager
-        ->getFieldDefinitions($entity_type, $bundle);
+        ->getFieldDefinitions($form_state->getValue('entity_type'), $form_state->getValue('bundle'));
+
       foreach ($dependent as $field) {
         if ($conditional_values['dependee'] == $field) {
           $form_state->setErrorByName('dependee', $this->t('You should select two different fields.'));
           $form_state->setErrorByName('dependent', $this->t('You should select two different fields.'));
         }
-        // Validate required field should be visible.
-        $field_instance = $instances[$field];
-        if (
-          $field_instance->isRequired() &&
-          in_array($state, ['!visible', 'disabled', '!required'])
-        ) {
+
+        if (!empty($instances[$field]) && $this->requiredFieldIsNotVisible($instances[$field], $state)) {
+          $field_instance = $instances[$field];
           $form_state->setErrorByName('state', $this->t('Field %field is required and can not have state %state.', [
             '%field' => $field_instance->getLabel() . ' (' . $field_instance->getName() . ')',
-            '%state' => $all_states[$state],
+            '%state' => $this->list->conditionalFieldsStates()[$state],
           ]));
         }
       }
     }
-
     parent::validateForm($form, $form_state);
   }
 
+  /**
+   * Determine if a field is configured to be required, but not visible.
+   *
+   * This is considered an error condition as a user would not be able to fill
+   * out the field.
+   *
+   * @param \Drupal\Core\Field\FieldDefinitionInterface $field
+   *   The field to evaluate.
+   * @param null|string $state
+   *   The configured state for the field.
+   *
+   * @return bool
+   *   TRUE if the field is required but not visible; FALSE otherwise.
+   */  protected function requiredFieldIsNotVisible(FieldDefinitionInterface $field, $state): bool {
+    return method_exists($field, 'isRequired') &&
+      $field->isRequired() &&
+      in_array($state, ['!visible', 'disabled', '!required']);
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -227,11 +272,11 @@ class ConditionalFieldForm extends FormBase {
   /**
    * Builds table with conditional fields.
    */
-  protected function buildTable(array $form, FormStateInterface $form_state, $entity_type, $bundle_name = NULL) {
-    $form['table'] = [
+  protected function buildTable() {
+    $table = [
       '#type' => 'table',
-      '#entity_type' => $entity_type,
-      '#bundle_name' => $bundle_name,
+      '#entity_type' => $this->entity_type,
+      '#bundle_name' => $this->bundle_name,
       '#header' => [
         $this->t('Target field'),
         $this->t('Controlled by'),
@@ -240,22 +285,14 @@ class ConditionalFieldForm extends FormBase {
       ],
     ];
 
-    // Build list of available fields.
-    $fields = [];
-    $instances = $this->entityFieldManager
-      ->getFieldDefinitions($entity_type, $bundle_name);
-    foreach ($instances as $field) {
-      $fields[$field->getName()] = $field->getLabel() . ' (' . $field->getName() . ')';
-    }
-
-    asort($fields);
+    $fields = $this->getFieldsList();
 
     /* Existing conditions. */
 
     /** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display_entity */
     $form_display_entity = $this->entityTypeManager
       ->getStorage('entity_form_display')
-      ->load("$entity_type.$bundle_name.default");
+      ->load("$this->entity_type.$this->bundle_name.default");
 
     if (empty($form_display_entity) && $entity_type == 'taxonomy_term') {
       $form_display_entity = $this->entityTypeManager->getStorage('entity_form_display')->create([
@@ -267,7 +304,8 @@ class ConditionalFieldForm extends FormBase {
     }
 
     if (!$form_display_entity) {
-      return $form['table'];
+      $this->form['conditional_fields_wrapper']['table'] = $table;
+      return;
     }
 
     foreach ($fields as $field_name => $label) {
@@ -284,7 +322,7 @@ class ConditionalFieldForm extends FormBase {
           'uuid' => $uuid,
         ];
 
-        $form['table'][] = [
+        $table[] = [
           'dependent' => ['#markup' => $field_name],
           'dependee' => ['#markup' => $condition['dependee']],
           'state' => ['#markup' => $condition['settings']['state']],
@@ -319,7 +357,7 @@ class ConditionalFieldForm extends FormBase {
     }
 
     // Add new dependency row.
-    $form['table']['add_new_dependency'] = [
+    $table['add_new_dependency'] = [
       'dependent' => [
         '#type' => 'select',
         '#multiple' => TRUE,
@@ -368,9 +406,21 @@ class ConditionalFieldForm extends FormBase {
       ],
     ];
 
-    $form['table']['#attached']['library'][] = 'conditional_fields/admin';
+    $table['#attached']['library'][] = 'conditional_fields/admin';
 
-    return $form['table'];
+    $this->form['conditional_fields_wrapper']['table'] = $table;
+  }
+
+  /**
+   * Build options list of available fields.
+   */
+  protected function getFieldsList() {
+    $dependency_helper = new DependencyHelper($this->entity_type, $this->bundle_name);
+    $fields = [];
+    foreach ($dependency_helper->getAvailableConditionalFields() as $name => $label) {
+      $fields[$name] = $label . ' (' . $name . ')';
+    }
+    return $fields;
   }
 
 }
-- 
2.25.1

