diff --git a/core/modules/migrate/tests/src/Kernel/Plugin/MigrationPluginListTest.php b/core/modules/migrate/tests/src/Kernel/Plugin/MigrationPluginListTest.php index 62f3d19ba31a08f26270fdbc9de3a1c246e8a8a1..3a3487f69acebab7e51a5820ba98d5de2ee33953 100644 --- a/core/modules/migrate/tests/src/Kernel/Plugin/MigrationPluginListTest.php +++ b/core/modules/migrate/tests/src/Kernel/Plugin/MigrationPluginListTest.php @@ -41,6 +41,7 @@ class MigrationPluginListTest extends KernelTestBase { 'menu_link_content', 'menu_ui', 'node', + 'options', 'path', 'search', 'shortcut', diff --git a/core/modules/migrate_drupal/config/install/migrate_drupal.settings.yml b/core/modules/migrate_drupal/config/install/migrate_drupal.settings.yml index d5a464e4eba5217a6495baf2f4701de457d3841c..d134dec393b7b1aea0e067ba3bc8199b353f53fe 100644 --- a/core/modules/migrate_drupal/config/install/migrate_drupal.settings.yml +++ b/core/modules/migrate_drupal/config/install/migrate_drupal.settings.yml @@ -3,3 +3,8 @@ enforce_source_module_tags: - Drupal 6 - Drupal 7 +# Migrations with any of these tags will not be derived and executed with the +# other migrations. They will be derived and executed after the migrations on +# which they depend have been successfully executed. +follow_up_migration_tags: + - Follow-up migration diff --git a/core/modules/migrate_drupal/config/schema/migrate_drupal.schema.yml b/core/modules/migrate_drupal/config/schema/migrate_drupal.schema.yml index e980081fc54bc190602b1071b69bbb6cd031d4c8..1b09e06ce604f7790bbd89115b3c4c65c4fc6c2b 100644 --- a/core/modules/migrate_drupal/config/schema/migrate_drupal.schema.yml +++ b/core/modules/migrate_drupal/config/schema/migrate_drupal.schema.yml @@ -8,3 +8,9 @@ migrate_drupal.settings: sequence: type: string label: 'Tag' + follow_up_migration_tags: + type: sequence + label: 'Follow-up migration tags' + sequence: + type: string + label: 'Tag' diff --git a/core/modules/migrate_drupal/migrate_drupal.install b/core/modules/migrate_drupal/migrate_drupal.install index 0ef88afd1c7d85219656b1c0576675ae49ce089f..ed60201f7e80d414087302967dbcfd0ec349db06 100644 --- a/core/modules/migrate_drupal/migrate_drupal.install +++ b/core/modules/migrate_drupal/migrate_drupal.install @@ -14,3 +14,13 @@ function migrate_drupal_update_8501() { ->set('enforce_source_module_tags', ['Drupal 6', 'Drupal 7']) ->save(); } + +/** + * Sets the follow-up migration tags. + */ +function migrate_drupal_update_8502() { + \Drupal::configFactory() + ->getEditable('migrate_drupal.settings') + ->set('follow_up_migration_tags', ['Follow-up migration']) + ->save(); +} diff --git a/core/modules/migrate_drupal/migrations/d6_entity_reference_translation.yml b/core/modules/migrate_drupal/migrations/d6_entity_reference_translation.yml new file mode 100644 index 0000000000000000000000000000000000000000..31888207392fcd80a9f28d044b128b5a5c95d114 --- /dev/null +++ b/core/modules/migrate_drupal/migrations/d6_entity_reference_translation.yml @@ -0,0 +1,22 @@ +id: d6_entity_reference_translation +label: Entity reference translations +migration_tags: + - Drupal 6 + - Follow-up migration +deriver: Drupal\migrate_drupal\Plugin\migrate\EntityReferenceTranslationDeriver +# Supported target types for entity reference translation migrations. The array +# keys are the supported target types and the values are arrays of migrations +# to lookup for the translated entity IDs. +target_types: + node: + - d6_node_translation +# The source plugin will be set by the deriver. +source: + plugin: empty + key: default + target: default +# The process pipeline will be set by the deriver. +process: [] +# The destination plugin will be set by the deriver. +destination: + plugin: null diff --git a/core/modules/migrate_drupal/migrations/d7_entity_reference_translation.yml b/core/modules/migrate_drupal/migrations/d7_entity_reference_translation.yml new file mode 100644 index 0000000000000000000000000000000000000000..7e59a9a0c75b7ea951394b92e75af55fa48e88c2 --- /dev/null +++ b/core/modules/migrate_drupal/migrations/d7_entity_reference_translation.yml @@ -0,0 +1,22 @@ +id: d7_entity_reference_translation +label: Entity reference translations +migration_tags: + - Drupal 7 + - Follow-up migration +deriver: Drupal\migrate_drupal\Plugin\migrate\EntityReferenceTranslationDeriver +# Supported target types for entity reference translation migrations. The array +# keys are the supported target types and the values are arrays of migrations +# to lookup for the translated entity IDs. +target_types: + node: + - d7_node_translation +# The source plugin will be set by the deriver. +source: + plugin: empty + key: default + target: default +# The process pipeline will be set by the deriver. +process: [] +# The destination plugin will be set by the deriver. +destination: + plugin: null diff --git a/core/modules/migrate_drupal/src/MigrationConfigurationTrait.php b/core/modules/migrate_drupal/src/MigrationConfigurationTrait.php index b566c821677f4b76c60e9ea7630473180f208a0c..0aca5be2448cdf08a64776b108dd8b4226007a2d 100644 --- a/core/modules/migrate_drupal/src/MigrationConfigurationTrait.php +++ b/core/modules/migrate_drupal/src/MigrationConfigurationTrait.php @@ -12,6 +12,13 @@ */ trait MigrationConfigurationTrait { + /** + * The follow-up migration tags. + * + * @var string[] + */ + protected $followUpMigrationTags; + /** * Gets the database connection for the source Drupal database. * @@ -96,6 +103,13 @@ protected function getMigrations($database_state_key, $drupal_version) { $all_migrations = $plugin_manager->createInstancesByTag($version_tag); $migrations = []; foreach ($all_migrations as $migration) { + // Skip migrations tagged with any of the follow-up migration tags. They + // will be derived and executed after the migrations on which they depend + // have been successfully executed. + // @see Drupal\migrate_drupal\Plugin\MigrationWithFollowUpInterface + if (!empty(array_intersect($migration->getMigrationTags(), $this->getFollowUpMigrationTags()))) { + continue; + } try { // @todo https://drupal.org/node/2681867 We should be able to validate // the entire migration at this point. @@ -119,6 +133,20 @@ protected function getMigrations($database_state_key, $drupal_version) { return $migrations; } + /** + * Returns the follow-up migration tags. + * + * @return string[] + */ + protected function getFollowUpMigrationTags() { + if ($this->followUpMigrationTags === NULL) { + $this->followUpMigrationTags = \Drupal::configFactory() + ->get('migrate_drupal.settings') + ->get('follow_up_migration_tags') ?: []; + } + return $this->followUpMigrationTags; + } + /** * Determines what version of Drupal the source database contains. * diff --git a/core/modules/migrate_drupal/src/Plugin/MigrationWithFollowUpInterface.php b/core/modules/migrate_drupal/src/Plugin/MigrationWithFollowUpInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..93ef8f91171a5bbd9c4309874b98db7ae26c6c96 --- /dev/null +++ b/core/modules/migrate_drupal/src/Plugin/MigrationWithFollowUpInterface.php @@ -0,0 +1,40 @@ +<?php + +namespace Drupal\migrate_drupal\Plugin; + +/** + * Interface for migrations with follow-up migrations. + * + * Some migrations need to be derived and executed after other migrations have + * been successfully executed. For example, a migration might need to be derived + * based on previously migrated data. For such a case, the migration dependency + * system is not enough since all migrations would still be derived before any + * one of them has been executed. + * + * Those "follow-up" migrations need to be tagged with the "Follow-up migration" + * tag (or any tag in the "follow_up_migration_tags" configuration) and thus + * they won't be derived with the other migrations. + * + * To get those follow-up migrations derived at the right time, the migrations + * on which they depend must implement this interface and generate them in the + * generateFollowUpMigrations() method. + * + * When the migrations implementing this interface have been successfully + * executed, the follow-up migrations will then be derived having access to the + * now migrated data. + */ +interface MigrationWithFollowUpInterface { + + /** + * Generates follow-up migrations. + * + * When the migration implementing this interface has been succesfully + * executed, this method will be used to generate the follow-up migrations + * which depends on the now migrated data. + * + * @return \Drupal\migrate\Plugin\MigrationInterface[] + * The follow-up migrations. + */ + public function generateFollowUpMigrations(); + +} diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/EntityReferenceTranslationDeriver.php b/core/modules/migrate_drupal/src/Plugin/migrate/EntityReferenceTranslationDeriver.php new file mode 100644 index 0000000000000000000000000000000000000000..d81b92cc10690d396a8fe79f7b0fcab4459e19fd --- /dev/null +++ b/core/modules/migrate_drupal/src/Plugin/migrate/EntityReferenceTranslationDeriver.php @@ -0,0 +1,189 @@ +<?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; + + // 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; + $derivative['source']['bundle'] = $bundle; + + // Set the process pipeline. + $entity_type_definition = $this->entityTypeManager->getDefinition($entity_type); + $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; + $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; + } + +} diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/field/NodeReference.php b/core/modules/migrate_drupal/src/Plugin/migrate/field/NodeReference.php index e8c2dd5e6de4d84f3af0f76d9b80a814924e5359..d8b756b916bad879e62a4b5961214d311920e607 100644 --- a/core/modules/migrate_drupal/src/Plugin/migrate/field/NodeReference.php +++ b/core/modules/migrate_drupal/src/Plugin/migrate/field/NodeReference.php @@ -26,8 +26,7 @@ public function processFieldValues(MigrationInterface $migration, $field_name, $ 'source' => $field_name, 'process' => [ 'target_id' => [ - 'plugin' => 'migration_lookup', - 'migration' => 'd6_node', + 'plugin' => 'get', 'source' => 'nid', ], ], diff --git a/core/modules/migrate_drupal/tests/fixtures/drupal6.php b/core/modules/migrate_drupal/tests/fixtures/drupal6.php index 4d4e18be489a55017b2d97d465794ae4bae58d16..7b0eba3e03644e2ac6f75194e526768e793c4ce5 100644 --- a/core/modules/migrate_drupal/tests/fixtures/drupal6.php +++ b/core/modules/migrate_drupal/tests/fixtures/drupal6.php @@ -2752,6 +2752,30 @@ 'active' => '1', 'locked' => '0', )) +->values(array( + 'field_name' => 'field_reference', + 'type' => 'nodereference', + 'global_settings' => 'a:1:{s:19:"referenceable_types";a:11:{s:4:"page";s:4:"page";s:7:"article";i:0;s:7:"company";i:0;s:8:"employee";i:0;s:5:"forum";i:0;s:10:"test_event";i:0;s:9:"test_page";i:0;s:11:"test_planet";i:0;s:10:"test_story";i:0;s:7:"sponsor";i:0;s:5:"story";i:0;}}', + 'required' => '0', + 'multiple' => '0', + 'db_storage' => '1', + 'module' => 'nodereference', + 'db_columns' => 'a:1:{s:3:"nid";a:4:{s:4:"type";s:3:"int";s:8:"unsigned";b:1;s:8:"not null";b:0;s:5:"index";b:1;}}', + 'active' => '1', + 'locked' => '0', +)) +->values(array( + 'field_name' => 'field_reference_2', + 'type' => 'nodereference', + 'global_settings' => 'a:1:{s:19:"referenceable_types";a:11:{s:4:"page";s:4:"page";s:7:"article";i:0;s:7:"company";i:0;s:8:"employee";i:0;s:5:"forum";i:0;s:10:"test_event";i:0;s:9:"test_page";i:0;s:11:"test_planet";i:0;s:10:"test_story";i:0;s:7:"sponsor";i:0;s:5:"story";i:0;}}', + 'required' => '0', + 'multiple' => '0', + 'db_storage' => '1', + 'module' => 'nodereference', + 'db_columns' => 'a:1:{s:3:"nid";a:4:{s:4:"type";s:3:"int";s:8:"unsigned";b:1;s:8:"not null";b:0;s:5:"index";b:1;}}', + 'active' => '1', + 'locked' => '0', +)) ->values(array( 'field_name' => 'field_test', 'type' => 'text', @@ -3134,6 +3158,30 @@ 'widget_module' => 'number', 'widget_active' => '1', )) +->values(array( + 'field_name' => 'field_reference', + 'type_name' => 'page', + 'weight' => '31', + 'label' => 'Reference', + 'widget_type' => 'nodereference_select', + 'widget_settings' => 'a:4:{s:18:"autocomplete_match";s:8:"contains";s:4:"size";i:60;s:13:"default_value";a:1:{i:0;a:1:{s:3:"nid";s:0:"";}}s:17:"default_value_php";N;}', + 'display_settings' => 'a:5:{s:5:"label";a:2:{s:6:"format";s:5:"above";s:7:"exclude";i:0;}i:5;a:2:{s:6:"format";s:7:"default";s:7:"exclude";i:0;}s:6:"teaser";a:2:{s:6:"format";s:7:"default";s:7:"exclude";i:0;}s:4:"full";a:2:{s:6:"format";s:7:"default";s:7:"exclude";i:0;}i:4;a:2:{s:6:"format";s:7:"default";s:7:"exclude";i:0;}}', + 'description' => '', + 'widget_module' => 'nodereference', + 'widget_active' => '1', +)) +->values(array( + 'field_name' => 'field_reference_2', + 'type_name' => 'page', + 'weight' => '32', + 'label' => 'Reference', + 'widget_type' => 'nodereference_select', + 'widget_settings' => 'a:4:{s:18:"autocomplete_match";s:8:"contains";s:4:"size";i:60;s:13:"default_value";a:1:{i:0;a:1:{s:3:"nid";s:0:"";}}s:17:"default_value_php";N;}', + 'display_settings' => 'a:5:{s:5:"label";a:2:{s:6:"format";s:5:"above";s:7:"exclude";i:0;}i:5;a:2:{s:6:"format";s:7:"default";s:7:"exclude";i:0;}s:6:"teaser";a:2:{s:6:"format";s:7:"default";s:7:"exclude";i:0;}s:4:"full";a:2:{s:6:"format";s:7:"default";s:7:"exclude";i:0;}i:4;a:2:{s:6:"format";s:7:"default";s:7:"exclude";i:0;}}', + 'description' => '', + 'widget_module' => 'nodereference', + 'widget_active' => '1', +)) ->values(array( 'field_name' => 'field_test', 'type_name' => 'story', @@ -3493,13 +3541,71 @@ 'not null' => FALSE, 'size' => 'normal', ), + 'field_reference_nid' => array( + 'type' => 'int', + 'not null' => FALSE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'field_reference_2_nid' => array( + 'type' => 'int', + 'not null' => FALSE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), ), 'primary key' => array( 'vid', ), + 'indexes' => array( + 'field_reference_nid' => array( + 'field_reference_nid', + ), + 'field_reference_2_nid' => array( + 'field_reference_2_nid', + ), + ), 'mysql_character_set' => 'utf8', )); +$connection->insert('content_type_page') +->fields(array( + 'vid', + 'nid', + 'field_text_field_value', + 'field_reference_nid', + 'field_reference_2_nid', +)) +->values(array( + 'vid' => '13', + 'nid' => '10', + 'field_text_field_value' => NULL, + 'field_reference_nid' => '13', + 'field_reference_2_nid' => '13', +)) +->values(array( + 'vid' => '14', + 'nid' => '11', + 'field_text_field_value' => NULL, + 'field_reference_nid' => '20', + 'field_reference_2_nid' => '20', +)) +->values(array( + 'vid' => '16', + 'nid' => '13', + 'field_text_field_value' => NULL, + 'field_reference_nid' => '10', + 'field_reference_2_nid' => '10', +)) +->values(array( + 'vid' => '23', + 'nid' => '20', + 'field_text_field_value' => NULL, + 'field_reference_nid' => '11', + 'field_reference_2_nid' => '11', +)) +->execute(); + $connection->schema()->createTable('content_type_story', array( 'fields' => array( 'nid' => array( @@ -43548,6 +43654,23 @@ 'tnid' => '0', 'translate' => '0', )) +->values(array( + 'nid' => '20', + 'vid' => '23', + 'type' => 'page', + 'language' => 'fr', + 'title' => 'Le peuple zoulou', + 'uid' => '1', + 'status' => '1', + 'created' => '1520613038', + 'changed' => '1520613305', + 'comment' => '0', + 'promote' => '1', + 'moderate' => '0', + 'sticky' => '0', + 'tnid' => '12', + 'translate' => '0', +)) ->execute(); $connection->schema()->createTable('node_access', array( @@ -44206,6 +44329,17 @@ 'timestamp' => '1501955771', 'format' => '1', )) +->values(array( + 'nid' => '20', + 'vid' => '23', + 'uid' => '1', + 'title' => 'Le peuple zoulou', + 'body' => 'Le peuple zoulou.', + 'teaser' => 'Le peuple zoulou.', + 'log' => '', + 'timestamp' => '1520613305', + 'format' => '1', +)) ->execute(); $connection->schema()->createTable('node_type', array( @@ -44472,7 +44606,7 @@ 'custom' => '1', 'modified' => '1', 'locked' => '0', - 'orig_type' => 'page', + 'orig_type' => 'test_page', )) ->values(array( 'type' => 'test_planet', diff --git a/core/modules/migrate_drupal/tests/fixtures/drupal7.php b/core/modules/migrate_drupal/tests/fixtures/drupal7.php index 6f61f67dcdd5e78444568e4de0827b1735d2d2fd..d77940e486f798177ba1dc702ef7e4d652239fbe 100644 --- a/core/modules/migrate_drupal/tests/fixtures/drupal7.php +++ b/core/modules/migrate_drupal/tests/fixtures/drupal7.php @@ -3649,6 +3649,36 @@ 'translatable' => '0', 'deleted' => '0', )) +->values(array( + 'id' => '38', + 'field_name' => 'field_reference', + 'type' => 'entityreference', + 'module' => 'entityreference', + 'active' => '1', + 'storage_type' => 'field_sql_storage', + 'storage_module' => 'field_sql_storage', + 'storage_active' => '1', + 'locked' => '0', + 'data' => 'a:7:{s:12:"translatable";s:1:"0";s:12:"entity_types";a:0:{}s:8:"settings";a:3:{s:11:"target_type";s:4:"node";s:7:"handler";s:4:"base";s:16:"handler_settings";a:2:{s:14:"target_bundles";a:1:{s:7:"article";s:7:"article";}s:4:"sort";a:1:{s:4:"type";s:4:"none";}}}s:7:"storage";a:5:{s:4:"type";s:17:"field_sql_storage";s:8:"settings";a:0:{}s:6:"module";s:17:"field_sql_storage";s:6:"active";s:1:"1";s:7:"details";a:1:{s:3:"sql";a:2:{s:18:"FIELD_LOAD_CURRENT";a:1:{s:26:"field_data_field_reference";a:1:{s:9:"target_id";s:25:"field_reference_target_id";}}s:19:"FIELD_LOAD_REVISION";a:1:{s:30:"field_revision_field_reference";a:1:{s:9:"target_id";s:25:"field_reference_target_id";}}}}}s:12:"foreign keys";a:1:{s:4:"node";a:2:{s:5:"table";s:4:"node";s:7:"columns";a:1:{s:9:"target_id";s:3:"nid";}}}s:7:"indexes";a:1:{s:9:"target_id";a:1:{i:0;s:9:"target_id";}}s:2:"id";s:2:"37";}', + 'cardinality' => '1', + 'translatable' => '0', + 'deleted' => '0', +)) +->values(array( + 'id' => '39', + 'field_name' => 'field_reference_2', + 'type' => 'entityreference', + 'module' => 'entityreference', + 'active' => '1', + 'storage_type' => 'field_sql_storage', + 'storage_module' => 'field_sql_storage', + 'storage_active' => '1', + 'locked' => '0', + 'data' => 'a:7:{s:12:"translatable";s:1:"0";s:12:"entity_types";a:0:{}s:8:"settings";a:3:{s:11:"target_type";s:4:"node";s:7:"handler";s:4:"base";s:16:"handler_settings";a:2:{s:14:"target_bundles";a:1:{s:7:"article";s:7:"article";}s:4:"sort";a:1:{s:4:"type";s:4:"none";}}}s:7:"storage";a:5:{s:4:"type";s:17:"field_sql_storage";s:8:"settings";a:0:{}s:6:"module";s:17:"field_sql_storage";s:6:"active";s:1:"1";s:7:"details";a:1:{s:3:"sql";a:2:{s:18:"FIELD_LOAD_CURRENT";a:1:{s:28:"field_data_field_reference_2";a:1:{s:9:"target_id";s:27:"field_reference_2_target_id";}}s:19:"FIELD_LOAD_REVISION";a:1:{s:32:"field_revision_field_reference_2";a:1:{s:9:"target_id";s:27:"field_reference_2_target_id";}}}}}s:12:"foreign keys";a:1:{s:4:"node";a:2:{s:5:"table";s:4:"node";s:7:"columns";a:1:{s:9:"target_id";s:3:"nid";}}}s:7:"indexes";a:1:{s:9:"target_id";a:1:{i:0;s:9:"target_id";}}s:2:"id";s:2:"39";}', + 'cardinality' => '1', + 'translatable' => '0', + 'deleted' => '0', +)) ->execute(); $connection->schema()->createTable('field_config_instance', array( @@ -4258,6 +4288,24 @@ 'data' => 'a:7:{s:5:"label";s:10:"Float List";s:6:"widget";a:5:{s:6:"weight";s:2:"20";s:4:"type";s:14:"options_select";s:6:"module";s:7:"options";s:6:"active";i:1;s:8:"settings";a:0:{}}s:8:"settings";a:1:{s:18:"user_register_form";b:0;}s:7:"display";a:1:{s:7:"default";a:5:{s:5:"label";s:5:"above";s:4:"type";s:12:"list_default";s:8:"settings";a:0:{}s:6:"module";s:4:"list";s:6:"weight";i:19;}}s:8:"required";i:0;s:11:"description";s:0:"";s:13:"default_value";N;}', 'deleted' => '0', )) +->values(array( + 'id' => '64', + 'field_id' => '38', + 'field_name' => 'field_reference', + 'entity_type' => 'node', + 'bundle' => 'article', + 'data' => 'a:7:{s:5:"label";s:9:"Reference";s:6:"widget";a:5:{s:6:"weight";s:2:"20";s:4:"type";s:14:"options_select";s:6:"module";s:7:"options";s:6:"active";i:1;s:8:"settings";a:0:{}}s:8:"settings";a:1:{s:18:"user_register_form";b:0;}s:7:"display";a:1:{s:7:"default";a:5:{s:5:"label";s:5:"above";s:4:"type";s:21:"entityreference_label";s:6:"weight";s:2:"20";s:8:"settings";a:2:{s:13:"bypass_access";i:0;s:4:"link";i:1;}s:6:"module";s:15:"entityreference";}}s:8:"required";i:0;s:11:"description";s:0:"";s:13:"default_value";N;}', + 'deleted' => '0', +)) +->values(array( + 'id' => '65', + 'field_id' => '39', + 'field_name' => 'field_reference_2', + 'entity_type' => 'node', + 'bundle' => 'article', + 'data' => 'a:6:{s:5:"label";s:11:"Reference 2";s:6:"widget";a:4:{s:4:"type";s:14:"options_select";s:6:"weight";s:2:"21";s:8:"settings";a:0:{}s:6:"module";s:7:"options";}s:8:"settings";a:1:{s:18:"user_register_form";b:0;}s:7:"display";a:1:{s:7:"default";a:5:{s:5:"label";s:5:"above";s:4:"type";s:21:"entityreference_label";s:8:"settings";a:2:{s:4:"link";b:0;s:13:"bypass_access";b:0;}s:6:"module";s:15:"entityreference";s:6:"weight";i:21;}}s:8:"required";b:0;s:11:"description";s:0:"";}', + 'deleted' => '0', +)) ->execute(); $connection->schema()->createTable('field_data_body', array( @@ -6165,6 +6213,280 @@ )) ->execute(); +$connection->schema()->createTable('field_data_field_reference', array( + 'fields' => array( + 'entity_type' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '128', + 'default' => '', + ), + 'bundle' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '128', + 'default' => '', + ), + 'deleted' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'tiny', + 'default' => '0', + ), + 'entity_id' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'revision_id' => array( + 'type' => 'int', + 'not null' => FALSE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'language' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '32', + 'default' => '', + ), + 'delta' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'field_reference_target_id' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + ), + 'primary key' => array( + 'entity_type', + 'entity_id', + 'deleted', + 'delta', + 'language', + ), + 'indexes' => array( + 'entity_type' => array( + 'entity_type', + ), + 'bundle' => array( + 'bundle', + ), + 'deleted' => array( + 'deleted', + ), + 'entity_id' => array( + 'entity_id', + ), + 'revision_id' => array( + 'revision_id', + ), + 'language' => array( + 'language', + ), + 'field_reference_target_id' => array( + 'field_reference_target_id', + ), + ), + 'mysql_character_set' => 'utf8', +)); + +$connection->insert('field_data_field_reference') +->fields(array( + 'entity_type', + 'bundle', + 'deleted', + 'entity_id', + 'revision_id', + 'language', + 'delta', + 'field_reference_target_id', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '2', + 'revision_id' => '2', + 'language' => 'und', + 'delta' => '0', + 'field_reference_target_id' => '5', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '3', + 'revision_id' => '3', + 'language' => 'und', + 'delta' => '0', + 'field_reference_target_id' => '4', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '4', + 'revision_id' => '4', + 'language' => 'und', + 'delta' => '0', + 'field_reference_target_id' => '3', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '5', + 'revision_id' => '5', + 'language' => 'und', + 'delta' => '0', + 'field_reference_target_id' => '2', +)) +->execute(); + +$connection->schema()->createTable('field_data_field_reference_2', array( + 'fields' => array( + 'entity_type' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '128', + 'default' => '', + ), + 'bundle' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '128', + 'default' => '', + ), + 'deleted' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'tiny', + 'default' => '0', + ), + 'entity_id' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'revision_id' => array( + 'type' => 'int', + 'not null' => FALSE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'language' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '32', + 'default' => '', + ), + 'delta' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'field_reference_2_target_id' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + ), + 'primary key' => array( + 'entity_type', + 'entity_id', + 'deleted', + 'delta', + 'language', + ), + 'indexes' => array( + 'entity_type' => array( + 'entity_type', + ), + 'bundle' => array( + 'bundle', + ), + 'deleted' => array( + 'deleted', + ), + 'entity_id' => array( + 'entity_id', + ), + 'revision_id' => array( + 'revision_id', + ), + 'language' => array( + 'language', + ), + 'field_reference_2_target_id' => array( + 'field_reference_2_target_id', + ), + ), + 'mysql_character_set' => 'utf8', +)); + +$connection->insert('field_data_field_reference_2') +->fields(array( + 'entity_type', + 'bundle', + 'deleted', + 'entity_id', + 'revision_id', + 'language', + 'delta', + 'field_reference_2_target_id', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '2', + 'revision_id' => '2', + 'language' => 'und', + 'delta' => '0', + 'field_reference_2_target_id' => '5', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '3', + 'revision_id' => '3', + 'language' => 'und', + 'delta' => '0', + 'field_reference_2_target_id' => '4', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '4', + 'revision_id' => '4', + 'language' => 'und', + 'delta' => '0', + 'field_reference_2_target_id' => '3', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '5', + 'revision_id' => '5', + 'language' => 'und', + 'delta' => '0', + 'field_reference_2_target_id' => '2', +)) +->execute(); + $connection->schema()->createTable('field_data_field_tags', array( 'fields' => array( 'entity_type' => array( @@ -9650,6 +9972,282 @@ )) ->execute(); +$connection->schema()->createTable('field_revision_field_reference', array( + 'fields' => array( + 'entity_type' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '128', + 'default' => '', + ), + 'bundle' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '128', + 'default' => '', + ), + 'deleted' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'tiny', + 'default' => '0', + ), + 'entity_id' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'revision_id' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'language' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '32', + 'default' => '', + ), + 'delta' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'field_reference_target_id' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + ), + 'primary key' => array( + 'entity_type', + 'entity_id', + 'revision_id', + 'deleted', + 'delta', + 'language', + ), + 'indexes' => array( + 'entity_type' => array( + 'entity_type', + ), + 'bundle' => array( + 'bundle', + ), + 'deleted' => array( + 'deleted', + ), + 'entity_id' => array( + 'entity_id', + ), + 'revision_id' => array( + 'revision_id', + ), + 'language' => array( + 'language', + ), + 'field_reference_target_id' => array( + 'field_reference_target_id', + ), + ), + 'mysql_character_set' => 'utf8', +)); + +$connection->insert('field_revision_field_reference') +->fields(array( + 'entity_type', + 'bundle', + 'deleted', + 'entity_id', + 'revision_id', + 'language', + 'delta', + 'field_reference_target_id', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '2', + 'revision_id' => '2', + 'language' => 'und', + 'delta' => '0', + 'field_reference_target_id' => '5', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '3', + 'revision_id' => '3', + 'language' => 'und', + 'delta' => '0', + 'field_reference_target_id' => '4', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '4', + 'revision_id' => '4', + 'language' => 'und', + 'delta' => '0', + 'field_reference_target_id' => '3', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '5', + 'revision_id' => '5', + 'language' => 'und', + 'delta' => '0', + 'field_reference_target_id' => '2', +)) +->execute(); + +$connection->schema()->createTable('field_revision_field_reference_2', array( + 'fields' => array( + 'entity_type' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '128', + 'default' => '', + ), + 'bundle' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '128', + 'default' => '', + ), + 'deleted' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'tiny', + 'default' => '0', + ), + 'entity_id' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'revision_id' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'language' => array( + 'type' => 'varchar', + 'not null' => TRUE, + 'length' => '32', + 'default' => '', + ), + 'delta' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + 'field_reference_2_target_id' => array( + 'type' => 'int', + 'not null' => TRUE, + 'size' => 'normal', + 'unsigned' => TRUE, + ), + ), + 'primary key' => array( + 'entity_type', + 'entity_id', + 'revision_id', + 'deleted', + 'delta', + 'language', + ), + 'indexes' => array( + 'entity_type' => array( + 'entity_type', + ), + 'bundle' => array( + 'bundle', + ), + 'deleted' => array( + 'deleted', + ), + 'entity_id' => array( + 'entity_id', + ), + 'revision_id' => array( + 'revision_id', + ), + 'language' => array( + 'language', + ), + 'field_reference_2_target_id' => array( + 'field_reference_2_target_id', + ), + ), + 'mysql_character_set' => 'utf8', +)); + +$connection->insert('field_revision_field_reference_2') +->fields(array( + 'entity_type', + 'bundle', + 'deleted', + 'entity_id', + 'revision_id', + 'language', + 'delta', + 'field_reference_2_target_id', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '2', + 'revision_id' => '2', + 'language' => 'und', + 'delta' => '0', + 'field_reference_2_target_id' => '5', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '3', + 'revision_id' => '3', + 'language' => 'und', + 'delta' => '0', + 'field_reference_2_target_id' => '4', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '4', + 'revision_id' => '4', + 'language' => 'und', + 'delta' => '0', + 'field_reference_2_target_id' => '3', +)) +->values(array( + 'entity_type' => 'node', + 'bundle' => 'article', + 'deleted' => '0', + 'entity_id' => '5', + 'revision_id' => '5', + 'language' => 'und', + 'delta' => '0', + 'field_reference_2_target_id' => '2', +)) +->execute(); + $connection->schema()->createTable('field_revision_field_tags', array( 'fields' => array( 'entity_type' => array( diff --git a/core/modules/migrate_drupal/tests/src/Kernel/d6/FollowUpMigrationsTest.php b/core/modules/migrate_drupal/tests/src/Kernel/d6/FollowUpMigrationsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e02254a5e50d27fd7d12471d442202a5b3d15b1b --- /dev/null +++ b/core/modules/migrate_drupal/tests/src/Kernel/d6/FollowUpMigrationsTest.php @@ -0,0 +1,78 @@ +<?php + +namespace Drupal\Tests\migrate_drupal\Kernel\d6; + +use Drupal\node\Entity\Node; +use Drupal\Tests\node\Kernel\Migrate\d6\MigrateNodeTestBase; + +/** + * Tests follow-up migrations. + * + * @group migrate_drupal + */ +class FollowUpMigrationsTest extends MigrateNodeTestBase { + + /** + * {@inheritdoc} + */ + public static $modules = [ + 'content_translation', + 'language', + 'menu_ui', + ]; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + $this->executeMigrations([ + 'language', + 'd6_language_content_settings', + 'd6_node', + 'd6_node_translation', + ]); + } + + /** + * Test entity reference translations. + */ + public function testEntityReferenceTranslations() { + // Test the entity reference field before the follow-up migrations. + $node = Node::load(10); + $this->assertSame('13', $node->get('field_reference')->target_id); + $this->assertSame('13', $node->get('field_reference_2')->target_id); + $translation = $node->getTranslation('fr'); + $this->assertSame('20', $translation->get('field_reference')->target_id); + $this->assertSame('20', $translation->get('field_reference_2')->target_id); + + $node = Node::load(12)->getTranslation('en'); + $this->assertSame('10', $node->get('field_reference')->target_id); + $this->assertSame('10', $node->get('field_reference_2')->target_id); + $translation = $node->getTranslation('fr'); + $this->assertSame('11', $translation->get('field_reference')->target_id); + $this->assertSame('11', $translation->get('field_reference_2')->target_id); + + // Run the follow-up migrations. + $migration_plugin_manager = $this->container->get('plugin.manager.migration'); + $migration_plugin_manager->clearCachedDefinitions(); + $follow_up_migrations = $migration_plugin_manager->createInstances('d6_entity_reference_translation'); + $this->executeMigrations(array_keys($follow_up_migrations)); + + // Test the entity reference field after the follow-up migrations. + $node = Node::load(10); + $this->assertSame('12', $node->get('field_reference')->target_id); + $this->assertSame('12', $node->get('field_reference_2')->target_id); + $translation = $node->getTranslation('fr'); + $this->assertSame('12', $translation->get('field_reference')->target_id); + $this->assertSame('12', $translation->get('field_reference_2')->target_id); + + $node = Node::load(12)->getTranslation('en'); + $this->assertSame('10', $node->get('field_reference')->target_id); + $this->assertSame('10', $node->get('field_reference_2')->target_id); + $translation = $node->getTranslation('fr'); + $this->assertSame('10', $translation->get('field_reference')->target_id); + $this->assertSame('10', $translation->get('field_reference_2')->target_id); + } + +} diff --git a/core/modules/migrate_drupal/tests/src/Kernel/d7/FollowUpMigrationsTest.php b/core/modules/migrate_drupal/tests/src/Kernel/d7/FollowUpMigrationsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..96841937c8dee7dce0dfc43c3446c318098f9e5f --- /dev/null +++ b/core/modules/migrate_drupal/tests/src/Kernel/d7/FollowUpMigrationsTest.php @@ -0,0 +1,105 @@ +<?php + +namespace Drupal\Tests\migrate_drupal\Kernel\d7; + +use Drupal\node\Entity\Node; +use Drupal\Tests\file\Kernel\Migrate\d7\FileMigrationSetupTrait; + +/** + * Tests follow-up migrations. + * + * @group migrate_drupal + */ +class FollowUpMigrationsTest extends MigrateDrupal7TestBase { + + use FileMigrationSetupTrait; + + /** + * {@inheritdoc} + */ + public static $modules = [ + 'content_translation', + 'comment', + 'datetime', + 'file', + 'image', + 'language', + 'link', + 'menu_ui', + 'node', + 'taxonomy', + 'telephone', + 'text', + ]; + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + $this->fileMigrationSetup(); + + $this->installEntitySchema('node'); + $this->installEntitySchema('comment'); + $this->installEntitySchema('taxonomy_term'); + $this->installConfig(static::$modules); + $this->installSchema('node', ['node_access']); + + $this->executeMigrations([ + 'language', + 'd7_user_role', + 'd7_user', + 'd7_node_type', + 'd7_language_content_settings', + 'd7_comment_type', + 'd7_taxonomy_vocabulary', + 'd7_field', + 'd7_field_instance', + 'd7_node', + 'd7_node_translation', + ]); + } + + /** + * Test entity reference translations. + */ + public function testEntityReferenceTranslations() { + // Test the entity reference field before the follow-up migrations. + $node = Node::load(2); + $this->assertSame('5', $node->get('field_reference')->target_id); + $this->assertSame('5', $node->get('field_reference_2')->target_id); + $translation = $node->getTranslation('is'); + $this->assertSame('4', $translation->get('field_reference')->target_id); + $this->assertSame('4', $translation->get('field_reference_2')->target_id); + + $node = Node::load(4); + $this->assertSame('3', $node->get('field_reference')->target_id); + $this->assertSame('3', $node->get('field_reference_2')->target_id); + $translation = $node->getTranslation('en'); + $this->assertSame('2', $translation->get('field_reference')->target_id); + $this->assertSame('2', $translation->get('field_reference_2')->target_id); + + // Run the follow-up migrations. + $migration_plugin_manager = $this->container->get('plugin.manager.migration'); + $migration_plugin_manager->clearCachedDefinitions(); + $follow_up_migrations = $migration_plugin_manager->createInstances('d7_entity_reference_translation'); + $this->executeMigrations(array_keys($follow_up_migrations)); + + // Test the entity reference field after the follow-up migrations. + $node = Node::load(2); + $this->assertSame('4', $node->get('field_reference')->target_id); + $this->assertSame('4', $node->get('field_reference_2')->target_id); + $translation = $node->getTranslation('is'); + $this->assertSame('4', $translation->get('field_reference')->target_id); + $this->assertSame('4', $translation->get('field_reference_2')->target_id); + + $node = Node::load(4); + $this->assertSame('2', $node->get('field_reference')->target_id); + $this->assertSame('2', $node->get('field_reference_2')->target_id); + $translation = $node->getTranslation('en'); + $this->assertSame('2', $translation->get('field_reference')->target_id); + $this->assertSame('2', $translation->get('field_reference_2')->target_id); + } + +} diff --git a/core/modules/migrate_drupal_ui/src/Batch/MigrateUpgradeImportBatch.php b/core/modules/migrate_drupal_ui/src/Batch/MigrateUpgradeImportBatch.php index 12dc5c1c89229f8c45612420f3b204c3db8a457e..7145b8d2493046822f5c68e8dbb15882400cc323 100644 --- a/core/modules/migrate_drupal_ui/src/Batch/MigrateUpgradeImportBatch.php +++ b/core/modules/migrate_drupal_ui/src/Batch/MigrateUpgradeImportBatch.php @@ -9,11 +9,13 @@ use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Event\MigrateEvents; use Drupal\migrate\Event\MigrateIdMapMessageEvent; +use Drupal\migrate\Event\MigrateImportEvent; use Drupal\migrate\Event\MigrateMapDeleteEvent; use Drupal\migrate\Event\MigrateMapSaveEvent; use Drupal\migrate\Event\MigratePostRowSaveEvent; use Drupal\migrate\Event\MigrateRowDeleteEvent; use Drupal\migrate\MigrateExecutable; +use Drupal\migrate_drupal\Plugin\MigrationWithFollowUpInterface; /** * Runs a single migration batch. @@ -55,6 +57,13 @@ class MigrateUpgradeImportBatch { */ protected static $messages; + /** + * The follow-up migrations. + * + * @var \Drupal\migrate\Plugin\MigrationInterface[]; + */ + protected static $followUpMigrations; + /** * Runs a single migrate batch import. * @@ -69,6 +78,7 @@ public static function run($initial_ids, $config, &$context) { if (!static::$listenersAdded) { $event_dispatcher = \Drupal::service('event_dispatcher'); $event_dispatcher->addListener(MigrateEvents::POST_ROW_SAVE, [static::class, 'onPostRowSave']); + $event_dispatcher->addListener(MigrateEvents::POST_IMPORT, [static::class, 'onPostImport']); $event_dispatcher->addListener(MigrateEvents::MAP_SAVE, [static::class, 'onMapSave']); $event_dispatcher->addListener(MigrateEvents::IDMAP_MESSAGE, [static::class, 'onIdMapMessage']); @@ -138,6 +148,25 @@ public static function run($initial_ids, $config, &$context) { \Drupal::logger('migrate_drupal_ui')->notice($message); $context['sandbox']['num_processed'] = 0; $context['results']['successes']++; + + // If the completed migration has any follow-up migrations, add them + // to the batch migrations. + // @see onPostImport() + if (!empty(static::$followUpMigrations)) { + foreach (static::$followUpMigrations as $migration_id => $migration) { + if (!in_array($migration_id, $context['sandbox']['migration_ids'], TRUE)) { + // Add the follow-up migration ID to the batch migration IDs for + // later execution. + $context['sandbox']['migration_ids'][] = $migration_id; + // Increase the number of migrations in the batch to update the + // progress bar and keep it accurate. + $context['sandbox']['max']++; + // Unset the follow-up migration to make sure it won't get added + // to the batch twice. + unset(static::$followUpMigrations[$migration_id]); + } + } + } break; case MigrationInterface::RESULT_INCOMPLETE: @@ -262,6 +291,24 @@ public static function onPostRowSave(MigratePostRowSaveEvent $event) { } } + /** + * Adds follow-up migrations. + * + * @param \Drupal\migrate\Event\MigrateImportEvent $event + * The import event. + */ + public static function onPostImport(MigrateImportEvent $event) { + $migration = $event->getMigration(); + if ($migration instanceof MigrationWithFollowUpInterface) { + // After the migration on which they depend has been successfully + // executed, the follow-up migrations are immediately added to the batch + // and removed from the $followUpMigrations property. This means that the + // $followUpMigrations property is always empty at this point and it's OK + // to override it with the next follow-up migrations. + static::$followUpMigrations = $migration->generateFollowUpMigrations(); + } + } + /** * Reacts to item deletion. * diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeExecuteTestBase.php b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeExecuteTestBase.php index 6cb10e4f9bd843d51a502c73ceba1a804efbbdee..22ec01c0ebd8b352a9c2bab9d83d5812987bfdef 100644 --- a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeExecuteTestBase.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeExecuteTestBase.php @@ -2,7 +2,6 @@ namespace Drupal\Tests\migrate_drupal_ui\Functional; -use Drupal\migrate_drupal\MigrationConfigurationTrait; use Drupal\Tests\migrate_drupal\Traits\CreateTestContentEntitiesTrait; /** @@ -10,7 +9,6 @@ */ abstract class MigrateUpgradeExecuteTestBase extends MigrateUpgradeTestBase { - use MigrationConfigurationTrait; use CreateTestContentEntitiesTrait; /** diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeReviewPageTestBase.php b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeReviewPageTestBase.php index af2916a631434512e6aca2dee6cccc1afd9021a8..54fb0ba5f34ca2be4e365688e98d0d3fcfc10f65 100644 --- a/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeReviewPageTestBase.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/MigrateUpgradeReviewPageTestBase.php @@ -2,7 +2,6 @@ namespace Drupal\Tests\migrate_drupal_ui\Functional; -use Drupal\migrate_drupal\MigrationConfigurationTrait; use Drupal\Tests\migrate_drupal\Traits\CreateTestContentEntitiesTrait; /** @@ -10,7 +9,6 @@ */ abstract class MigrateUpgradeReviewPageTestBase extends MigrateUpgradeTestBase { - use MigrationConfigurationTrait; use CreateTestContentEntitiesTrait; /** diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php index f32e60a61057506de77ec6a8cc7fdf0abb1a8833..2468cd47448de11f2e7ec72fc645e78d3b5c806b 100644 --- a/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\migrate_drupal_ui\Functional\d6; +use Drupal\node\Entity\Node; use Drupal\Tests\migrate_drupal_ui\Functional\MigrateUpgradeExecuteTestBase; use Drupal\user\Entity\User; @@ -63,8 +64,8 @@ protected function getEntityCounts() { 'contact_form' => 5, 'configurable_language' => 5, 'editor' => 2, - 'field_config' => 86, - 'field_storage_config' => 60, + 'field_config' => 88, + 'field_storage_config' => 62, 'file' => 8, 'filter_format' => 7, 'image_style' => 5, @@ -90,7 +91,7 @@ protected function getEntityCounts() { 'date_format' => 11, 'entity_form_display' => 29, 'entity_form_mode' => 1, - 'entity_view_display' => 53, + 'entity_view_display' => 55, 'entity_view_mode' => 14, 'base_field_override' => 38, ]; @@ -103,7 +104,7 @@ protected function getEntityCountsIncremental() { $counts = $this->getEntityCounts(); $counts['block_content'] = 3; $counts['comment'] = 7; - $counts['entity_view_display'] = 53; + $counts['entity_view_display'] = 55; $counts['entity_view_mode'] = 14; $counts['file'] = 9; $counts['menu_link_content'] = 11; @@ -192,6 +193,26 @@ public function testMigrateUpgradeExecute() { $user = User::load(2); $user->passRaw = 'john.doe_pass'; $this->drupalLogin($user); + $this->assertFollowUpMigrationResults(); + } + + /** + * Tests that follow-up migrations have been run successfully. + */ + protected function assertFollowUpMigrationResults() { + $node = Node::load(10); + $this->assertSame('12', $node->get('field_reference')->target_id); + $this->assertSame('12', $node->get('field_reference_2')->target_id); + $translation = $node->getTranslation('fr'); + $this->assertSame('12', $translation->get('field_reference')->target_id); + $this->assertSame('12', $translation->get('field_reference_2')->target_id); + + $node = Node::load(12)->getTranslation('en'); + $this->assertSame('10', $node->get('field_reference')->target_id); + $this->assertSame('10', $node->get('field_reference_2')->target_id); + $translation = $node->getTranslation('fr'); + $this->assertSame('10', $translation->get('field_reference')->target_id); + $this->assertSame('10', $translation->get('field_reference_2')->target_id); } } diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php index 4df392f2c9e17d1734690d74c58b93140a4ab8a1..a82429ce6999fc7635eaceb6fa30546f264205b8 100644 --- a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\migrate_drupal_ui\Functional\d7; +use Drupal\node\Entity\Node; use Drupal\Tests\migrate_drupal_ui\Functional\MigrateUpgradeExecuteTestBase; use Drupal\user\Entity\User; @@ -65,8 +66,8 @@ protected function getEntityCounts() { 'configurable_language' => 4, 'contact_form' => 3, 'editor' => 2, - 'field_config' => 64, - 'field_storage_config' => 47, + 'field_config' => 66, + 'field_storage_config' => 49, 'file' => 3, 'filter_format' => 7, 'image_style' => 6, @@ -190,6 +191,27 @@ public function testMigrateUpgradeExecute() { $user = User::load(2); $user->passRaw = 'a password'; $this->drupalLogin($user); + $this->assertFollowUpMigrationResults(); + } + + /** + * Tests that follow-up migrations have been run successfully. + */ + protected function assertFollowUpMigrationResults() { + $node = Node::load(2); + $this->assertSame('4', $node->get('field_reference')->target_id); + $this->assertSame('4', $node->get('field_reference_2')->target_id); + $translation = $node->getTranslation('is'); + $this->assertSame('4', $translation->get('field_reference')->target_id); + $this->assertSame('4', $translation->get('field_reference_2')->target_id); + + $node = Node::load(4); + $this->assertSame('2', $node->get('field_reference')->target_id); + $this->assertSame('2', $node->get('field_reference_2')->target_id); + $translation = $node->getTranslation('en'); + $this->assertSame('2', $translation->get('field_reference')->target_id); + $this->assertSame('2', $translation->get('field_reference_2')->target_id); + } } diff --git a/core/modules/node/migrations/d6_node_translation.yml b/core/modules/node/migrations/d6_node_translation.yml index 26cb0243bc91c6749a46b17306e9a6a8c7029ccf..b1345ed065f092e7334dc7ccae0886f1996170be 100644 --- a/core/modules/node/migrations/d6_node_translation.yml +++ b/core/modules/node/migrations/d6_node_translation.yml @@ -2,7 +2,9 @@ id: d6_node_translation label: Node translations migration_tags: - Drupal 6 + - translation - Content +class: Drupal\node\Plugin\migrate\D6NodeTranslation deriver: Drupal\node\Plugin\migrate\D6NodeDeriver source: plugin: d6_node diff --git a/core/modules/node/migrations/d7_node_translation.yml b/core/modules/node/migrations/d7_node_translation.yml index 542ab33b20a91434b0fad2b564a1b488c2108af9..b5d488e268722c37ad73eff8fd899baef131f1cf 100644 --- a/core/modules/node/migrations/d7_node_translation.yml +++ b/core/modules/node/migrations/d7_node_translation.yml @@ -4,6 +4,7 @@ migration_tags: - Drupal 7 - translation - Content +class: Drupal\node\Plugin\migrate\D7NodeTranslation deriver: Drupal\node\Plugin\migrate\D7NodeDeriver source: plugin: d7_node diff --git a/core/modules/node/src/Plugin/migrate/D6NodeTranslation.php b/core/modules/node/src/Plugin/migrate/D6NodeTranslation.php new file mode 100644 index 0000000000000000000000000000000000000000..3abf7720cf53e78d2d86f7bf4a084018c3b93ca1 --- /dev/null +++ b/core/modules/node/src/Plugin/migrate/D6NodeTranslation.php @@ -0,0 +1,21 @@ +<?php + +namespace Drupal\node\Plugin\migrate; + +use Drupal\migrate\Plugin\Migration; +use Drupal\migrate_drupal\Plugin\MigrationWithFollowUpInterface; + +/** + * Migration plugin for the Drupal 6 node translations. + */ +class D6NodeTranslation extends Migration implements MigrationWithFollowUpInterface { + + /** + * {@inheritdoc} + */ + public function generateFollowUpMigrations() { + $this->migrationPluginManager->clearCachedDefinitions(); + return $this->migrationPluginManager->createInstances('d6_entity_reference_translation'); + } + +} diff --git a/core/modules/node/src/Plugin/migrate/D7NodeTranslation.php b/core/modules/node/src/Plugin/migrate/D7NodeTranslation.php new file mode 100644 index 0000000000000000000000000000000000000000..fd016f3c31b7e8a775bf4c2f39e89afe42a11c8c --- /dev/null +++ b/core/modules/node/src/Plugin/migrate/D7NodeTranslation.php @@ -0,0 +1,21 @@ +<?php + +namespace Drupal\node\Plugin\migrate; + +use Drupal\migrate\Plugin\Migration; +use Drupal\migrate_drupal\Plugin\MigrationWithFollowUpInterface; + +/** + * Migration plugin for the Drupal 7 node translations. + */ +class D7NodeTranslation extends Migration implements MigrationWithFollowUpInterface { + + /** + * {@inheritdoc} + */ + public function generateFollowUpMigrations() { + $this->migrationPluginManager->clearCachedDefinitions(); + return $this->migrationPluginManager->createInstances('d7_entity_reference_translation'); + } + +}