diff --git a/src/Plugin/Derivative/MigrateEntityReferenceRevisions.php b/src/Plugin/Derivative/MigrateEntityReferenceRevisions.php new file mode 100644 index 0000000..2b8ae69 --- /dev/null +++ b/src/Plugin/Derivative/MigrateEntityReferenceRevisions.php @@ -0,0 +1,31 @@ +entityDefinitions as $entityType => $entityInfo) { + if ($entityInfo->getKey('revision')) { + $this->derivatives[$entityType] = array( + 'id' => "entity_reference_revisions:$entityType", + 'class' => EntityReferenceRevisions::class, + 'requirements_met' => 1, + 'provider' => $entityInfo->getProvider(), + ); + } + } + return $this->derivatives; + } + +} diff --git a/src/Plugin/migrate/destination/EntityReferenceRevisions.php b/src/Plugin/migrate/destination/EntityReferenceRevisions.php new file mode 100644 index 0000000..b444233 --- /dev/null +++ b/src/Plugin/migrate/destination/EntityReferenceRevisions.php @@ -0,0 +1,140 @@ +save(); + + return [ + $this->getKey('id') => $entity->id(), + $this->getKey('revision') => $entity->getRevisionId(), + ]; + } + + /** + * {@inheritdoc} + */ + public function getIds() { + if ($revision_key = $this->getKey('revision')) { + $id_key = $this->getKey('id'); + $ids[$id_key]['type'] = 'integer'; + + // TODO: Improve after https://www.drupal.org/node/2783715 is finished. + $ids[$revision_key]['type'] = 'integer'; + + if ($this->isTranslationDestination()) { + if ($revision_key = $this->getKey('langcode')) { + $ids[$revision_key]['type'] = 'string'; + } + else { + throw new MigrateException('This entity type does not support translation.'); + } + } + + return $ids; + } + throw new MigrateException('This entity type does not support revisions.'); + } + + /** + * {@inheritdoc} + */ + protected function getEntity(Row $row, array $oldDestinationIdValues) { + $revision_id = $oldDestinationIdValues ? + array_pop($oldDestinationIdValues) : + $row->getDestinationProperty($this->getKey('revision')); + if (!empty($revision_id) && ($entity = $this->storage->loadRevision($revision_id))) { + $entity->setNewRevision(FALSE); + } + else { + // Attempt to ensure we always have a bundle. + if ($bundle = $this->getBundle($row)) { + $row->setDestinationProperty($this->getKey('bundle'), $bundle); + } + + $entity = $this->storage->create($row->getDestination()); + $entity->enforceIsNew(); + } + $entity = $this->updateEntity($entity, $row) ?: $entity; + return $entity; + } + + /** + * {@inheritdoc} + */ + public function rollback(array $destination_identifiers) { + if ($this->isTranslationDestination()) { + $this->rollbackTranslation($destination_identifiers); + } + else { + $this->rollbackNonTranslation($destination_identifiers); + } + } + + /** + * Rollback translation destinations. + * + * @param array $destination_identifiers + * The IDs of the destination object to delete. + */ + protected function rollbackTranslation(array $destination_identifiers) { + $entity = $this->storage->loadRevision($destination_identifiers[1]); + if ($entity && $entity instanceof TranslatableInterface) { + if ($key = $this->getKey('langcode')) { + if (isset($destination_identifier[$key])) { + $langcode = $destination_identifier[$key]; + if ($entity->hasTranslation($langcode)) { + // Make sure we don't remove the default translation. + $translation = $entity->getTranslation($langcode); + if (!$translation->isDefaultTranslation()) { + $entity->removeTranslation($langcode); + $entity->save(); + } + } + } + } + } + } + + /** + * Rollback non-translation destinations. + * + * @param array $destination_identifiers + * The IDs of the destination object to delete. + */ + protected function rollbackNonTranslation(array $destination_identifiers) { + $entity = $this->storage->loadRevision($destination_identifiers[1]); + if ($entity) { + $entity->delete(); + } + } +} diff --git a/tests/modules/err_migration_test/err_migration_test.info.yml b/tests/modules/err_migration_test/err_migration_test.info.yml new file mode 100644 index 0000000..d61822b --- /dev/null +++ b/tests/modules/err_migration_test/err_migration_test.info.yml @@ -0,0 +1,9 @@ +name: 'ERR migration test' +type: module +description: 'Entity migration test' +package: Testing +core: 8.x + +dependencies: + - entity_reference_revisions + - migrate diff --git a/tests/modules/err_migration_test/migration_templates/err_migration_test.yml b/tests/modules/err_migration_test/migration_templates/err_migration_test.yml new file mode 100644 index 0000000..e5d6d34 --- /dev/null +++ b/tests/modules/err_migration_test/migration_templates/err_migration_test.yml @@ -0,0 +1,12 @@ +id: err_migration_test +migration_tags: {} +label: 'ERR Migration Test' +source: + plugin: err_dummy +process: + id: id + revision_id: revision_id + name: name +destination: + plugin: 'entity_reference_revisions:entity_test_composite' +migration_dependencies: { } diff --git a/tests/modules/err_migration_test/src/Plugin/migrate/source/DummySource.php b/tests/modules/err_migration_test/src/Plugin/migrate/source/DummySource.php new file mode 100644 index 0000000..3a13d00 --- /dev/null +++ b/tests/modules/err_migration_test/src/Plugin/migrate/source/DummySource.php @@ -0,0 +1,49 @@ + [ + 'type' => 'integer', + ] + ]; + } + + /** + * {@inheritdoc} + */ + protected function initializeIterator() { + return new \ArrayIterator([ + ['id' => 1, 'revision_id' => 1, 'name' => 'content item 1'], + ['id' => '', 'revision_id' => '', 'name' => 'content item 2'], + ]); + } +} diff --git a/tests/src/Kernel/Plugin/Derivative/EntityReferenceRevisionsDeriverTest.php b/tests/src/Kernel/Plugin/Derivative/EntityReferenceRevisionsDeriverTest.php new file mode 100644 index 0000000..d61b720 --- /dev/null +++ b/tests/src/Kernel/Plugin/Derivative/EntityReferenceRevisionsDeriverTest.php @@ -0,0 +1,49 @@ +installConfig($this->modules); + } + + /** + * Tests deriver. + * + * @covers ::getDerivativeDefinitions + */ + public function testDestinationDeriver() { + /** @var MigrationPluginManager $migrationManager */ + $migrationManager = \Drupal::service('plugin.manager.migration'); + /** @var MigrateDestinationPluginManager $migrationDestinationManager */ + $migrationDestinationManager = \Drupal::service('plugin.manager.migrate.destination'); + + $definition = $migrationManager->getDefinition('err_migration_test'); + $destination = $migrationDestinationManager->getDefinition($definition['destination']['plugin']); + $this->assertEquals(EntityReferenceRevisions::class, $destination['class']); + } + + + +} diff --git a/tests/src/Kernel/Plugin/migrate/destination/EntityReferenceRevisionsDestinationTest.php b/tests/src/Kernel/Plugin/migrate/destination/EntityReferenceRevisionsDestinationTest.php new file mode 100644 index 0000000..85cd928 --- /dev/null +++ b/tests/src/Kernel/Plugin/migrate/destination/EntityReferenceRevisionsDestinationTest.php @@ -0,0 +1,104 @@ +installEntitySchema('entity_test_composite'); + $this->installSchema('system', ['sequences']); + $this->installConfig($this->modules); + } + + /** + * Tests get entity type id. + * + * @covers ::getEntityTypeId + */ + public function testGetEntityTypeId() { + /** @var MigrationPluginManager $migrationManager */ + $migrationManager = \Drupal::service('plugin.manager.migration'); + $definition = $migrationManager->getDefinition('err_migration_test'); + /** @var Migration $migration */ + $migration = $migrationManager->createStubMigration($definition); + /** @var EntityReferenceRevisions $destination */ + $destination = $migration->getDestinationPlugin(); + + /** @var EntityStorageBase $storage */ + $storage = $this->readAttribute($destination, 'storage'); + $actual = $this->readAttribute($storage, 'entityTypeId'); + + $expected = 'entity_test_composite'; + $this->assertEquals($expected, $actual); + } + + /** + * Tests get entity. + * + * @covers ::getEntity + * @covers ::rollback + * @covers ::rollbackNonTranslation + */ + public function testGetEntity() { + /** @var MigrationPluginManager $migrationManager */ + $migrationManager = \Drupal::service('plugin.manager.migration'); + $definition = $migrationManager->getDefinition('err_migration_test'); + /** @var Migration $migration */ + $migration = $migrationManager->createStubMigration($definition); + $migrationExecutable = (new MigrateExecutable($migration, $this)); + /** @var EntityStorageBase $storage */ + $storage = $this->readAttribute($migration->getDestinationPlugin(), 'storage'); + for ($i = 0; $i < 2; $i++) { + $migrationExecutable->import(); + $migration->getIdMap()->prepareUpdate(); + $entity = $storage->loadRevision(1); + $this->assertEquals('content item 1', $entity->label()); + $entity = $storage->loadRevision(2); + $this->assertEquals('content item 2', $entity->label()); + } + $migrationExecutable->rollback(); + $entity = $storage->loadRevision(1); + $this->assertEmpty($entity); + $entity = $storage->loadRevision(2); + $this->assertEmpty($entity); + } + + /** + * {@inheritdoc} + */ + public function display($message, $type = 'status') { + $this->assertTrue($type == 'status', $message); + } + +}