Loading core/modules/migrate/src/Row.php +26 −0 Original line number Diff line number Diff line Loading @@ -266,6 +266,32 @@ public function getEmptyDestinationProperties() { return $this->emptyDestinationProperties; } /** * 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. * Loading core/modules/migrate/tests/src/Kernel/RowTest.php 0 → 100644 +152 −0 Original line number Diff line number Diff line <?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(); } } core/modules/migrate/tests/src/Unit/RowTest.php +37 −0 Original line number Diff line number Diff line Loading @@ -291,6 +291,43 @@ public function testDestination(): void { $this->assertEquals(['nid' => 2], $row->getDestination()); } /** * 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. */ Loading Loading
core/modules/migrate/src/Row.php +26 −0 Original line number Diff line number Diff line Loading @@ -266,6 +266,32 @@ public function getEmptyDestinationProperties() { return $this->emptyDestinationProperties; } /** * 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. * Loading
core/modules/migrate/tests/src/Kernel/RowTest.php 0 → 100644 +152 −0 Original line number Diff line number Diff line <?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(); } }
core/modules/migrate/tests/src/Unit/RowTest.php +37 −0 Original line number Diff line number Diff line Loading @@ -291,6 +291,43 @@ public function testDestination(): void { $this->assertEquals(['nid' => 2], $row->getDestination()); } /** * 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. */ Loading