summaryrefslogtreecommitdiffstatshomepage
path: root/core/modules/migrate
diff options
context:
space:
mode:
Diffstat (limited to 'core/modules/migrate')
-rw-r--r--core/modules/migrate/migrate.api.php4
-rw-r--r--core/modules/migrate/src/Plugin/Discovery/AnnotatedClassDiscoveryAutomatedProviders.php2
-rw-r--r--core/modules/migrate/src/Plugin/migrate/source/ConfigEntity.php3
-rw-r--r--core/modules/migrate/src/Row.php26
-rw-r--r--core/modules/migrate/tests/src/Functional/MigrateMessageTestBase.php4
-rw-r--r--core/modules/migrate/tests/src/Kernel/RowTest.php152
-rw-r--r--core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityTestBase.php2
-rw-r--r--core/modules/migrate/tests/src/Unit/RowTest.php37
8 files changed, 222 insertions, 8 deletions
diff --git a/core/modules/migrate/migrate.api.php b/core/modules/migrate/migrate.api.php
index 1e58b0090ff2..5d2af7db180e 100644
--- a/core/modules/migrate/migrate.api.php
+++ b/core/modules/migrate/migrate.api.php
@@ -156,7 +156,7 @@ function hook_migrate_prepare_row(Row $row, MigrateSourceInterface $source, Migr
if ($migration->id() == 'd6_filter_formats') {
$value = $source->getDatabase()->query('SELECT [value] FROM {variable} WHERE [name] = :name', [':name' => 'my_module_filter_foo_' . $row->getSourceProperty('format')])->fetchField();
if ($value) {
- $row->setSourceProperty('settings:my_module:foo', unserialize($value));
+ $row->setSourceProperty('settings:my_module:foo', unserialize($value, ['allowed_classes' => FALSE]));
}
}
}
@@ -179,7 +179,7 @@ function hook_migrate_prepare_row(Row $row, MigrateSourceInterface $source, Migr
function hook_migrate_MIGRATION_ID_prepare_row(Row $row, MigrateSourceInterface $source, MigrationInterface $migration) {
$value = $source->getDatabase()->query('SELECT [value] FROM {variable} WHERE [name] = :name', [':name' => 'my_module_filter_foo_' . $row->getSourceProperty('format')])->fetchField();
if ($value) {
- $row->setSourceProperty('settings:my_module:foo', unserialize($value));
+ $row->setSourceProperty('settings:my_module:foo', unserialize($value, ['allowed_classes' => FALSE]));
}
}
diff --git a/core/modules/migrate/src/Plugin/Discovery/AnnotatedClassDiscoveryAutomatedProviders.php b/core/modules/migrate/src/Plugin/Discovery/AnnotatedClassDiscoveryAutomatedProviders.php
index 9adf60b46ffe..30cc28562e8d 100644
--- a/core/modules/migrate/src/Plugin/Discovery/AnnotatedClassDiscoveryAutomatedProviders.php
+++ b/core/modules/migrate/src/Plugin/Discovery/AnnotatedClassDiscoveryAutomatedProviders.php
@@ -65,7 +65,7 @@ class AnnotatedClassDiscoveryAutomatedProviders extends AnnotatedClassDiscovery
if (isset($cached['id'])) {
// Explicitly unserialize this to create a new object
// instance.
- $definitions[$cached['id']] = unserialize($cached['content']);
+ $definitions[$cached['id']] = unserialize($cached['content'], ['allowed_classes' => FALSE]);
}
continue;
}
diff --git a/core/modules/migrate/src/Plugin/migrate/source/ConfigEntity.php b/core/modules/migrate/src/Plugin/migrate/source/ConfigEntity.php
index dc70496282f3..a5351c748620 100644
--- a/core/modules/migrate/src/Plugin/migrate/source/ConfigEntity.php
+++ b/core/modules/migrate/src/Plugin/migrate/source/ConfigEntity.php
@@ -71,7 +71,8 @@ class ConfigEntity extends SqlBase {
* {@inheritdoc}
*/
public function prepareRow(Row $row) {
- $row->setSourceProperty('data', unserialize($row->getSourceProperty('data')));
+ // @see \Drupal\Core\Config\DatabaseStorage::decode()
+ $row->setSourceProperty('data', unserialize($row->getSourceProperty('data'), ['allowed_classes' => FALSE]));
return parent::prepareRow($row);
}
diff --git a/core/modules/migrate/src/Row.php b/core/modules/migrate/src/Row.php
index 3d961902bcde..2b5e8b2fb4e6 100644
--- a/core/modules/migrate/src/Row.php
+++ b/core/modules/migrate/src/Row.php
@@ -267,6 +267,32 @@ class Row {
}
/**
+ * Tests if a property is an empty destination.
+ *
+ * @param string $property
+ * The name of the property.
+ *
+ * @return bool
+ * TRUE if the property is an empty destination.
+ */
+ public function hasEmptyDestinationProperty(string $property): bool {
+ return in_array($property, $this->emptyDestinationProperties);
+ }
+
+ /**
+ * Removes an empty destination property.
+ *
+ * @param string $property
+ * The name of the empty destination property.
+ */
+ public function removeEmptyDestinationProperty(string $property): void {
+ $this->emptyDestinationProperties = array_diff(
+ $this->emptyDestinationProperties,
+ [$property],
+ );
+ }
+
+ /**
* Returns the whole destination array.
*
* @return array
diff --git a/core/modules/migrate/tests/src/Functional/MigrateMessageTestBase.php b/core/modules/migrate/tests/src/Functional/MigrateMessageTestBase.php
index 6885ba378e9c..84dddc3c1825 100644
--- a/core/modules/migrate/tests/src/Functional/MigrateMessageTestBase.php
+++ b/core/modules/migrate/tests/src/Functional/MigrateMessageTestBase.php
@@ -12,10 +12,8 @@ use Drupal\migrate\Plugin\MigrationInterface;
/**
* Provides base class for testing migrate messages.
- *
- * @group migrate
*/
-class MigrateMessageTestBase extends BrowserTestBase {
+abstract class MigrateMessageTestBase extends BrowserTestBase {
/**
* {@inheritdoc}
diff --git a/core/modules/migrate/tests/src/Kernel/RowTest.php b/core/modules/migrate/tests/src/Kernel/RowTest.php
new file mode 100644
index 000000000000..1b4adf181c88
--- /dev/null
+++ b/core/modules/migrate/tests/src/Kernel/RowTest.php
@@ -0,0 +1,152 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\migrate\Kernel;
+
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\migrate\Event\MigratePreRowSaveEvent;
+use Drupal\migrate\Event\MigrateEvents;
+use Drupal\migrate\MigrateExecutable;
+use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+
+/**
+ * Tests the Row class.
+ *
+ * @group migrate
+ */
+class RowTest extends KernelTestBase {
+
+ /**
+ * The event dispatcher.
+ */
+ protected EventDispatcherInterface $eventDispatcher;
+
+ /**
+ * The entity type manager.
+ */
+ protected EntityTypeManagerInterface $entityTypeManager;
+
+ /**
+ * The migration manager.
+ */
+ protected MigrationPluginManagerInterface $migrationManager;
+
+ /**
+ * {@inheritdoc}
+ */
+ protected static $modules = [
+ 'entity_test',
+ 'field',
+ 'migrate',
+ 'user',
+ ];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setUp(): void {
+ parent::setUp();
+ $this->installEntitySchema('entity_test');
+
+ $this->eventDispatcher = \Drupal::service('event_dispatcher');
+ $this->entityTypeManager = \Drupal::service('entity_type.manager');
+ $this->migrationManager = \Drupal::service('plugin.manager.migration');
+
+ // Create two fields that will be set during migration.
+ $fields = ['field1', 'field2'];
+ foreach ($fields as $field) {
+ $this->entityTypeManager->getStorage('field_storage_config')->create([
+ 'entity_type' => 'entity_test',
+ 'field_name' => $field,
+ 'type' => 'string',
+ ])->save();
+ $this->entityTypeManager->getStorage('field_config')->create([
+ 'entity_type' => 'entity_test',
+ 'field_name' => $field,
+ 'bundle' => 'entity_test',
+ ])->save();
+ }
+ }
+
+ /**
+ * Tests the destination properties of the Row class.
+ */
+ public function testRowDestinations(): void {
+ $storage = $this->entityTypeManager->getStorage('entity_test');
+
+ // Execute a migration that creates an entity with two fields.
+ $data_rows = [
+ ['id' => 1, 'field1' => 'f1value', 'field2' => 'f2value'],
+ ];
+ $ids = ['id' => ['type' => 'integer']];
+ $definition = [
+ 'source' => [
+ 'plugin' => 'embedded_data',
+ 'data_rows' => $data_rows,
+ 'ids' => $ids,
+ ],
+ 'process' => [
+ 'id' => 'id',
+ 'field1' => 'field1',
+ 'field2' => 'field2',
+ ],
+ 'destination' => ['plugin' => 'entity:entity_test'],
+ ];
+ $this->executeMigrationImport($definition);
+ $entity = $storage->load(1);
+ $this->assertEquals('f1value', $entity->get('field1')->getValue()[0]['value']);
+ $this->assertEquals('f2value', $entity->get('field2')->getValue()[0]['value']);
+
+ // Execute a second migration that attempts to remove both field values.
+ // The event listener prevents the removal of the second field.
+ $data_rows = [
+ ['id' => 1, 'field1' => NULL, 'field2' => NULL],
+ ];
+ $definition['source']['data_rows'] = $data_rows;
+ $this->eventDispatcher->addListener(MigrateEvents::PRE_ROW_SAVE, [$this, 'preventFieldRemoval']);
+ $this->executeMigrationImport($definition);
+
+ // The first field is now empty but the second field is still set.
+ $entity = $storage->load(1);
+ $this->assertTrue($entity->get('field1')->isEmpty());
+ $this->assertEquals('f2value', $entity->get('field2')->getValue()[0]['value']);
+ }
+
+ /**
+ * The pre-row-save event handler for the second migration.
+ *
+ * Checks row destinations and prevents the removal of the second field.
+ *
+ * @param \Drupal\migrate\Event\MigratePreRowSaveEvent $event
+ * The migration event.
+ * @param string $name
+ * The event name.
+ */
+ public function preventFieldRemoval(MigratePreRowSaveEvent $event, string $name): void {
+ $row = $event->getRow();
+
+ // Both fields are empty and their existing values will be removed.
+ $this->assertFalse($row->hasDestinationProperty('field1'));
+ $this->assertFalse($row->hasDestinationProperty('field2'));
+ $this->assertTrue($row->hasEmptyDestinationProperty('field1'));
+ $this->assertTrue($row->hasEmptyDestinationProperty('field2'));
+
+ // Prevent removal of field 2.
+ $row->removeEmptyDestinationProperty('field2');
+ }
+
+ /**
+ * Executes a migration import for the given migration definition.
+ *
+ * @param array $definition
+ * The migration definition.
+ */
+ protected function executeMigrationImport(array $definition): void {
+ $migration = $this->migrationManager->createStubMigration($definition);
+ (new MigrateExecutable($migration))->import();
+ }
+
+}
diff --git a/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityTestBase.php b/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityTestBase.php
index ba9ab78cff66..ed223601abb3 100644
--- a/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityTestBase.php
+++ b/core/modules/migrate/tests/src/Unit/Plugin/migrate/destination/EntityTestBase.php
@@ -14,7 +14,7 @@ use Drupal\Tests\UnitTestCase;
/**
* Base test class for entity migration destination functionality.
*/
-class EntityTestBase extends UnitTestCase {
+abstract class EntityTestBase extends UnitTestCase {
/**
* The migration entity.
diff --git a/core/modules/migrate/tests/src/Unit/RowTest.php b/core/modules/migrate/tests/src/Unit/RowTest.php
index 69fce6f78607..7db1a51db437 100644
--- a/core/modules/migrate/tests/src/Unit/RowTest.php
+++ b/core/modules/migrate/tests/src/Unit/RowTest.php
@@ -292,6 +292,43 @@ class RowTest extends UnitTestCase {
}
/**
+ * Tests checking for and removing destination properties that may be empty.
+ *
+ * @covers ::hasEmptyDestinationProperty
+ * @covers ::removeEmptyDestinationProperty
+ */
+ public function testDestinationOrEmptyProperty(): void {
+ $row = new Row($this->testValues, $this->testSourceIds);
+
+ // Set a destination.
+ $row->setDestinationProperty('nid', 2);
+ $this->assertTrue($row->hasDestinationProperty('nid'));
+ $this->assertFalse($row->hasEmptyDestinationProperty('nid'));
+
+ // Set an empty destination.
+ $row->setEmptyDestinationProperty('a_property_with_no_value');
+ $this->assertTrue($row->hasEmptyDestinationProperty('a_property_with_no_value'));
+ $this->assertFalse($row->hasDestinationProperty('a_property_with_no_value'));
+
+ // Removing an empty destination that is not actually empty has no effect.
+ $row->removeEmptyDestinationProperty('nid');
+ $this->assertTrue($row->hasDestinationProperty('nid'));
+ $this->assertFalse($row->hasEmptyDestinationProperty('nid'));
+
+ // Removing a destination that is actually empty has no effect.
+ $row->removeDestinationProperty('a_property_with_no_value');
+ $this->assertTrue($row->hasEmptyDestinationProperty('a_property_with_no_value'));
+
+ // Remove the empty destination.
+ $row->removeEmptyDestinationProperty('a_property_with_no_value');
+ $this->assertFalse($row->hasEmptyDestinationProperty('a_property_with_no_value'));
+
+ // Removing a destination that does not exist does not throw an error.
+ $this->assertFalse($row->hasEmptyDestinationProperty('not_a_property'));
+ $row->removeEmptyDestinationProperty('not_a_property');
+ }
+
+ /**
* Tests setting/getting multiple destination IDs.
*/
public function testMultipleDestination(): void {