flow-1.0.0-beta8/src/Plugin/flow/Task/Relate.php

src/Plugin/flow/Task/Relate.php
<?php

namespace Drupal\flow\Plugin\flow\Task;

use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Field\EntityReferenceFieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState;
use Drupal\Core\Plugin\PluginFormInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\field\FieldStorageConfigInterface;
use Drupal\flow\Flow;
use Drupal\flow\FlowCompatibility;
use Drupal\flow\Helpers\EntityFieldManagerTrait;
use Drupal\flow\Helpers\EntityTypeBundleInfoTrait;
use Drupal\flow\Helpers\EntityTypeManagerTrait;
use Drupal\flow\Helpers\ModuleHandlerTrait;
use Drupal\flow\Helpers\SingleTaskOperationTrait;
use Drupal\flow\Plugin\FlowSubjectInterface;
use Drupal\flow\Plugin\FlowSubjectManager;
use Drupal\flow\Plugin\FlowTaskBase;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Task for relating content.
 *
 * @FlowTask(
 *   id = "relate",
 *   label = @Translation("Relate with content"),
 *   deriver = "Drupal\flow\Plugin\flow\Derivative\Task\RelateDeriver"
 * )
 */
class Relate extends FlowTaskBase implements PluginFormInterface {

  use EntityFieldManagerTrait;
  use EntityTypeBundleInfoTrait;
  use EntityTypeManagerTrait;
  use ModuleHandlerTrait;
  use SingleTaskOperationTrait;
  use StringTranslationTrait;

  /**
   * The subject plugin instance used as the target of the reference.
   *
   * Can be NULL if the subject plugin has not yet been defined and configured.
   *
   * @var \Drupal\flow\Plugin\FlowSubjectInterface|null
   */
  protected ?FlowSubjectInterface $target = NULL;

  /**
   * The Flow subject manager for retreiving other plugin definitions.
   *
   * @var \Drupal\flow\Plugin\FlowSubjectManager
   */
  protected FlowSubjectManager $subjectManager;

  /**
   * Some internal stateful flags when working on large lists.
   *
   * @var array
   */
  protected array $flags = [];

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    [, $target] = explode('::', $plugin_id);
    [$target_entity_type_id, $target_bundle] = explode('.', $target);

    /** @var \Drupal\flow\Plugin\flow\Task\Relate $instance */
    $instance = parent::create($container, ['target_entity_type_id' => $target_entity_type_id, 'target_bundle' => $target_bundle] + $configuration, $plugin_id, $plugin_definition);
    $instance->setModuleHandler($container->get(self::$moduleHandlerServiceName));
    $instance->setStringTranslation($container->get('string_translation'));
    $instance->setEntityFieldManager($container->get(self::$entityFieldManagerServiceName));
    $instance->setEntityTypeBundleInfo($container->get(self::$entityTypeBundleInfoServiceName));
    $instance->setEntityTypeManager($container->get(self::$entityTypeManagerServiceName));
    $instance->setSubjectManager($container->get('plugin.manager.flow.subject'));
    $instance->initTargetSubject();
    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  public function setConfiguration(array $configuration) {
    parent::setConfiguration($configuration);
    $this->target = NULL;
    $this->initTargetSubject();
  }

  /**
   * {@inheritdoc}
   */
  public function setSettings(array $settings): void {
    parent::setSettings($settings);
    $this->target = NULL;
    $this->initTargetSubject();
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
    $form['#task'] = $this;

    $source_definition = $this->getPluginDefinition();
    $source_entity_type_id = $source_definition['entity_type'];
    $source_bundle = $source_definition['bundle'];
    $source_entity_type = $this->entityTypeManager->getDefinition($source_entity_type_id);
    $source_bundle_info = $this->entityTypeBundleInfo->getBundleInfo($source_entity_type_id)[$source_bundle];
    $bundle_label = $source_bundle_info['label'] instanceof TranslatableMarkup ? $source_bundle_info['label'] : new TranslatableMarkup($source_bundle_info['label']);
    $source_label = $source_entity_type->getBundleEntityType() ? $this->t('@bundle item (@type)', [
      '@bundle' => $bundle_label,
      '@type' => $source_entity_type->getLabel(),
    ]) : $this->t('@type item', ['@type' => $bundle_label]);

    $target_entity_type_id = $this->configuration['target_entity_type_id'];
    $target_bundle = $this->configuration['target_bundle'];
    $target_entity_type = $this->entityTypeManager->getDefinition($target_entity_type_id);
    $target_bundle_info = $this->entityTypeBundleInfo->getBundleInfo($target_entity_type_id)[$target_bundle];
    $bundle_label = $target_bundle_info['label'] instanceof TranslatableMarkup ? $target_bundle_info['label'] : new TranslatableMarkup($target_bundle_info['label']);
    $target_label = $target_entity_type->getBundleEntityType() ? $this->t('@bundle item (@type)', [
      '@bundle' => $bundle_label,
      '@type' => $target_entity_type->getLabel(),
    ]) : $this->t('@type item', ['@type' => $bundle_label]);

    $select_widget = $this->moduleHandler->moduleExists('select2') ? 'select2' : 'select';
    $weight = 0;

    $weight += 10;
    $field_name_options = [
      '_none' => $this->t('- Select -'),
    ];
    foreach ($this->entityFieldManager->getFieldDefinitions($source_entity_type_id, $source_bundle) as $field_definition) {
      $field_storage_definition = $field_definition->getFieldStorageDefinition();
      if (!(strpos($field_storage_definition->getType(), 'entity_reference') === 0)) {
        continue;
      };
      $field_target_type = $field_storage_definition->getSetting('target_type');
      if ($field_target_type !== $target_entity_type_id) {
        continue;
      }
      $handler_settings = $field_definition->getConfig($source_bundle)->getSetting('handler_settings');
      if (!empty($handler_settings['target_bundles']) && !in_array($target_bundle, $handler_settings['target_bundles'])) {
        continue;
      }
      $field_name_options[$field_storage_definition->getName()] = $this->t('@source: @field', [
        '@source' => $source_label,
        '@field' => $field_definition->getLabel(),
      ]);
    }
    $form['field_name'] = [
      '#type' => $select_widget,
      '#title' => $this->t('Reference field'),
      '#description' => $this->t('Select the field of the subject that will hold the reference to the @target.', [
        '@target' => $target_label,
      ]),
      '#options' => $field_name_options,
      '#default_value' => $this->settings['field_name'] ?? '_none',
      '#required' => TRUE,
      '#empty_value' => '_none',
      '#weight' => $weight++,
    ];

    $weight += 10;
    $reference_method_options = [
      '_none' => $this->t('- Select -'),
      'append:not_full' => $this->t('Append when list is not full yet'),
      'append:drop_first' => $this->t('Append and drop first item on a full list'),
      'append:drop_last' => $this->t('Append and drop last item on a full list'),
      'prepend:not_full' => $this->t('Prepend when list is not full yet'),
      'prepend:drop_first' => $this->t('Prepend and drop first item on a full list'),
      'prepend:drop_last' => $this->t('Prepend and drop last item on a full list'),
      'set:not_set' => $this->t('Set when no other reference was set before'),
      'set:clear' => $this->t('Set and clear any previously set reference'),
      'remove' => $this->t('Remove the reference instead of adding it'),
    ];
    $form['method'] = [
      '#type' => 'select',
      '#title' => $this->t('Reference method'),
      '#description' => $this->t('Select how the relationship will be defined, using the above selected reference field.'),
      '#options' => $reference_method_options,
      '#default_value' => $this->settings['method'] ?? '_none',
      '#required' => TRUE,
      '#empty_value' => '_none',
      '#weight' => $weight++,
    ];

    $field_name_options = [];
    foreach ($this->entityFieldManager->getFieldDefinitions($target_entity_type_id, $target_bundle) as $field_definition) {
      $field_storage_definition = $field_definition->getFieldStorageDefinition();
      if (!(strpos($field_storage_definition->getType(), 'entity_reference') === 0)) {
        continue;
      };
      $field_target_type = $field_storage_definition->getSetting('target_type');
      if ($field_target_type !== $source_entity_type_id) {
        continue;
      }
      $handler_settings = $field_definition->getConfig($source_bundle)->getSetting('handler_settings');
      if (!empty($handler_settings['target_bundles']) && !in_array($source_bundle, $handler_settings['target_bundles'])) {
        continue;
      }
      $field_name_options[$field_storage_definition->getName()] = $this->t('@source: @field', [
        '@source' => $target_label,
        '@field' => $field_definition->getLabel(),
      ]);
    }
    if ($field_name_options) {
      $weight += 10;
      $field_name_options = ['_none' => $this->t('- None -')] + $field_name_options;
      $reverse_wrapper_id = Html::getUniqueId('flow-reverse-reference');
      $form['reverse'] = [
        '#type' => 'container',
        '#attributes' => ['id' => $reverse_wrapper_id],
        '#weight' => $weight++,
      ];
      $form['reverse']['field_name'] = [
        '#type' => $select_widget,
        '#title' => $this->t('Reverse reference field'),
        '#description' => $this->t('Optionally you may define the reverse relationship, by selecting the field of the target that will hold the reference back to the @source.', [
          '@source' => $source_label,
        ]),
        '#options' => $field_name_options,
        '#default_value' => $this->settings['reverse']['field_name'] ?? '_none',
        '#required' => FALSE,
        '#empty_value' => '_none',
        '#weight' => 10,
        '#ajax' => [
          'callback' => [$this, 'selectAjax'],
          'wrapper' => $reverse_wrapper_id,
        ],
        '#submit' => [[$this, 'selectSubmitAjax']],
        '#executes_submit_callback' => TRUE,
        '#limit_validation_errors' => [],
      ];
      $weight += 10;
      if (!empty($this->settings['reverse']['field_name'])) {
        $form['reverse']['method'] = [
          '#type' => 'select',
          '#title' => $this->t('Reverse reference method'),
          '#description' => $this->t('Select how the reverse relationship will be defined, using the above selected reverse reference field.'),
          '#options' => $reference_method_options,
          '#default_value' => $this->settings['reverse']['method'] ?? '_none',
          '#required' => TRUE,
          '#empty_value' => '_none',
          '#weight' => 20,
        ];
      }
    }

    $weight += 10;
    $target_wrapper_id = Html::getUniqueId('flow-target-subject');
    $form['target'] = [
      '#type' => 'container',
      '#attributes' => ['id' => $target_wrapper_id],
      '#weight' => $weight++,
    ];
    $flow = Flow::getFlow($this->configuration['entity_type_id'], $this->configuration['bundle'], $this->configuration['task_mode']);
    $target_plugin_options = [
      '_none' => $this->t('- Select -'),
    ];
    foreach ($this->subjectManager->getDefinitions() as $target_plugin_id => $target_plugin_definition) {
      $subject = $this->subjectManager->createInstance($target_plugin_id, array_intersect_key($this->configuration, [
        'entity_type_id' => TRUE,
        'bundle' => TRUE,
        'task_mode' => TRUE,
      ]));
      if ($target_plugin_definition['entity_type'] !== $target_entity_type_id) {
        continue;
      }
      if ($target_plugin_definition['bundle'] !== $target_bundle) {
        continue;
      }
      if (!FlowCompatibility::validate($flow, $subject)) {
        continue;
      }
      $target_plugin_options[$target_plugin_id] = $target_plugin_definition['label'];
    }
    $form['target']['id'] = [
      '#type' => $select_widget,
      '#title' => $this->t('Reference target'),
      '#description' => $this->t('Select how the @target will be identified.', [
        '@target' => $target_label,
      ]),
      '#options' => $target_plugin_options,
      '#default_value' => $this->settings['target']['id'] ?? '_none',
      '#required' => TRUE,
      '#empty_value' => '_none',
      '#weight' => 10,
      '#ajax' => [
        'callback' => [$this, 'selectAjax'],
        'wrapper' => $target_wrapper_id,
      ],
      '#submit' => [[$this, 'selectSubmitAjax']],
      '#executes_submit_callback' => TRUE,
      '#limit_validation_errors' => [],
    ];
    if ($target = $this->getTargetSubject()) {
      if ($target instanceof PluginFormInterface) {
        $form['target']['settings'] = [
          '#type' => 'fieldset',
          '#title' => $this->t('@type: @name', [
            '@type' => $this->t('Target'),
            '@name' => $target_label,
          ]),
          '#weight' => 20,
        ];
        $target_form_state = SubformState::createForSubform($form['target']['settings'], $form, $form_state);
        $form['target']['settings'] += $target->buildConfigurationForm($form['target']['settings'], $target_form_state);
        if (isset($form['target']['settings']['fallback']['#title'])) {
          $form['target']['settings']['fallback']['#title'] = $this->t('When the target could not be loaded');
        }
      }
      else {
        $form['target']['no_settings'] = [
          '#type' => 'markup',
          '#markup' => $this->t('This target does not provide any settings.'),
        ];
      }
    }

    return $form;
  }

  /**
   * Ajax submit callback for select widgets.
   *
   * @param array &$form
   *   The current form build array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The according form state.
   */
  public function selectSubmitAjax(array &$form, FormStateInterface $form_state): void {
    $button = $form_state->getTriggeringElement();
    $button_parents = $button['#array_parents'];
    while ($element = &NestedArray::getValue($form, $button_parents)) {
      foreach (Element::children($element) as $child) {
        if (isset($element[$child]['#value'])) {
          $value = $element[$child]['#value'] === '_none' ? NULL : $element[$child]['#value'];
          $form_state->setValueForElement($element[$child], $value);
        }
      }
      if (isset($element['#task']) && $element['#task'] === $this) {
        break;
      }
      array_pop($button_parents);
    }
    $subform_state = SubformState::createForSubform($element, $form, $form_state);
    $this->settings['field_name'] = $subform_state->getValue(['field_name']);
    $this->settings['method'] = $subform_state->getValue(['method']);
    $this->settings['reverse'] = $subform_state->getValue(['reverse']);
    $this->settings['target']['id'] = $subform_state->getValue(['target', 'id']);
    $form_state->setRebuild();
  }

  /**
   * Ajax callback for setting up the identifier of the target.
   *
   * @param array &$form
   *   The current form build array.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The according form state.
   *
   * @return array
   *   The part of the form that got refreshed via Ajax.
   */
  public function selectAjax(array &$form, FormStateInterface $form_state): array {
    $button = &$form_state->getTriggeringElement();
    $element = &NestedArray::getValue($form, array_slice($button['#array_parents'], 0, -1));
    return $element;
  }

  /**
   * {@inheritdoc}
   */
  public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
    $target = $this->getTargetSubject();
    if (isset($form['target']['settings']) && $target instanceof PluginFormInterface) {
      $target_form_state = SubformState::createForSubform($form['target']['settings'], $form, $form_state);
      $target->validateConfigurationForm($form['target']['settings'], $target_form_state);
    }
  }

  /**
   * {@inheritdoc}
   */
  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
    $this->settings['field_name'] = $form_state->getValue(['field_name']);
    $this->settings['method'] = $form_state->getValue(['method']);
    $this->settings['reverse'] = $form_state->getValue(['reverse']);
    $this->settings['target']['id'] = $form_state->getValue(['target', 'id']);

    $this->target = NULL;
    $this->initTargetSubject();
    if ($target = $this->getTargetSubject()) {
      if (isset($form['target']['settings']) && $form_state->hasValue(['target', 'settings']) && $target instanceof PluginFormInterface) {
        $target_form_state = SubformState::createForSubform($form['target']['settings'], $form, $form_state);
        $target->submitConfigurationForm($form['target']['settings'], $target_form_state);
      }
      $this->settings['target'] = [
        'id' => $target->getPluginId(),
        'type' => $target->getBaseId(),
        'settings' => $target->getSettings(),
        'third_party_settings' => [],
      ];
      foreach ($target->getThirdPartyProviders() as $provider) {
        $this->settings['target']['third_party_settings'][$provider] = $target->getThirdPartySettings($provider);
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  protected function doOperate(ContentEntityInterface $entity): void {
    $field_name = $this->settings['field_name'];
    if (!$entity->hasField($field_name)) {
      return;
    }
    $item_list = $entity->get($field_name);
    if (!($item_list instanceof EntityReferenceFieldItemListInterface)) {
      return;
    }
    $method_settings = explode(':', $this->settings['method']);
    $reverse_field_name = $this->settings['reverse']['field_name'] ?? NULL;
    $reverse_method_settings = !empty($this->settings['reverse']['method']) ? explode(':', $this->settings['reverse']['method']) : NULL;

    if (empty($this->flags)) {
      $this->flags = [
        'count' => 0,
        'cardinality' => $item_list->getFieldDefinition()->getFieldStorageDefinition()->getCardinality(),
      ];
    }
    $cardinality = $this->flags['cardinality'];
    $unlimited = FieldStorageConfigInterface::CARDINALITY_UNLIMITED;

    foreach ($this->getTargetSubject()->getSubjectItems() as $target) {
      if (($cardinality === $unlimited) || ($this->flags['count'] < $cardinality)) {
        $this->relate($item_list, $target, $method_settings, $this->flags);
        $this->flags['count']++;
      }
      else {
        break;
      }

      if (isset($reverse_field_name) && $target->hasField($reverse_field_name)) {
        $reverse_item_list = $target->get($reverse_field_name);
        if ($reverse_item_list instanceof EntityReferenceFieldItemListInterface) {
          $target_flags = [
            'cardinality' => $reverse_item_list->getFieldDefinition()->getFieldStorageDefinition()->getCardinality(),
          ];
          $this->relate($reverse_item_list, $entity, $reverse_method_settings, $target_flags);
        }
      }
    }

    $this->flags = [];
  }

  /**
   * Relates content using its item list with a specific target.
   *
   * @param \Drupal\Core\Field\EntityReferenceFieldItemListInterface $item_list
   *   The field item list holding current entity references.
   * @param \Drupal\Core\Entity\ContentEntityInterface $target
   *   The target to relate with.
   * @param array &$method_settings
   *   The settings that specify the reference method.
   * @param array &$flags
   *   Stateful flags that may be reused on multiple relate calls. Must be
   *   initialized with the configured field cardinality of the item list.
   */
  public function relate(EntityReferenceFieldItemListInterface $item_list, ContentEntityInterface $target, array &$method_settings, array &$flags): void {
    if (!isset($flags['cardinality'])) {
      throw new \InvalidArgumentException("The flags argument was not properly initialized.");
    }

    $cardinality = $flags['cardinality'];
    $unlimited = FieldStorageConfigInterface::CARDINALITY_UNLIMITED;
    $referenced_entities = $item_list->referencedEntities();

    $is_full = !($cardinality === $unlimited) && !(count($referenced_entities) < $cardinality);
    $is_referenced = (bool) (static::getReferenceIndex($referenced_entities, $target) !== FALSE);
    $can_add = FALSE;
    $entity_needs_save = FALSE;
    foreach ($method_settings as $method_setting) {
      switch ($method_setting) {

        case 'drop_first':
          if (!$is_referenced) {
            while ($is_full) {
              array_shift($referenced_entities);
              $entity_needs_save = TRUE;
              $is_full = !($cardinality === $unlimited) && !(count($referenced_entities) < $cardinality);
            }
            $can_add = TRUE;
          }
          break;

        case 'drop_last':
          if (!$is_referenced) {
            while ($is_full) {
              array_pop($referenced_entities);
              $entity_needs_save = TRUE;
              $is_full = !($cardinality === $unlimited) && !(count($referenced_entities) < $cardinality);
            }
            $can_add = TRUE;
          }
          break;

        case 'not_full':
          $can_add = !$is_referenced && !$is_full;
          break;

        case 'not_set':
          if (!$is_referenced) {
            if (empty($flags['not_set'])) {
              $flags['not_set'] = TRUE;
              $flags['can_add'] = empty($referenced_entities);
            }
            $can_add = !empty($flags['can_add']);
          }
          break;

        case 'clear':
          if (!$is_referenced) {
            if (empty($flags['cleared'])) {
              if (!empty($referenced_entities)) {
                $referenced_entities = [];
                $entity_needs_save = TRUE;
                $is_referenced = FALSE;
                $is_full = FALSE;
              }
              $can_add = TRUE;
              $flags['cleared'] = TRUE;
            }
          }
          break;

        case 'remove':
          if ($is_referenced) {
            $index = static::getReferenceIndex($referenced_entities, $target);
            while ($index !== FALSE) {
              unset($referenced_entities[$index]);
              $entity_needs_save = TRUE;
              $index = static::getReferenceIndex($referenced_entities, $target);
              $is_full = !($cardinality === $unlimited) && !(count($referenced_entities) < $cardinality);
            }
            $is_referenced = FALSE;
          }
          break;

      }
    }
    if ($can_add) {
      foreach ($method_settings as $method_setting) {
        switch ($method_setting) {

          case 'append':
            array_push($referenced_entities, $target);
            $entity_needs_save = TRUE;
            $is_referenced = TRUE;
            break;

          case 'prepend':
            array_unshift($referenced_entities, $target);
            $entity_needs_save = TRUE;
            $is_referenced = TRUE;
            break;

          case 'set':
            if (empty($flags['cleared'])) {
              if (!empty($referenced_entities)) {
                $referenced_entities = [];
                $is_referenced = FALSE;
              }
              $flags['cleared'] = TRUE;
            }
            $referenced_entities[] = $target;
            $entity_needs_save = TRUE;
            $is_referenced = TRUE;
            break;

        }
      }
    }
    if ($entity_needs_save) {
      $item_list->setValue(array_values($referenced_entities));
      Flow::needsSave($item_list->getEntity(), $this);
    }
  }

  /**
   * Set the Flow subject plugin manager.
   *
   * @param \Drupal\flow\Plugin\FlowSubjectManager $subject_manager
   *   The subject plugin manager.
   */
  public function setSubjectManager(FlowSubjectManager $subject_manager): void {
    $this->subjectManager = $subject_manager;
  }

  /**
   * Initializes the target subject.
   */
  public function initTargetSubject(): void {
    // Initialize the plugin instance of the target subject, if configured.
    $settings = $this->getSettings();
    if (empty($settings['target']['id'])) {
      return;
    }
    $definition = $this->getPluginDefinition();
    $target_configuration = $settings['target'] ?? [];
    $target_configuration += array_intersect_key($this->configuration,
      ['entity_type_id' => TRUE, 'bundle' => TRUE, 'task_mode' => TRUE]);
    // Attach a runtime setting, which describes for which type of reference
    // this target is being used for.
    $target_configuration['settings']['target_for'] = [
      'entity_type' => $definition['entity_type'],
      'bundle' => $definition['bundle'],
    ];
    if (isset($settings['field_name'])) {
      $target_configuration['settings']['target_for']['field'] = $settings['field_name'];
    }
    if (isset($settings['reverse']['field_name'])) {
      $target_configuration['settings']['target_for']['reverse_field'] = $settings['reverse']['field_name'];
    }
    $this->setTargetSubject($this->subjectManager->createInstance($settings['target']['id'], $target_configuration));
  }

  /**
   * Get the target subject, if defined.
   *
   * @return \Drupal\flow\Plugin\FlowSubjectInterface
   *   The target subject, or NULL if the subject has not yet been configured.
   */
  public function getTargetSubject(): ?FlowSubjectInterface {
    if (!isset($this->target)) {
      $this->initTargetSubject();
    }
    return $this->target;
  }

  /**
   * Set a subject plugin instance as the target subject.
   *
   * @param \Drupal\flow\Plugin\FlowSubjectInterface $subject
   *   The target subject.
   */
  public function setTargetSubject(FlowSubjectInterface $subject): void {
    $this->target = $subject;
  }

  /**
   * Returns the delta when the entity is in the list of referenced entities.
   *
   * @param \Drupal\Core\Entity\ContentEntityInterface[] &$referenced_entities
   *   The list of referenced entities.
   * @param \Drupal\Core\Entity\ContentEntityInterface $entity
   *   The entity to check for.
   *
   * @return int|bool
   *   Returns the delta index if the entity is referenced, FALSE otherwise.
   *   Please note that the index may be 0.
   */
  public static function getReferenceIndex(&$referenced_entities, ContentEntityInterface $entity) {
    foreach ($referenced_entities as $i => $referenced) {
      if (($referenced === $entity) || ($referenced->uuid() === $entity->uuid()) || (($referenced->getEntityTypeId() === $entity->getEntityTypeId()) && ($referenced->id() === $entity->id()))) {
        return (int) $i;
      }
    }
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function calculateDependencies() {
    $dependencies = parent::calculateDependencies();

    $etm = \Drupal::entityTypeManager();
    $source_definition = $this->getPluginDefinition();
    if (isset($this->settings['field_name'])) {
      if ($field_storage_config = $etm->getStorage('field_storage_config')->load($source_definition['entity_type'] . '.' . $this->settings['field_name'])) {
        $dependencies[$field_storage_config->getConfigDependencyKey()][] = $field_storage_config->getConfigDependencyName();
      }
      if ($field_config = $etm->getStorage('field_config')->load($source_definition['entity_type'] . '.' . $source_definition['bundle'] . '.' . $this->settings['field_name'])) {
        $dependencies[$field_config->getConfigDependencyKey()][] = $field_config->getConfigDependencyName();
      }
    }
    $target_entity_type_id = $this->configuration['target_entity_type_id'] ?? NULL;
    $target_bundle = $this->configuration['target_bundle'] ?? NULL;
    if ($target_entity_type_id && $target_bundle && !empty($this->settings['reverse']['field_name'])) {
      if ($field_storage_config = $etm->getStorage('field_storage_config')->load($target_entity_type_id . '.' . $this->settings['reverse']['field_name'])) {
        $dependencies[$field_storage_config->getConfigDependencyKey()][] = $field_storage_config->getConfigDependencyName();
      }
      if ($field_config = $etm->getStorage('field_config')->load($target_entity_type_id . '.' . $target_bundle . '.' . $this->settings['reverse']['field_name'])) {
        $dependencies[$field_config->getConfigDependencyKey()][] = $field_config->getConfigDependencyName();
      }
    }
    /** @var \Drupal\flow\FlowSubjectBase $target */
    if ($target = $this->getTargetSubject()) {
      foreach ($target->calculateDependencies() as $key => $source_dependencies) {
        if (!isset($dependencies[$key])) {
          $dependencies[$key] = $source_dependencies;
        }
        else {
          $dependencies[$key] = array_unique(array_merge($dependencies[$key], $source_dependencies));
        }
      }
    }

    return $dependencies;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc