--- /dev/null
+<?php
+
+namespace Drupal\migrate_drupal\Plugin\migrate;
+
+use Drupal\Component\Plugin\Derivative\DeriverBase;
+use Drupal\Component\Plugin\PluginBase;
+use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Deriver for entity reference field translations.
+ *
+ * A migration will be created for every bundle with at least one entity
+ * reference field that is configured to point to one of the supported target
+ * entity types. The migrations will update the entity reference fields with
+ * values found in the mapping tables of the migrations associated with the
+ * target types.
+ *
+ * Example:
+ *
+ * @code
+ * id: d7_entity_reference_translation
+ * label: Entity reference translations
+ * migration_tags:
+ * - Drupal 7
+ * - Follow-up migration
+ * deriver: Drupal\migrate_drupal\Plugin\migrate\EntityReferenceTranslationDeriver
+ * target_types:
+ * node:
+ * - d7_node_translation
+ * source:
+ * plugin: empty
+ * key: default
+ * target: default
+ * process: []
+ * destination:
+ * plugin: null
+ * @endcode
+ *
+ * In this example, the only supported target type is 'node' and the associated
+ * migration for the mapping table lookup is 'd7_node_translation'.
+ */
+class EntityReferenceTranslationDeriver extends DeriverBase implements ContainerDeriverInterface {
+
+ use StringTranslationTrait;
+
+ /**
+ * The entity field manager.
+ *
+ * @var \Drupal\Core\Entity\EntityFieldManagerInterface
+ */
+ protected $entityFieldManager;
+
+ /**
+ * The entity type manager.
+ *
+ * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+ */
+ protected $entityTypeManager;
+
+ /**
+ * EntityReferenceTranslationDeriver constructor.
+ *
+ * @param string $base_plugin_id
+ * The base plugin ID.
+ * @param \Drupal\core\Entity\EntityFieldManagerInterface $entity_field_manager
+ * The entity field manager.
+ * @param \Drupal\core\Entity\EntityTypeManagerInterface $entity_type_manager
+ * The entity type manager.
+ */
+ public function __construct($base_plugin_id, EntityFieldManagerInterface $entity_field_manager, EntityTypeManagerInterface $entity_type_manager) {
+ $this->entityFieldManager = $entity_field_manager;
+ $this->entityTypeManager = $entity_type_manager;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public static function create(ContainerInterface $container, $base_plugin_id) {
+ return new static(
+ $base_plugin_id,
+ $container->get('entity_field.manager'),
+ $container->get('entity_type.manager')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getDerivativeDefinitions($base_plugin_definition) {
+ // Get all entity reference fields.
+ $field_map = $this->entityFieldManager->getFieldMapByFieldType('entity_reference');
+
+ foreach ($field_map as $entity_type => $fields) {
+ foreach ($fields as $field_name => $field) {
+ foreach ($field['bundles'] as $bundle) {
+ $field_definitions = $this->entityFieldManager->getFieldDefinitions($entity_type, $bundle);
+ $target_type = $field_definitions[$field_name]->getSetting('target_type');
+
+ // If the field's target type is not supported, skip it.
+ if (!array_key_exists($target_type, $base_plugin_definition['target_types'])) {
+ continue;
+ }
+
+ // Key derivatives by entity types and bundles.
+ $derivative_key = $entity_type . '__' . $bundle;
+
+ $derivative = $base_plugin_definition;
+ $entity_type_definition = $this->entityTypeManager->getDefinition($entity_type);
+
+ // Set the migration label.
+ $derivative['label'] = $this->t('@label (@derivative)', [
+ '@label' => $base_plugin_definition['label'],
+ '@derivative' => $derivative_key,
+ ]);
+
+ // Set the source plugin.
+ $derivative['source']['plugin'] = 'content_entity' . PluginBase::DERIVATIVE_SEPARATOR . $entity_type;
+ if ($entity_type_definition->hasKey('bundle')) {
+ $derivative['source']['bundle'] = $bundle;
+ }
+
+ // Set the process pipeline.
+ $id_key = $entity_type_definition->getKey('id');
+ $derivative['process'][$id_key] = $id_key;
+ if ($entity_type_definition->isRevisionable()) {
+ $revision_key = $entity_type_definition->getKey('revision');
+ $derivative['process'][$revision_key] = $revision_key;
+ }
+ if ($entity_type_definition->isTranslatable()) {
+ $langcode_key = $entity_type_definition->getKey('langcode');
+ $derivative['process'][$langcode_key] = $langcode_key;
+ }
+
+ // Set the destination plugin.
+ $derivative['destination']['plugin'] = 'entity' . PluginBase::DERIVATIVE_SEPARATOR . $entity_type;
+ if ($entity_type_definition->hasKey('bundle')) {
+ $derivative['destination']['default_bundle'] = $bundle;
+ }
+ if ($entity_type_definition->isTranslatable()) {
+ $derivative['destination']['translations'] = TRUE;
+ }
+
+ // Allow overwriting the entity reference field so we can update its
+ // values with the ones found in the mapping table.
+ $derivative['destination']['overwrite_properties'][$field_name] = $field_name;
+
+ // Add the entity reference field to the process pipeline.
+ $derivative['process'][$field_name] = [
+ 'plugin' => 'sub_process',
+ 'source' => $field_name,
+ 'process' => [
+ 'target_id' => [
+ [
+ 'plugin' => 'migration_lookup',
+ 'source' => 'target_id',
+ 'migration' => $base_plugin_definition['target_types'][$target_type],
+ 'no_stub' => TRUE,
+ ],
+ [
+ 'plugin' => 'skip_on_empty',
+ 'method' => 'row',
+ ],
+ [
+ 'plugin' => 'extract',
+ 'index' => [0],
+ ],
+ ],
+ ],
+ ];
+
+ if (!isset($this->derivatives[$derivative_key])) {
+ // If this is a new derivative, add it to the returned derivatives.
+ $this->derivatives[$derivative_key] = $derivative;
+ }
+ else {
+ // If this is an existing derivative, it means this bundle has more
+ // than one entity reference field. In that case, we only want to add
+ // the field to the process pipeline and make it overwritable.
+ $this->derivatives[$derivative_key]['process'] += $derivative['process'];
+ $this->derivatives[$derivative_key]['destination']['overwrite_properties'] += $derivative['destination']['overwrite_properties'];
+ }
+ }
+ }
+ }
+
+ return $this->derivatives;
+ }
+
+}