You are here

class EntityReference in Entity Share 8.3

Handle entity reference.

Plugin annotation


@ImportProcessor(
  id = "entity_reference",
  label = @Translation("Entity reference"),
  description = @Translation("Handle entity reference fields."),
  stages = {
    "process_entity" = 10,
  },
  locked = true,
)

Hierarchy

Expanded class hierarchy of EntityReference

File

modules/entity_share_client/src/Plugin/EntityShareClient/Processor/EntityReference.php, line 31

Namespace

Drupal\entity_share_client\Plugin\EntityShareClient\Processor
View source
class EntityReference extends ImportProcessorPluginBase implements PluginFormInterface {

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The event dispatcher service.
   *
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
   */
  protected $eventDispatcher;

  /**
   * The remote manager.
   *
   * @var \Drupal\entity_share_client\Service\RemoteManagerInterface
   */
  protected $remoteManager;

  /**
   * Logger.
   *
   * @var \Psr\Log\LoggerInterface
   */
  protected $logger;

  /**
   * Entity reference helper service.
   *
   * @var \Drupal\entity_share_client\Service\EntityReferenceHelperInterface
   */
  protected $entityReferenceHelper;

  /**
   * The current recursion depth.
   *
   * @var int
   */
  protected $currentRecursionDepth = 0;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    $instance = parent::create($container, $configuration, $plugin_id, $plugin_definition);
    $instance->entityTypeManager = $container
      ->get('entity_type.manager');
    $instance->eventDispatcher = $container
      ->get('event_dispatcher');
    $instance->remoteManager = $container
      ->get('entity_share_client.remote_manager');
    $instance->logger = $container
      ->get('logger.channel.entity_share_client');
    $instance->entityReferenceHelper = $container
      ->get('entity_share_client.entity_reference_helper');
    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration() {
    return [
      'max_recursion_depth' => -1,
    ] + parent::defaultConfiguration();
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
    $form['max_recursion_depth'] = [
      '#type' => 'number',
      '#title' => $this
        ->t('Maximum recursion depth'),
      '#description' => $this
        ->t('The maximum recursion depth. -1 for unlimited. When reaching max recursion depth, referenced entities are set if the entity already exists on the website.'),
      '#default_value' => $this->configuration['max_recursion_depth'],
      '#min' => -1,
    ];
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function processEntity(RuntimeImportContext $runtime_import_context, ContentEntityInterface $processed_entity, array $entity_json_data) {
    if (isset($entity_json_data['relationships'])) {
      $field_mappings = $runtime_import_context
        ->getFieldMappings();

      // Loop on reference fields.
      foreach ($entity_json_data['relationships'] as $field_public_name => $field_data) {
        $field_internal_name = array_search($field_public_name, $field_mappings[$processed_entity
          ->getEntityTypeId()][$processed_entity
          ->bundle()]);
        if (!$processed_entity
          ->hasField($field_internal_name)) {
          $this->logger
            ->notice('Error during import. The field @field does not exist.', [
            '@field' => $field_internal_name,
          ]);
          continue;
        }
        $field = $processed_entity
          ->get($field_internal_name);
        if ($this->entityReferenceHelper
          ->relationshipHandleable($field) !== EntityReferenceHelperInterface::RELATIONSHIP_HANDLEABLE) {
          continue;
        }
        $main_property = $field
          ->getItemDefinition()
          ->getMainPropertyName();
        $field_values = [];

        // Check that the field has data.
        if ($field_data['data'] != NULL) {
          $prepared_field_data = EntityShareUtility::prepareData($field_data['data']);
          $referenced_entities_ids = [];

          // Max recursion depth reached. Reference only existing entities.
          if ($this->currentRecursionDepth == $this->configuration['max_recursion_depth']) {
            $referenced_entities_ids = $this
              ->getExistingEntities($prepared_field_data);
          }
          elseif (isset($field_data['links']['related']['href'])) {
            $referenced_entities_ids = $this
              ->importUrl($runtime_import_context, $field_data['links']['related']['href']);

            // It is possible that some entities have been skipped from import,
            // but do exist, so ensure that those are available to the
            // mapping code below.
            $referenced_entities_ids = $this
              ->getExistingEntities($prepared_field_data) + $referenced_entities_ids;
          }

          // Add field value.
          // As the loop is on the JSON:API data, the sort is preserved.
          foreach ($prepared_field_data as $field_value_data) {
            $referenced_entity_uuid = $field_value_data['id'];

            // Check that the referenced entity exists or had been imported.
            if (!isset($referenced_entities_ids[$referenced_entity_uuid])) {
              continue;
            }
            $field_value = [
              $main_property => $referenced_entities_ids[$referenced_entity_uuid],
            ];

            // Add field metadata.
            if (isset($field_value_data['meta'])) {
              $field_value += $field_value_data['meta'];
            }

            // Allow to alter the field value with an event.
            $event = new RelationshipFieldValueEvent($field, $field_value);
            $this->eventDispatcher
              ->dispatch(RelationshipFieldValueEvent::EVENT_NAME, $event);
            $field_values[] = $event
              ->getFieldValue();
          }
        }
        $processed_entity
          ->set($field_public_name, $field_values);
      }

      // @todo Test if this is still needed.
      // Save the entity once all the references have been updated.
      $processed_entity
        ->save();
    }
  }

  /**
   * Helper function to get existing reference entities.
   *
   * @param array $data
   *   The JSON:API data for an entity reference field.
   *
   * @return array
   *   An array of entity IDs keyed by UUID.
   */
  protected function getExistingEntities(array $data) {
    $referenced_entities_ids = [];
    $entity_uuids = [];

    // Extract list of UUIDs.
    foreach ($data as $field_value_data) {
      if ($field_value_data['id'] !== 'missing') {
        $parsed_type = explode('--', $field_value_data['type']);
        $entity_type_id = $parsed_type[0];
        $entity_uuids[] = $field_value_data['id'];
      }
    }
    if (!empty($entity_uuids)) {
      try {

        // Load the entities to be able to return an array of IDs keyed by
        // UUIDs. Sorting the array will be done later.
        $entity_storage = $this->entityTypeManager
          ->getStorage($entity_type_id);
        $existing_entity_ids = $entity_storage
          ->getQuery()
          ->condition('uuid', $entity_uuids, 'IN')
          ->execute();
        $existing_entities = $entity_storage
          ->loadMultiple($existing_entity_ids);
        foreach ($existing_entities as $existing_entity) {
          $referenced_entities_ids[$existing_entity
            ->uuid()] = $existing_entity
            ->id();
        }
      } catch (\Exception $e) {
        $log_variables = [];
        $log_variables['@msg'] = $e
          ->getMessage();
        $this->logger
          ->error('Caught exception trying to load existing entities. Error message was @msg', $log_variables);
      }
    }
    return $referenced_entities_ids;
  }

  /**
   * Helper function.
   *
   * @param \Drupal\entity_share_client\RuntimeImportContext $runtime_import_context
   *   The runtime import context.
   * @param string $url
   *   The URL to import.
   *
   * @return array
   *   The list of entity IDs imported keyed by UUIDs.
   */
  protected function importUrl(RuntimeImportContext $runtime_import_context, $url) {
    $referenced_entities_ids = [];
    $referenced_entities_response = $this->remoteManager
      ->jsonApiRequest($runtime_import_context
      ->getRemote(), 'GET', $url);
    $referenced_entities_json = Json::decode((string) $referenced_entities_response
      ->getBody());

    // $referenced_entities_json['data'] can be null in the case of
    // missing/deleted referenced entities.
    if (!isset($referenced_entities_json['errors']) && !is_null($referenced_entities_json['data'])) {
      $this->currentRecursionDepth++;
      $referenced_entities_ids = $runtime_import_context
        ->getImportService()
        ->importEntityListData($referenced_entities_json['data']);
      $this->currentRecursionDepth--;
    }
    return $referenced_entities_ids;
  }

}

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
EntityReference::$currentRecursionDepth protected property The current recursion depth.
EntityReference::$entityReferenceHelper protected property Entity reference helper service.
EntityReference::$entityTypeManager protected property The entity type manager.
EntityReference::$eventDispatcher protected property The event dispatcher service.
EntityReference::$logger protected property Logger.
EntityReference::$remoteManager protected property The remote manager.
EntityReference::buildConfigurationForm public function Form constructor. Overrides PluginFormInterface::buildConfigurationForm
EntityReference::create public static function Creates an instance of the plugin. Overrides ImportProcessorPluginBase::create
EntityReference::defaultConfiguration public function Gets default configuration for this plugin. Overrides ImportProcessorPluginBase::defaultConfiguration
EntityReference::getExistingEntities protected function Helper function to get existing reference entities.
EntityReference::importUrl protected function Helper function. 1
EntityReference::processEntity public function Method called on STAGE_PROCESS_ENTITY. Overrides ImportProcessorPluginBase::processEntity
ImportProcessorInterface::STAGE_IS_ENTITY_IMPORTABLE constant Processing stage: is entity importable.
ImportProcessorInterface::STAGE_POST_ENTITY_SAVE constant Processing stage: post entity save.
ImportProcessorInterface::STAGE_PREPARE_ENTITY_DATA constant Processing stage: prepare entity data.
ImportProcessorInterface::STAGE_PREPARE_IMPORTABLE_ENTITY_DATA constant Processing stage: prepare importable entity data.
ImportProcessorInterface::STAGE_PROCESS_ENTITY constant Processing stage: process entity.
ImportProcessorPluginBase::getConfiguration public function Gets this plugin's configuration. Overrides ConfigurableInterface::getConfiguration
ImportProcessorPluginBase::getDescription public function Returns the plugin's description. Overrides ImportProcessorInterface::getDescription
ImportProcessorPluginBase::getWeight public function Returns the weight for a specific processing stage. Overrides ImportProcessorInterface::getWeight
ImportProcessorPluginBase::isEntityImportable public function Method called on STAGE_IS_ENTITY_IMPORTABLE. Overrides ImportProcessorInterface::isEntityImportable 2
ImportProcessorPluginBase::isLocked public function Determines whether this processor should always be enabled. Overrides ImportProcessorInterface::isLocked
ImportProcessorPluginBase::label public function Returns the label for use on the administration pages. Overrides ImportProcessorInterface::label
ImportProcessorPluginBase::postEntitySave public function Method called on STAGE_POST_ENTITY_SAVE. Overrides ImportProcessorInterface::postEntitySave 1
ImportProcessorPluginBase::prepareEntityData public function Method called on STAGE_PREPARE_ENTITY_DATA. Overrides ImportProcessorInterface::prepareEntityData
ImportProcessorPluginBase::prepareImportableEntityData public function Method called on STAGE_PREPARE_IMPORTABLE_ENTITY_DATA. Overrides ImportProcessorInterface::prepareImportableEntityData 4
ImportProcessorPluginBase::setConfiguration public function Sets the configuration for this plugin instance. Overrides ConfigurableInterface::setConfiguration
ImportProcessorPluginBase::setWeight public function Sets the weight for a specific processing stage. Overrides ImportProcessorInterface::setWeight
ImportProcessorPluginBase::submitConfigurationForm public function Form submission handler.
ImportProcessorPluginBase::supportsStage public function Checks whether this processor implements a particular stage. Overrides ImportProcessorInterface::supportsStage
ImportProcessorPluginBase::validateConfigurationForm public function Form validation handler.
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
PluginBase::$configuration protected property Configuration information passed into the plugin. 1
PluginBase::$pluginDefinition protected property The plugin implementation definition. 1
PluginBase::$pluginId protected property The plugin_id.
PluginBase::DERIVATIVE_SEPARATOR constant A string which is used to separate base plugin IDs from the derivative ID.
PluginBase::getBaseId public function Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface::getBaseId
PluginBase::getDerivativeId public function Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface::getDerivativeId
PluginBase::getPluginDefinition public function Gets the definition of the plugin implementation. Overrides PluginInspectionInterface::getPluginDefinition 3
PluginBase::getPluginId public function Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface::getPluginId
PluginBase::isConfigurable public function Determines if the plugin is configurable.
PluginBase::__construct public function Constructs a \Drupal\Component\Plugin\PluginBase object. 92
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.