Loading src/Plugin/migrate/process/EntityLookup.php +19 −7 Changes for src/Plugin/migrate/process/EntityLookup.php: 19 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -32,13 +32,17 @@ use Symfony\Component\DependencyInjection\ContainerInterface; * - entity_reference: The entity label key. * - file: The uri field. * - image: The uri field. * - operator: (optional) The comparison operator supported by entity query: * See \Drupal\Core\Entity\Query\QueryInterface::condition() for available * values. Defaults to '=' for scalar values and 'IN' for arrays. * - bundle_key: (optional) The name of the bundle field on the entity type * being queried. * - bundle: (optional) The value to query for the bundle. * - access_check: (optional) Indicates if access to the entity for this user * will be checked. Default is true. * - ignore_case: (optional) Whether to ignore case in the query. Defaults to * true. * false, meaning the query is case-sensitive by default. Works only with * strict operators: '=' and 'IN'. * - destination_field: (optional) If specified, and if the plugin's source * value is an array, the result array's items will be themselves arrays of * the form [destination_field => ENTITY_ID]. Loading Loading @@ -70,10 +74,13 @@ use Symfony\Component\DependencyInjection\ContainerInterface; * bundle: tags * entity_type: taxonomy_term * ignore_case: true * operator: STARTS_WITH * @endcode * * @codingStandardsIgnoreEnd * * @see \Drupal\Core\Entity\Query\QueryInterface::condition() * * @MigrateProcessPlugin( * id = "entity_lookup", * handle_multiples = TRUE Loading Loading @@ -220,17 +227,22 @@ class EntityLookup extends ProcessPluginBase implements ContainerFactoryPluginIn */ protected function query($value) { // Entity queries typically are case-insensitive. Therefore, we need to // handle case sensitive filtering as a post-query step. By default, it // filters case insensitive. Change to true if that is not the desired // handle case-sensitive filtering as a post-query step. By default, it // filters case-insensitive. Change to true if that is not the desired // outcome. $ignoreCase = !empty($this->configuration['ignore_case']) ?: FALSE; $operator = !empty($this->configuration['operator']) ? $this->configuration['operator'] : '='; $multiple = is_array($value); // Apply correct operator for multiple values. if ($multiple && $operator === '=') { $operator = 'IN'; } $query = $this->entityTypeManager->getStorage($this->lookupEntityType) ->getQuery() ->accessCheck($this->accessCheck) ->condition($this->lookupValueKey, $value, $multiple ? 'IN' : NULL); ->condition($this->lookupValueKey, $value, $operator); // Sqlite and possibly others returns data in a non-deterministic order. // Make it deterministic. if ($multiple) { Loading @@ -246,8 +258,8 @@ class EntityLookup extends ProcessPluginBase implements ContainerFactoryPluginIn return NULL; } // By default do a case-sensitive comparison. if (!$ignoreCase) { // Do a case-sensitive comparison only for strict operators. if (!$ignoreCase && in_array($operator, ['=', 'IN'], TRUE)) { // Returns the entity's identifier. foreach ($results as $k => $identifier) { $entity = $this->entityTypeManager->getStorage($this->lookupEntityType)->load($identifier); Loading tests/src/Kernel/Plugin/migrate/process/EntityLookupTest.php +88 −14 Changes for tests/src/Kernel/Plugin/migrate/process/EntityLookupTest.php: 88 added lines, 14 removed lines. Original line number Diff line number Diff line Loading @@ -5,10 +5,8 @@ declare(strict_types = 1); namespace Drupal\Tests\migrate_plus\Kernel\Plugin\migrate\process; use Drupal\KernelTests\KernelTestBase; use Drupal\migrate\MigrateExecutableInterface; use Drupal\migrate\Plugin\MigrateDestinationInterface; use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Row; use Drupal\Tests\node\Traits\NodeCreationTrait; use Drupal\Tests\user\Traits\UserCreationTrait; /** Loading @@ -20,6 +18,7 @@ use Drupal\Tests\user\Traits\UserCreationTrait; final class EntityLookupTest extends KernelTestBase { use UserCreationTrait; use NodeCreationTrait; /** * The migrate executable mock object. Loading @@ -36,6 +35,7 @@ final class EntityLookupTest extends KernelTestBase { 'migrate', 'user', 'system', 'node', 'filter', ]; Loading @@ -46,11 +46,22 @@ final class EntityLookupTest extends KernelTestBase { parent::setUp(); $this->installSchema('system', ['sequences']); $this->installEntitySchema('user'); $this->installEntitySchema('node'); $this->installConfig(['filter']); $this->migrateExecutable = $this->getMockBuilder('Drupal\migrate\MigrateExecutable') ->disableOriginalConstructor() ->getMock(); $test_nodes = [ ['title' => 'foo 1'], ['title' => 'foo 2'], ['title' => 'bar 1'], ]; foreach ($test_nodes as $test_node) { $this->createNode($test_node); } } /** Loading @@ -62,29 +73,33 @@ final class EntityLookupTest extends KernelTestBase { * @covers ::transform */ public function testLookupEntityWithoutBundles(): void { $migration = \Drupal::service('plugin.manager.migration') ->createStubMigration([ 'id' => 'test', 'source' => [], 'process' => [], 'destination' => [ 'plugin' => 'entity:user', ], ]); // Create a user. $known_user = $this->createUser([], 'lucuma'); // Setup test migration objects. $migration_prophecy = $this->prophesize(MigrationInterface::class); $migrate_destination_prophecy = $this->prophesize(MigrateDestinationInterface::class); $migrate_destination_prophecy->getPluginId()->willReturn('user'); $migrate_destination = $migrate_destination_prophecy->reveal(); $migration_prophecy->getDestinationPlugin()->willReturn($migrate_destination); $migration_prophecy->getProcess()->willReturn([]); $migration = $migration_prophecy->reveal(); $configuration = [ 'entity_type' => 'user', 'value_key' => 'name', ]; $plugin = \Drupal::service('plugin.manager.migrate.process') ->createInstance('entity_lookup', $configuration, $migration); $executable = $this->prophesize(MigrateExecutableInterface::class)->reveal(); $row = new Row(); // Check the known user is found. $value = $plugin->transform('lucuma', $executable, $row, 'name'); $value = $plugin->transform('lucuma', $this->migrateExecutable, $row, 'name'); $this->assertSame($known_user->id(), $value); // Check an unknown user is not found. $value = $plugin->transform('orange', $executable, $row, 'name'); $value = $plugin->transform('orange', $this->migrateExecutable, $row, 'name'); $this->assertNull($value); } Loading Loading @@ -113,4 +128,63 @@ final class EntityLookupTest extends KernelTestBase { $this->assertEquals('plain_text', $value); } /** * Tests lookup with different operators. * * @covers ::transform * @dataProvider providerTestLookupOperators */ public function testLookupOperators($configuration, $lookup_value, $expected_value): void { $migration = \Drupal::service('plugin.manager.migration') ->createStubMigration([ 'id' => 'test', 'source' => [], 'process' => [], 'destination' => [ 'plugin' => 'entity:node', ], ]); $plugin = \Drupal::service('plugin.manager.migrate.process') ->createInstance('entity_lookup', $configuration, $migration); $value = $plugin->transform($lookup_value, $this->migrateExecutable, new Row(), 'destination_property'); $this->assertEquals($expected_value, $value); } /** * Data provider for testLookupOperators test. * * @return array[] * The test cases. */ public function providerTestLookupOperators(): array { return [ 'Default operator' => [ [ 'entity_type' => 'node', 'value_key' => 'title', ], 'foo 1', '1', ], 'Multiple values' => [ [ 'entity_type' => 'node', 'value_key' => 'title', ], ['foo 1', 'foo 2'], ['2', '1'], ], 'Starts with' => [ [ 'entity_type' => 'node', 'value_key' => 'title', 'operator' => 'STARTS_WITH', ], 'bar', '3', ], ]; } } Loading
src/Plugin/migrate/process/EntityLookup.php +19 −7 Changes for src/Plugin/migrate/process/EntityLookup.php: 19 added lines, 7 removed lines. Original line number Diff line number Diff line Loading @@ -32,13 +32,17 @@ use Symfony\Component\DependencyInjection\ContainerInterface; * - entity_reference: The entity label key. * - file: The uri field. * - image: The uri field. * - operator: (optional) The comparison operator supported by entity query: * See \Drupal\Core\Entity\Query\QueryInterface::condition() for available * values. Defaults to '=' for scalar values and 'IN' for arrays. * - bundle_key: (optional) The name of the bundle field on the entity type * being queried. * - bundle: (optional) The value to query for the bundle. * - access_check: (optional) Indicates if access to the entity for this user * will be checked. Default is true. * - ignore_case: (optional) Whether to ignore case in the query. Defaults to * true. * false, meaning the query is case-sensitive by default. Works only with * strict operators: '=' and 'IN'. * - destination_field: (optional) If specified, and if the plugin's source * value is an array, the result array's items will be themselves arrays of * the form [destination_field => ENTITY_ID]. Loading Loading @@ -70,10 +74,13 @@ use Symfony\Component\DependencyInjection\ContainerInterface; * bundle: tags * entity_type: taxonomy_term * ignore_case: true * operator: STARTS_WITH * @endcode * * @codingStandardsIgnoreEnd * * @see \Drupal\Core\Entity\Query\QueryInterface::condition() * * @MigrateProcessPlugin( * id = "entity_lookup", * handle_multiples = TRUE Loading Loading @@ -220,17 +227,22 @@ class EntityLookup extends ProcessPluginBase implements ContainerFactoryPluginIn */ protected function query($value) { // Entity queries typically are case-insensitive. Therefore, we need to // handle case sensitive filtering as a post-query step. By default, it // filters case insensitive. Change to true if that is not the desired // handle case-sensitive filtering as a post-query step. By default, it // filters case-insensitive. Change to true if that is not the desired // outcome. $ignoreCase = !empty($this->configuration['ignore_case']) ?: FALSE; $operator = !empty($this->configuration['operator']) ? $this->configuration['operator'] : '='; $multiple = is_array($value); // Apply correct operator for multiple values. if ($multiple && $operator === '=') { $operator = 'IN'; } $query = $this->entityTypeManager->getStorage($this->lookupEntityType) ->getQuery() ->accessCheck($this->accessCheck) ->condition($this->lookupValueKey, $value, $multiple ? 'IN' : NULL); ->condition($this->lookupValueKey, $value, $operator); // Sqlite and possibly others returns data in a non-deterministic order. // Make it deterministic. if ($multiple) { Loading @@ -246,8 +258,8 @@ class EntityLookup extends ProcessPluginBase implements ContainerFactoryPluginIn return NULL; } // By default do a case-sensitive comparison. if (!$ignoreCase) { // Do a case-sensitive comparison only for strict operators. if (!$ignoreCase && in_array($operator, ['=', 'IN'], TRUE)) { // Returns the entity's identifier. foreach ($results as $k => $identifier) { $entity = $this->entityTypeManager->getStorage($this->lookupEntityType)->load($identifier); Loading
tests/src/Kernel/Plugin/migrate/process/EntityLookupTest.php +88 −14 Changes for tests/src/Kernel/Plugin/migrate/process/EntityLookupTest.php: 88 added lines, 14 removed lines. Original line number Diff line number Diff line Loading @@ -5,10 +5,8 @@ declare(strict_types = 1); namespace Drupal\Tests\migrate_plus\Kernel\Plugin\migrate\process; use Drupal\KernelTests\KernelTestBase; use Drupal\migrate\MigrateExecutableInterface; use Drupal\migrate\Plugin\MigrateDestinationInterface; use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Row; use Drupal\Tests\node\Traits\NodeCreationTrait; use Drupal\Tests\user\Traits\UserCreationTrait; /** Loading @@ -20,6 +18,7 @@ use Drupal\Tests\user\Traits\UserCreationTrait; final class EntityLookupTest extends KernelTestBase { use UserCreationTrait; use NodeCreationTrait; /** * The migrate executable mock object. Loading @@ -36,6 +35,7 @@ final class EntityLookupTest extends KernelTestBase { 'migrate', 'user', 'system', 'node', 'filter', ]; Loading @@ -46,11 +46,22 @@ final class EntityLookupTest extends KernelTestBase { parent::setUp(); $this->installSchema('system', ['sequences']); $this->installEntitySchema('user'); $this->installEntitySchema('node'); $this->installConfig(['filter']); $this->migrateExecutable = $this->getMockBuilder('Drupal\migrate\MigrateExecutable') ->disableOriginalConstructor() ->getMock(); $test_nodes = [ ['title' => 'foo 1'], ['title' => 'foo 2'], ['title' => 'bar 1'], ]; foreach ($test_nodes as $test_node) { $this->createNode($test_node); } } /** Loading @@ -62,29 +73,33 @@ final class EntityLookupTest extends KernelTestBase { * @covers ::transform */ public function testLookupEntityWithoutBundles(): void { $migration = \Drupal::service('plugin.manager.migration') ->createStubMigration([ 'id' => 'test', 'source' => [], 'process' => [], 'destination' => [ 'plugin' => 'entity:user', ], ]); // Create a user. $known_user = $this->createUser([], 'lucuma'); // Setup test migration objects. $migration_prophecy = $this->prophesize(MigrationInterface::class); $migrate_destination_prophecy = $this->prophesize(MigrateDestinationInterface::class); $migrate_destination_prophecy->getPluginId()->willReturn('user'); $migrate_destination = $migrate_destination_prophecy->reveal(); $migration_prophecy->getDestinationPlugin()->willReturn($migrate_destination); $migration_prophecy->getProcess()->willReturn([]); $migration = $migration_prophecy->reveal(); $configuration = [ 'entity_type' => 'user', 'value_key' => 'name', ]; $plugin = \Drupal::service('plugin.manager.migrate.process') ->createInstance('entity_lookup', $configuration, $migration); $executable = $this->prophesize(MigrateExecutableInterface::class)->reveal(); $row = new Row(); // Check the known user is found. $value = $plugin->transform('lucuma', $executable, $row, 'name'); $value = $plugin->transform('lucuma', $this->migrateExecutable, $row, 'name'); $this->assertSame($known_user->id(), $value); // Check an unknown user is not found. $value = $plugin->transform('orange', $executable, $row, 'name'); $value = $plugin->transform('orange', $this->migrateExecutable, $row, 'name'); $this->assertNull($value); } Loading Loading @@ -113,4 +128,63 @@ final class EntityLookupTest extends KernelTestBase { $this->assertEquals('plain_text', $value); } /** * Tests lookup with different operators. * * @covers ::transform * @dataProvider providerTestLookupOperators */ public function testLookupOperators($configuration, $lookup_value, $expected_value): void { $migration = \Drupal::service('plugin.manager.migration') ->createStubMigration([ 'id' => 'test', 'source' => [], 'process' => [], 'destination' => [ 'plugin' => 'entity:node', ], ]); $plugin = \Drupal::service('plugin.manager.migrate.process') ->createInstance('entity_lookup', $configuration, $migration); $value = $plugin->transform($lookup_value, $this->migrateExecutable, new Row(), 'destination_property'); $this->assertEquals($expected_value, $value); } /** * Data provider for testLookupOperators test. * * @return array[] * The test cases. */ public function providerTestLookupOperators(): array { return [ 'Default operator' => [ [ 'entity_type' => 'node', 'value_key' => 'title', ], 'foo 1', '1', ], 'Multiple values' => [ [ 'entity_type' => 'node', 'value_key' => 'title', ], ['foo 1', 'foo 2'], ['2', '1'], ], 'Starts with' => [ [ 'entity_type' => 'node', 'value_key' => 'title', 'operator' => 'STARTS_WITH', ], 'bar', '3', ], ]; } }