You are here

class DynamicEntityReferenceItem in Dynamic Entity Reference 8

Same name and namespace in other branches
  1. 8.2 src/Plugin/Field/FieldType/DynamicEntityReferenceItem.php \Drupal\dynamic_entity_reference\Plugin\Field\FieldType\DynamicEntityReferenceItem

Defines the 'dynamic_entity_reference' entity field type.

Supported settings (below the definition's 'settings' key) are:

  • exclude_entity_types: Allow user to include or exclude entity_types.
  • entity_type_ids: The entity type ids that can or cannot be referenced.

@property int target_id @property string target_type @property \Drupal\Core\Entity\ContentEntityInterface entity

Plugin annotation


@FieldType(
  id = "dynamic_entity_reference",
  label = @Translation("Dynamic entity reference"),
  description = @Translation("An entity field containing a dynamic entity reference."),
  category = @Translation("Dynamic Reference"),
  no_ui = FALSE,
  list_class = "\Drupal\dynamic_entity_reference\Plugin\Field\FieldType\DynamicEntityReferenceFieldItemList",
  default_widget = "dynamic_entity_reference_default",
  default_formatter = "dynamic_entity_reference_label"
)

Hierarchy

Expanded class hierarchy of DynamicEntityReferenceItem

8 files declare their use of DynamicEntityReferenceItem
DynamicEntityReferenceEntityFormatter.php in src/Plugin/Field/FieldFormatter/DynamicEntityReferenceEntityFormatter.php
DynamicEntityReferenceItemNormalizer.php in src/Normalizer/DynamicEntityReferenceItemNormalizer.php
DynamicEntityReferenceOptionsTrait.php in src/Plugin/Field/FieldWidget/DynamicEntityReferenceOptionsTrait.php
DynamicEntityReferenceTest.php in tests/src/Functional/DynamicEntityReferenceTest.php
DynamicEntityReferenceWidget.php in src/Plugin/Field/FieldWidget/DynamicEntityReferenceWidget.php

... See full list

File

src/Plugin/Field/FieldType/DynamicEntityReferenceItem.php, line 41

Namespace

Drupal\dynamic_entity_reference\Plugin\Field\FieldType
View source
class DynamicEntityReferenceItem extends EntityReferenceItem {

  /**
   * {@inheritdoc}
   */
  public static function defaultStorageSettings() {
    return [
      'exclude_entity_types' => TRUE,
      'entity_type_ids' => [],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public static function defaultFieldSettings() {
    $default_settings = [];
    $labels = \Drupal::service('entity_type.repository')
      ->getEntityTypeLabels(TRUE);
    $options = $labels[(string) t('Content', [], [
      'context' => 'Entity type group',
    ])];

    // Field storage settings are not accessible here so we are assuming that
    // all the entity types are referenceable by default.
    // See https://www.drupal.org/node/2346273#comment-9385179 for more details.
    foreach (array_keys($options) as $entity_type_id) {
      $default_settings[$entity_type_id] = [
        'handler' => "default:{$entity_type_id}",
        'handler_settings' => [],
      ];
    }
    return $default_settings;
  }

  /**
   * {@inheritdoc}
   */
  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
    $properties['target_id'] = DataReferenceTargetDefinition::create('integer')
      ->setLabel(t('Entity ID'))
      ->setSetting('unsigned', TRUE)
      ->setRequired(TRUE);
    $properties['target_type'] = DataReferenceTargetDefinition::create('string')
      ->setLabel(new TranslatableMarkup('Target Entity Type'))
      ->setRequired(TRUE);
    $properties['entity'] = DataDynamicReferenceDefinition::create('entity')
      ->setLabel(t('Entity'))
      ->setDescription(new TranslatableMarkup('The referenced entity'))
      ->setComputed(TRUE)
      ->setReadOnly(FALSE);
    return $properties;
  }

  /**
   * {@inheritdoc}
   */
  public static function schema(FieldStorageDefinitionInterface $field_definition) {
    $columns = [
      'target_id' => [
        'description' => 'The ID of the target entity.',
        'type' => 'int',
        'unsigned' => TRUE,
      ],
      'target_type' => [
        'description' => 'The Entity Type ID of the target entity.',
        'type' => 'varchar',
        'length' => EntityTypeInterface::BUNDLE_MAX_LENGTH,
      ],
    ];
    $schema = [
      'columns' => $columns,
      'indexes' => [
        'target_id' => [
          'target_id',
          'target_type',
        ],
      ],
    ];
    return $schema;
  }

  /**
   * {@inheritdoc}
   */
  public function onChange($property_name, $notify = TRUE) {

    /** @var \Drupal\dynamic_entity_reference\Plugin\DataType\DynamicEntityReference $entity_property */
    $entity_property = $this
      ->get('entity');
    if ($property_name == 'target_type' && !$entity_property
      ->getValue()) {
      $entity_property
        ->getTargetDefinition()
        ->setEntityTypeId($this
        ->get('target_type')
        ->getValue());
    }
    elseif ($property_name == 'entity') {
      $this
        ->writePropertyValue('target_type', $entity_property
        ->getValue()
        ->getEntityTypeId());
    }
    elseif ($property_name == 'target_id') {

      // Just in case target_id is set before target_type then set it to empty
      // string instead of NULL so that
      // \Drupal\Core\Entity\Plugin\DataType\EntityReference::setValue
      // doesn't throw "InvalidArgumentException: Value is not a valid entity".
      $entity_property
        ->getTargetDefinition()
        ->setEntityTypeId($this
        ->get('target_type')
        ->getValue() ?: '');
    }
    parent::onChange($property_name, $notify);
  }

  /**
   * {@inheritdoc}
   *
   * To select both target_type and target_id the option value is
   * changed from target_id to target_type-target_id.
   *
   * @see \Drupal\dynamic_entity_reference\Plugin\Field\FieldWidget\DynamicEntityReferenceOptionsTrait::massageFormValues()
   */
  public function getSettableOptions(AccountInterface $account = NULL) {
    $field_definition = $this
      ->getFieldDefinition();
    $entity_type_manager = \Drupal::entityTypeManager();
    $entity_type_bundles_info = \Drupal::service('entity_type.bundle.info');
    $selection_manager = \Drupal::service('plugin.manager.dynamic_entity_reference_selection');
    $options = [];
    $settings = $this
      ->getSettings();
    $target_types = static::getTargetTypes($settings);
    foreach ($target_types as $target_type) {
      $options[$target_type] = $selection_manager
        ->getSelectionHandler($field_definition, $this
        ->getEntity(), $target_type)
        ->getReferenceableEntities();
    }
    if (empty($options)) {
      return [];
    }
    $return = [];
    foreach ($options as $target_type => $referenceable_entities) {
      $target_type_info = $entity_type_manager
        ->getDefinition($target_type);
      $target_type_label = $target_type_info
        ->getLabel();

      // Rebuild the array by changing the bundle key into the bundle label.
      $bundles = $entity_type_bundles_info
        ->getBundleInfo($target_type);
      foreach ($referenceable_entities as $bundle => $entities) {

        // The label does not need sanitizing since it is used as an optgroup
        // which is only supported by select elements and auto-escaped.
        $bundle_label = $bundles[$bundle]['label'];
        foreach ($entities as $id => $entity_label) {
          if (count($target_types) > 1) {
            if ($target_type_info
              ->hasKey('bundle')) {
              $return[(string) $target_type_label . ': ' . (string) $bundle_label]["{$target_type}-{$id}"] = "{$entity_label} ({$target_type_label}:{$id})";
            }
            else {
              $return[(string) $target_type_label]["{$target_type}-{$id}"] = "{$entity_label} ({$target_type_label}:{$id})";
            }
          }
          else {
            $return[(string) $bundle_label]["{$target_type}-{$id}"] = "{$entity_label} ({$target_type_label}:{$id})";
          }
        }
      }
    }
    return $return;
  }

  /**
   * {@inheritdoc}
   */
  public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) {

    // @todo inject this.
    $labels = \Drupal::service('entity_type.repository')
      ->getEntityTypeLabels(TRUE);
    $options = $labels[(string) t('Content', [], [
      'context' => 'Entity type group',
    ])];
    foreach (array_keys($options) as $entity_type_id) {
      if (!static::entityHasIntegerId($entity_type_id)) {
        unset($options[$entity_type_id]);
      }
    }
    $element['exclude_entity_types'] = [
      '#type' => 'checkbox',
      '#title' => t('Exclude the selected items'),
      '#default_value' => $this
        ->getSetting('exclude_entity_types'),
      '#disabled' => $has_data,
    ];
    $element['entity_type_ids'] = [
      '#type' => 'select',
      '#title' => t('Select items'),
      '#options' => $options,
      '#default_value' => $this
        ->getSetting('entity_type_ids'),
      '#disabled' => $has_data,
      '#multiple' => TRUE,
      '#element_validate' => [
        [
          DynamicEntityReferenceItem::class,
          'storageSettingsFormValidate',
        ],
      ],
    ];
    return $element;
  }

  /**
   * Form element validation for storage settings.
   *
   * @param array $element
   *   The form element .
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state.
   * @param array $form
   *   The complete form.
   */
  public static function storageSettingsFormValidate(array &$element, FormStateInterface $form_state, array $form) {
    $labels = \Drupal::service('entity_type.repository')
      ->getEntityTypeLabels(TRUE);
    $options = array_filter(array_keys($labels[(string) t('Content', [], [
      'context' => 'Entity type group',
    ])]), function ($entity_type_id) {
      return static::entityHasIntegerId($entity_type_id);
    });
    $exclude_entity_types = $form_state
      ->getValue([
      'settings',
      'exclude_entity_types',
    ], 0);
    $entity_type_ids = $form_state
      ->getValue([
      'settings',
      'entity_type_ids',
    ], []);
    $diff = array_diff($options, $entity_type_ids);
    if (!$exclude_entity_types && empty($entity_type_ids) || $exclude_entity_types && empty($diff)) {
      $form_state
        ->setError($element, t('Select at least one entity type ID.'));
    }
  }

  /**
   * {@inheritdoc}
   */
  public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
    $settings_form = [];
    $settings = $this
      ->getSettings();
    foreach (static::getTargetTypes($settings) as $target_type) {
      $entity_type = \Drupal::entityTypeManager()
        ->getDefinition($target_type);
      $settings_form[$target_type] = $this
        ->targetTypeFieldSettingsForm($form, $form_state, $target_type);
      $settings_form[$target_type]['handler']['#title'] = t('Reference type for @target_type', [
        '@target_type' => $entity_type
          ->getLabel(),
      ]);
    }
    return $settings_form;
  }

  /**
   * Returns a form for single target type settings.
   *
   * This is same as
   * \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::fieldSettingsForm()
   * but it uses dynamic_entity_reference_selection plugin manager instead of
   * entity_reference_selection plugin manager.
   *
   * @param array $form
   *   The form where the settings form is being included in.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state of the (entire) configuration form.
   * @param string $target_type
   *   The target entity type id.
   *
   * @return array
   *   The form definition for the field settings.
   */
  protected function targetTypeFieldSettingsForm(array $form, FormStateInterface $form_state, $target_type) {

    /** @var \Drupal\field\FieldConfigInterface $field */
    $field = $form_state
      ->getFormObject()
      ->getEntity();
    $field_settings = $field
      ->getSettings();

    /** @var \Drupal\dynamic_entity_reference\SelectionPluginManager $manager */
    $manager = \Drupal::service('plugin.manager.dynamic_entity_reference_selection');

    // Get all selection plugins for this entity type.
    $selection_plugins = $manager
      ->getSelectionGroups($target_type);
    $handlers_options = [];
    foreach (array_keys($selection_plugins) as $selection_group_id) {

      // We only display base plugins (e.g. 'default', 'views', ...) and not
      // entity type specific plugins (e.g. 'default:node', 'default:user',
      // ...).
      if (array_key_exists($selection_group_id, $selection_plugins[$selection_group_id])) {
        $handlers_options[$selection_group_id] = Html::escape($selection_plugins[$selection_group_id][$selection_group_id]['label']);
      }
      elseif (array_key_exists($selection_group_id . ':' . $target_type, $selection_plugins[$selection_group_id])) {
        $selection_group_plugin = $selection_group_id . ':' . $target_type;
        $handlers_options[$selection_group_plugin] = Html::escape($selection_plugins[$selection_group_id][$selection_group_plugin]['base_plugin_label']);
      }
    }
    $form = [
      '#type' => 'container',
      '#process' => [
        [
          EntityReferenceItem::class,
          'fieldSettingsAjaxProcess',
        ],
      ],
      '#element_validate' => [
        [
          DynamicEntityReferenceItem::class,
          'fieldSettingsFormValidate',
        ],
      ],
    ];
    $form['handler'] = [
      '#type' => 'details',
      '#title' => t('Reference type'),
      '#open' => TRUE,
      '#tree' => TRUE,
      '#process' => [
        [
          EntityReferenceItem::class,
          'formProcessMergeParent',
        ],
      ],
    ];
    $form['handler']['handler'] = [
      '#type' => 'select',
      '#title' => t('Reference method'),
      '#options' => $handlers_options,
      '#default_value' => $field_settings[$target_type]['handler'],
      '#required' => TRUE,
      '#ajax' => TRUE,
      '#limit_validation_errors' => [],
    ];
    $form['handler']['handler_submit'] = [
      '#type' => 'submit',
      '#value' => t('Change handler'),
      '#limit_validation_errors' => [],
      '#attributes' => [
        'class' => [
          'js-hide',
        ],
      ],
      '#submit' => [
        'entity_reference_settings_ajax_submit',
      ],
    ];
    $form['handler']['handler_settings'] = [
      '#type' => 'container',
      '#attributes' => [
        'class' => [
          'entity_reference-settings',
        ],
      ],
    ];
    $handler = $manager
      ->getSelectionHandler($field, NULL, $target_type);
    $form['handler']['handler_settings'] += $handler
      ->buildConfigurationForm([], $form_state);
    return $form;
  }

  /**
   * Form element validation handler; Stores the new values in the form state.
   *
   * @param array $form
   *   The form where the settings form is being included in.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The form state of the (entire) configuration form.
   */
  public static function fieldSettingsFormValidate(array $form, FormStateInterface $form_state) {

    /** @var \Drupal\field\Entity\FieldConfig $field */
    $field = $form_state
      ->getFormObject()
      ->getEntity();
    foreach (static::getTargetTypes($field
      ->getSettings()) as $target_type) {

      // If no checkboxes were checked for 'target_bundles', store NULL ("all
      // bundles are referenceable") rather than empty array ("no bundle is
      // referenceable" - typically happens when all referenceable bundles have
      // been deleted).
      if ($form_state
        ->getValue([
        'settings',
        $target_type,
        'handler_settings',
        'target_bundles',
      ]) === []) {
        $form_state
          ->setValue([
          'settings',
          $target_type,
          'handler_settings',
          'target_bundles',
        ], NULL);
      }

      // Don't store the 'target_bundles_update' button value into the field
      // config settings.
      $form_state
        ->unsetValue([
        'settings',
        $target_type,
        'handler_settings',
        'target_bundles_update',
      ]);
      $form_state
        ->unsetValue([
        'settings',
        $target_type,
        'handler_submit',
      ]);
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function mainPropertyName() {

    // Dynamic entity reference field has two main properties i.e. target_type
    // and target_id but for entity field query to evaluate the relationship
    // specifier correctly the main property is needed. It is also needed to
    // render the correct field value in views.
    return 'target_id';
  }

  /**
   * {@inheritdoc}
   */
  public function setValue($values, $notify = TRUE) {

    // If either a scalar or an object was passed as the value for the item,
    // assign it to the 'entity' property since that works for both cases.
    if (isset($values) && !is_array($values)) {
      $this
        ->set('entity', $values, $notify);
    }
    else {
      if (empty($values['target_type']) && !empty($values['target_id']) && !(isset($values['entity']) && $values['entity'] instanceof EntityInterface)) {
        throw new \InvalidArgumentException('No entity type was provided, value is not a valid entity.');
      }

      // We have to bypass the EntityReferenceItem::setValue() here because we
      // also want to invoke onChange for target_type.
      FieldItemBase::setValue($values, FALSE);

      // Support setting the field item with only one property, but make sure
      // values stay in sync if only property is passed.
      // NULL is a valid value, so we use array_key_exists().
      if (is_array($values) && array_key_exists('target_id', $values) && array_key_exists('target_type', $values) && !isset($values['entity'])) {
        $this
          ->onChange('target_type', FALSE);
        $this
          ->onChange('target_id', FALSE);
      }
      elseif (is_array($values) && !array_key_exists('target_id', $values) && !array_key_exists('target_type', $values) && isset($values['entity'])) {
        $this
          ->onChange('entity', FALSE);
      }
      elseif (is_array($values) && array_key_exists('target_id', $values) && array_key_exists('target_type', $values) && isset($values['entity'])) {

        /** @var \Drupal\dynamic_entity_reference\Plugin\DataType\DynamicEntityReference $entity_property */
        $entity_property = $this
          ->get('entity');
        $entity_id = $entity_property
          ->getTargetIdentifier();

        /** @var \Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface $target_definition */
        $target_definition = $entity_property
          ->getTargetDefinition();
        $entity_type = $target_definition
          ->getEntityTypeId();
        if (!$this->entity
          ->isNew() && $values['target_id'] !== NULL && ($entity_id !== $values['target_id'] || $entity_type !== $values['target_type'])) {
          throw new \InvalidArgumentException('The target id, target type and entity passed to the dynamic entity reference item do not match.');
        }
      }

      // Notify the parent if necessary.
      if ($notify && $this->parent) {
        $this->parent
          ->onChange($this
          ->getName());
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getValue() {
    $values = parent::getValue();
    if (!empty($values['target_type'])) {
      $this
        ->get('entity')
        ->getTargetDefinition()
        ->setEntityTypeId($values['target_type']);
    }
    return $values;
  }

  /**
   * {@inheritdoc}
   */
  public function isEmpty() {

    // Avoid loading the entity by first checking the 'target_id'.
    if ($this->target_id !== NULL && $this->target_type !== NULL) {
      return FALSE;
    }
    if ($this->entity && $this->entity instanceof EntityInterface) {
      return FALSE;
    }
    return TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public function preSave() {
    if ($this
      ->hasNewEntity()) {

      // Save the entity if it has not already been saved by some other code.
      if ($this->entity
        ->isNew()) {
        $this->entity
          ->save();
      }

      // Make sure the parent knows we are updating this property so it can
      // react properly.
      $this->target_id = $this->entity
        ->id();
      $this->target_type = $this->entity
        ->getEntityTypeId();
    }
    if (!$this
      ->isEmpty() && $this->target_id === NULL) {
      $this->target_id = $this->entity
        ->id();
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function generateSampleValue(FieldDefinitionInterface $field_definition) {

    /** @var \Drupal\dynamic_entity_reference\SelectionPluginManager $manager */
    $manager = \Drupal::service('plugin.manager.dynamic_entity_reference_selection');
    $settings = $field_definition
      ->getSettings();
    foreach (static::getTargetTypes($settings) as $target_type) {
      $values['target_type'] = $target_type;
      if ($referenceable = $manager
        ->getSelectionHandler($field_definition, NULL, $target_type)
        ->getReferenceableEntities()) {
        $group = array_rand($referenceable);
        $values['target_id'] = array_rand($referenceable[$group]);
        return $values;
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public static function calculateDependencies(FieldDefinitionInterface $field_definition) {
    $dependencies = FieldItemBase::calculateDependencies($field_definition);
    $entity_repository = \Drupal::service('entity.repository');
    if ($default_value = $field_definition
      ->getDefaultValueLiteral()) {
      foreach ($default_value as $value) {
        if (is_array($value) && isset($value['target_uuid']) && isset($value['target_type'])) {
          $entity = $entity_repository
            ->loadEntityByUuid($value['target_type'], $value['target_uuid']);

          // If the entity does not exist do not create the dependency.
          // @see \Drupal\dynamic_entity_reference\Plugin\Field\FieldType\DynamicEntityReferenceFieldItemList::processDefaultValue()
          if ($entity) {
            $dependencies[$entity
              ->getEntityType()
              ->getConfigDependencyKey()][] = $entity
              ->getConfigDependencyName();
          }
        }
      }
    }

    // Depend on target bundle configurations. Dependencies for 'target_bundles'
    // also covers the 'auto_create_bundle' setting, if any, because its value
    // is included in the 'target_bundles' list.
    $entity_type_manager = \Drupal::entityTypeManager();
    $settings = $field_definition
      ->getSettings();
    foreach (static::getTargetTypes($settings) as $target_type) {
      $handler = $settings[$target_type]['handler_settings'];
      if (!empty($handler['target_bundles'])) {
        $target_entity_type = $entity_type_manager
          ->getDefinition($target_type);
        if ($bundle_entity_type_id = $target_entity_type
          ->getBundleEntityType()) {
          if ($storage = $entity_type_manager
            ->getStorage($bundle_entity_type_id)) {
            foreach ($storage
              ->loadMultiple($handler['target_bundles']) as $bundle) {
              $dependencies[$bundle
                ->getConfigDependencyKey()][] = $bundle
                ->getConfigDependencyName();
            }
          }
        }
      }
    }
    return $dependencies;
  }

  /**
   * {@inheritdoc}
   */
  public static function onDependencyRemoval(FieldDefinitionInterface $field_definition, array $dependencies) {
    $changed = FALSE;
    if ($default_value = $field_definition
      ->getDefaultValueLiteral()) {
      foreach ($default_value as $key => $value) {
        if (is_array($value) && isset($value['target_uuid']) && isset($value['target_type'])) {
          $entity = \Drupal::service('entity.repository')
            ->loadEntityByUuid($value['target_type'], $value['target_uuid']);

          // @see \Drupal\dynamic_entity_reference\Plugin\Field\FieldType\DynamicEntityReferenceFieldItemList::processDefaultValue()
          if ($entity && isset($dependencies[$entity
            ->getConfigDependencyKey()][$entity
            ->getConfigDependencyName()])) {
            unset($default_value[$key]);
            $changed = TRUE;
          }
        }
      }
      if ($changed) {
        $field_definition
          ->setDefaultValue($default_value);
      }
    }
    $entity_type_manager = \Drupal::entityTypeManager();

    // Update the 'target_bundles' handler setting if a bundle config dependency
    // has been removed.
    $settings = $field_definition
      ->getSettings();
    foreach (static::getTargetTypes($settings) as $target_type) {
      $bundles_changed = FALSE;
      $handler_settings = $settings[$target_type]['handler_settings'];
      if (!empty($handler_settings['target_bundles'])) {
        $target_entity_type = $entity_type_manager
          ->getDefinition($target_type);
        if ($bundle_entity_type_id = $target_entity_type
          ->getBundleEntityType()) {
          if ($storage = $entity_type_manager
            ->getStorage($bundle_entity_type_id)) {
            foreach ($storage
              ->loadMultiple($handler_settings['target_bundles']) as $bundle) {
              if (isset($dependencies[$bundle
                ->getConfigDependencyKey()][$bundle
                ->getConfigDependencyName()])) {
                unset($handler_settings['target_bundles'][$bundle
                  ->id()]);

                // If this bundle is also used in the 'auto_create_bundle'
                // setting, disable the auto-creation feature completely.
                $auto_create_bundle = !empty($handler_settings['auto_create_bundle']) ? $handler_settings['auto_create_bundle'] : FALSE;
                if ($auto_create_bundle && $auto_create_bundle == $bundle
                  ->id()) {
                  $handler_settings['auto_create'] = FALSE;
                  $handler_settings['auto_create_bundle'] = NULL;
                }
                $bundles_changed = TRUE;

                // In case we deleted the only target bundle allowed by the
                // field we have to log a critical message because the field
                // will not function correctly anymore.
                if ($handler_settings['target_bundles'] === []) {
                  \Drupal::logger('dynamic_entity_reference')
                    ->critical('The %target_bundle bundle (entity type: %target_entity_type) was deleted. As a result, the %field_name dynamic entity reference field (entity_type: %entity_type, bundle: %bundle) no longer has any valid bundle it can reference. The field is not working correctly anymore and has to be adjusted.', [
                    '%target_bundle' => $bundle
                      ->label(),
                    '%target_entity_type' => $bundle
                      ->getEntityType()
                      ->getBundleOf(),
                    '%field_name' => $field_definition
                      ->getName(),
                    '%entity_type' => $field_definition
                      ->getTargetEntityTypeId(),
                    '%bundle' => $field_definition
                      ->getTargetBundle(),
                  ]);
                }
              }
            }
          }
        }
      }
      if ($bundles_changed) {
        $settings[$target_type]['handler_settings'] = $handler_settings;
        $field_definition
          ->setSettings($settings);
      }
      $changed |= $bundles_changed;
    }
    return $changed;
  }

  /**
   * Helper function to get all the entity type ids that can be referenced.
   *
   * @param array $settings
   *   The settings of the field storage.
   *
   * @return string[]
   *   All the target entity type ids that can be referenced.
   */
  public static function getTargetTypes(array $settings) {
    $labels = \Drupal::service('entity_type.repository')
      ->getEntityTypeLabels(TRUE);
    $options = array_filter(array_keys($labels[(string) t('Content', [], [
      'context' => 'Entity type group',
    ])]), function ($entity_type_id) {
      return static::entityHasIntegerId($entity_type_id);
    });
    if (!empty($settings['exclude_entity_types'])) {
      return array_diff($options, $settings['entity_type_ids'] ?: []);
    }
    else {
      return array_intersect($options, $settings['entity_type_ids'] ?: []);
    }
  }

  /**
   * Determines if an entity type has an integer-based ID definition.
   *
   * @param string $entity_type_id
   *   The ID the represents the entity type.
   *
   * @return bool
   *   Returns TRUE if the entity type has an integer-based ID definition and
   *   FALSE otherwise.
   */
  public static function entityHasIntegerId($entity_type_id) {
    $entity_type = \Drupal::entityTypeManager()
      ->getDefinition($entity_type_id);

    // Make sure entity type is a content entity type.
    if (!$entity_type instanceof ContentEntityTypeInterface) {
      return FALSE;
    }

    // Make sure entity type has an id.
    if (!$entity_type
      ->hasKey('id')) {
      return FALSE;
    }

    /** @var \Drupal\Core\Field\FieldDefinitionInterface[] $field_definitions */
    $field_definitions = \Drupal::service('entity_field.manager')
      ->getBaseFieldDefinitions($entity_type_id);
    $entity_type_id_definition = $field_definitions[$entity_type
      ->getKey('id')];
    return $entity_type_id_definition
      ->getType() === 'integer';
  }

  /**
   * {@inheritdoc}
   */
  public static function calculateStorageDependencies(FieldStorageDefinitionInterface $field_definition) {
    $dependencies = FieldItemBase::calculateStorageDependencies($field_definition);
    $entity_manager = \Drupal::entityTypeManager();
    foreach (static::getTargetTypes($field_definition
      ->getSettings()) as $entity_type_id) {
      if ($entity_manager
        ->hasDefinition($entity_type_id) && ($target_entity_type = $entity_manager
        ->getDefinition($entity_type_id))) {
        $dependencies['module'][] = $target_entity_type
          ->getProvider();
      }
    }
    return $dependencies;
  }

  /**
   * {@inheritdoc}
   */
  public static function getPreconfiguredOptions() {
    $options = [];

    // Add all the commonly referenced entity types as distinct pre-configured
    // options.
    $entity_types = \Drupal::entityTypeManager()
      ->getDefinitions();
    $common_references = array_filter($entity_types, function (EntityTypeInterface $entity_type) {
      return $entity_type
        ->isCommonReferenceTarget();
    });

    /** @var \Drupal\Core\Entity\EntityTypeInterface $entity_type */
    foreach ($common_references as $entity_type) {
      $options[$entity_type
        ->id()] = [
        'label' => $entity_type
          ->getLabel(),
        'field_storage_config' => [
          'settings' => [
            'exclude_entity_types' => FALSE,
            'entity_type_ids' => [
              $entity_type
                ->id(),
            ],
          ],
        ],
      ];
    }
    return $options;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DependencySerializationTrait::$_entityStorages protected property An array of entity type IDs keyed by the property name of their storages.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
DynamicEntityReferenceItem::calculateDependencies public static function Calculates dependencies for field items. Overrides EntityReferenceItem::calculateDependencies
DynamicEntityReferenceItem::calculateStorageDependencies public static function Calculates dependencies for field items on the storage level. Overrides EntityReferenceItem::calculateStorageDependencies
DynamicEntityReferenceItem::defaultFieldSettings public static function Defines the field-level settings for this plugin. Overrides EntityReferenceItem::defaultFieldSettings
DynamicEntityReferenceItem::defaultStorageSettings public static function Defines the storage-level settings for this plugin. Overrides EntityReferenceItem::defaultStorageSettings
DynamicEntityReferenceItem::entityHasIntegerId public static function Determines if an entity type has an integer-based ID definition.
DynamicEntityReferenceItem::fieldSettingsForm public function Returns a form for the field-level settings. Overrides EntityReferenceItem::fieldSettingsForm
DynamicEntityReferenceItem::fieldSettingsFormValidate public static function Form element validation handler; Stores the new values in the form state. Overrides EntityReferenceItem::fieldSettingsFormValidate
DynamicEntityReferenceItem::generateSampleValue public static function Generates placeholder field values. Overrides EntityReferenceItem::generateSampleValue
DynamicEntityReferenceItem::getPreconfiguredOptions public static function Returns preconfigured field options for a field type. Overrides EntityReferenceItem::getPreconfiguredOptions
DynamicEntityReferenceItem::getSettableOptions public function To select both target_type and target_id the option value is changed from target_id to target_type-target_id. Overrides EntityReferenceItem::getSettableOptions
DynamicEntityReferenceItem::getTargetTypes public static function Helper function to get all the entity type ids that can be referenced.
DynamicEntityReferenceItem::getValue public function Gets the data value. Overrides EntityReferenceItem::getValue
DynamicEntityReferenceItem::isEmpty public function Determines whether the data structure is empty. Overrides EntityReferenceItem::isEmpty
DynamicEntityReferenceItem::mainPropertyName public static function Returns the name of the main property, if any. Overrides EntityReferenceItem::mainPropertyName
DynamicEntityReferenceItem::onChange public function React to changes to a child property or item. Overrides EntityReferenceItem::onChange
DynamicEntityReferenceItem::onDependencyRemoval public static function Informs the plugin that a dependency of the field will be deleted. Overrides EntityReferenceItem::onDependencyRemoval
DynamicEntityReferenceItem::preSave public function Defines custom presave behavior for field values. Overrides EntityReferenceItem::preSave
DynamicEntityReferenceItem::propertyDefinitions public static function Defines field item properties. Overrides EntityReferenceItem::propertyDefinitions
DynamicEntityReferenceItem::schema public static function Returns the schema for the field. Overrides EntityReferenceItem::schema
DynamicEntityReferenceItem::setValue public function Sets the data value. Overrides EntityReferenceItem::setValue
DynamicEntityReferenceItem::storageSettingsForm public function Returns a form for the storage-level settings. Overrides EntityReferenceItem::storageSettingsForm
DynamicEntityReferenceItem::storageSettingsFormValidate public static function Form element validation for storage settings.
DynamicEntityReferenceItem::targetTypeFieldSettingsForm protected function Returns a form for single target type settings.
EntityReferenceItem::fieldSettingsAjaxProcess public static function Render API callback: Processes the field settings form and allows access to the form state.
EntityReferenceItem::fieldSettingsAjaxProcessElement public static function Adds entity_reference specific properties to AJAX form elements from the field settings form.
EntityReferenceItem::formProcessMergeParent public static function Render API callback: Moves entity_reference specific Form API elements (i.e. 'handler_settings') up a level for easier processing by the validation and submission handlers.
EntityReferenceItem::getConstraints public function Gets a list of validation constraints. Overrides TypedData::getConstraints
EntityReferenceItem::getPossibleOptions public function Returns an array of possible values with labels for display. Overrides OptionsProviderInterface::getPossibleOptions
EntityReferenceItem::getPossibleValues public function Returns an array of possible values. Overrides OptionsProviderInterface::getPossibleValues
EntityReferenceItem::getRandomBundle protected static function Gets a bundle for a given entity type and selection options.
EntityReferenceItem::getSettableValues public function Returns an array of settable values. Overrides OptionsProviderInterface::getSettableValues
EntityReferenceItem::hasNewEntity public function Determines whether the item holds an unsaved entity.
EntityReferenceItem::settingsAjax public static function Ajax callback for the handler settings form.
EntityReferenceItem::settingsAjaxSubmit public static function Submit handler for the non-JS case.
FieldItemBase::delete public function Defines custom delete behavior for field values. Overrides FieldItemInterface::delete 2
FieldItemBase::deleteRevision public function Defines custom revision delete behavior for field values. Overrides FieldItemInterface::deleteRevision
FieldItemBase::fieldSettingsFromConfigData public static function Returns a settings array in the field type's canonical representation. Overrides FieldItemInterface::fieldSettingsFromConfigData 1
FieldItemBase::fieldSettingsToConfigData public static function Returns a settings array that can be stored as a configuration value. Overrides FieldItemInterface::fieldSettingsToConfigData 1
FieldItemBase::getEntity public function Gets the entity that field belongs to. Overrides FieldItemInterface::getEntity
FieldItemBase::getFieldDefinition public function Gets the field definition. Overrides FieldItemInterface::getFieldDefinition
FieldItemBase::getLangcode public function Gets the langcode of the field values held in the object. Overrides FieldItemInterface::getLangcode
FieldItemBase::getSetting protected function Returns the value of a field setting.
FieldItemBase::getSettings protected function Returns the array of field settings.
FieldItemBase::postSave public function Defines custom post-save behavior for field values. Overrides FieldItemInterface::postSave 2
FieldItemBase::storageSettingsFromConfigData public static function Returns a settings array in the field type's canonical representation. Overrides FieldItemInterface::storageSettingsFromConfigData 2
FieldItemBase::storageSettingsToConfigData public static function Returns a settings array that can be stored as a configuration value. Overrides FieldItemInterface::storageSettingsToConfigData 2
FieldItemBase::view public function Returns a renderable array for a single field item. Overrides FieldItemInterface::view
FieldItemBase::writePropertyValue protected function Different to the parent Map class, we avoid creating property objects as far as possible in order to optimize performance. Thus we just update $this->values if no property object has been created yet. Overrides Map::writePropertyValue
FieldItemBase::__construct public function Constructs a TypedData object given its definition and context. Overrides TypedData::__construct 1
FieldItemBase::__get public function Magic method: Gets a property value. Overrides FieldItemInterface::__get 2
FieldItemBase::__isset public function Magic method: Determines whether a property is set. Overrides FieldItemInterface::__isset
FieldItemBase::__set public function Magic method: Sets a property value. Overrides FieldItemInterface::__set 1
FieldItemBase::__unset public function Magic method: Unsets a property. Overrides FieldItemInterface::__unset
Map::$definition protected property The data definition. Overrides TypedData::$definition
Map::$properties protected property The array of properties.
Map::$values protected property An array of values for the contained properties.
Map::applyDefaultValue public function Applies the default value. Overrides TypedData::applyDefaultValue 4
Map::get public function Gets a property object. Overrides ComplexDataInterface::get
Map::getIterator public function
Map::getProperties public function Gets an array of property objects. Overrides ComplexDataInterface::getProperties
Map::getString public function Returns a string representation of the data. Overrides TypedData::getString
Map::set public function Sets a property value. Overrides ComplexDataInterface::set
Map::toArray public function Returns an array of all property values. Overrides ComplexDataInterface::toArray 1
Map::__clone public function Magic method: Implements a deep clone.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
TypedData::$name protected property The property name.
TypedData::$parent protected property The parent typed data object.
TypedData::createInstance public static function Constructs a TypedData object given its definition and context. Overrides TypedDataInterface::createInstance
TypedData::getDataDefinition public function Gets the data definition. Overrides TypedDataInterface::getDataDefinition
TypedData::getName public function Returns the name of a property or item. Overrides TypedDataInterface::getName
TypedData::getParent public function Returns the parent data structure; i.e. either complex data or a list. Overrides TypedDataInterface::getParent
TypedData::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition
TypedData::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
TypedData::getPropertyPath public function Returns the property path of the data. Overrides TypedDataInterface::getPropertyPath
TypedData::getRoot public function Returns the root of the typed data tree. Overrides TypedDataInterface::getRoot
TypedData::setContext public function Sets the context of a property or item via a context aware parent. Overrides TypedDataInterface::setContext
TypedData::validate public function Validates the currently set data value. Overrides TypedDataInterface::validate
TypedDataTrait::$typedDataManager protected property The typed data manager used for creating the data types.
TypedDataTrait::getTypedDataManager public function Gets the typed data manager. 2
TypedDataTrait::setTypedDataManager public function Sets the typed data manager. 2