Commit 20642d53 authored by alexpott's avatar alexpott

Issue #2724941 by vasi: Need to be able to lookup destination IDs by partial source IDs

parent e5b5f315
......@@ -209,10 +209,32 @@ public function lookupSourceID(array $destination_id_values);
* The source identifier keyed values of the record, e.g. ['nid' => 5].
*
* @return array
* The destination identifier values of the record, or NULL on failure.
* The destination identifier values of the record, or empty on failure.
*
* @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.x. Use
* lookupDestinationIds() instead.
*/
public function lookupDestinationId(array $source_id_values);
/**
* Looks up the destination identifiers corresponding to a source key.
*
* This can look up a subset of source keys if only some are provided, and
* will return all destination keys that match.
*
* @param array $source_id_values
* The source identifier keyed values of the records, e.g. ['nid' => 5].
* If unkeyed, the first count($source_id_values) keys will be assumed.
*
* @return array
* An array of arrays of destination identifier values.
*
* @throws \Drupal\migrate\MigrateException
* Thrown when $source_id_values contains unknown keys, or is the wrong
* length.
*/
public function lookupDestinationIds(array $source_id_values);
/**
* Looks up the destination identifier currently being iterated.
*
......
......@@ -517,16 +517,56 @@ public function lookupSourceID(array $destination_id_values) {
* {@inheritdoc}
*/
public function lookupDestinationId(array $source_id_values) {
$results = $this->lookupDestinationIds($source_id_values);
return $results ? reset($results) : array();
}
/**
* {@inheritdoc}
*/
public function lookupDestinationIds(array $source_id_values) {
if (empty($source_id_values)) {
return array();
}
// Canonicalize the keys into a hash of DB-field => value.
$is_associative = !isset($source_id_values[0]);
$conditions = [];
foreach ($this->sourceIdFields() as $field_name => $db_field) {
if ($is_associative) {
// Associative $source_id_values can have fields out of order.
if (isset($source_id_values[$field_name])) {
$conditions[$db_field] = $source_id_values[$field_name];
unset($source_id_values[$field_name]);
}
}
else {
// For non-associative $source_id_values, we assume they're the first
// few fields.
if (empty($source_id_values)) {
break;
}
$conditions[$db_field] = array_shift($source_id_values);
}
}
if (!empty($source_id_values)) {
throw new MigrateException("Extra unknown items in source IDs");
}
$query = $this->getDatabase()->select($this->mapTableName(), 'map')
->fields('map', $this->destinationIdFields());
->fields('map', $this->destinationIdFields());
if (count($this->sourceIdFields()) === count($conditions)) {
// Optimization: Use the primary key.
$query->condition(self::SOURCE_IDS_HASH, $this->getSourceIDsHash(array_values($conditions)));
}
else {
foreach ($conditions as $db_field => $value) {
$query->condition($db_field, $value);
}
}
$query->condition(static::SOURCE_IDS_HASH, $this->getSourceIDsHash($source_id_values));
$result = $query->execute();
$destination_id = $result->fetchAssoc();
return array_values($destination_id ?: array());
return $query->execute()->fetchAll(\PDO::FETCH_NUM);
}
/**
......
......@@ -430,6 +430,123 @@ public function testLookupDestinationIdMapping($num_source_fields, $num_destinat
$this->assertSame(0, count($destination_id));
}
/**
* Setup a database with the given rows.
*
* @param array $source_keys
* The source keys for the ID map table.
* @param array $dest_keys
* The destination keys for the ID map table.
* @param array $rows
* An array of source and destination value arrays for the ID map table.
*
* @return \Drupal\Tests\migrate\Unit\TestSqlIdMap
* An ID map instance for testing.
*/
protected function setupRows($source_keys, $dest_keys, $rows) {
$this->database = $this->getDatabase([]);
$this->sourceIds = array_fill_keys($source_keys, []);
$this->destinationIds = array_fill_keys($dest_keys, []);
$db_keys = [];
foreach (array_keys($source_keys) as $i) {
$db_keys[] = 'sourceid' . ($i + 1);
}
foreach (array_keys($dest_keys) as $i) {
$db_keys[] = 'destid' . ($i + 1);
}
foreach ($rows as $row) {
$values = array_combine($db_keys, $row);
$source_values = array_slice($row, 0, count($source_keys));
$values['source_ids_hash'] = $this->getIdMap()->getSourceIDsHash($source_values);
$this->saveMap($values);
}
return $this->getIdMap();
}
/**
* Tests lookupDestinationIds().
*/
public function testLookupDestinationIds() {
// Simple map with one source and one destination ID.
$id_map = $this->setupRows(['nid'], ['nid'], [
[1, 101],
[2, 102],
[3, 103],
]);
// Lookup nothing, gives nothing.
$this->assertEquals([], $id_map->lookupDestinationIds([]));
// Lookup by complete non-associative list.
$this->assertEquals([[101]], $id_map->lookupDestinationIds([1]));
$this->assertEquals([[102]], $id_map->lookupDestinationIds([2]));
$this->assertEquals([], $id_map->lookupDestinationIds([99]));
// Lookup by complete associative list.
$this->assertEquals([[101]], $id_map->lookupDestinationIds(['nid' => 1]));
$this->assertEquals([[102]], $id_map->lookupDestinationIds(['nid' => 2]));
$this->assertEquals([], $id_map->lookupDestinationIds(['nid' => 99]));
// Map with multiple source and destination IDs.
$id_map = $this->setupRows(['nid', 'language'], ['nid', 'langcode'], [
[1, 'en', 101, 'en'],
[1, 'fr', 101, 'fr'],
[1, 'de', 101, 'de'],
[2, 'en', 102, 'en'],
]);
// Lookup nothing, gives nothing.
$this->assertEquals([], $id_map->lookupDestinationIds([]));
// Lookup by complete non-associative list.
$this->assertEquals([[101, 'en']], $id_map->lookupDestinationIds([1, 'en']));
$this->assertEquals([[101, 'fr']], $id_map->lookupDestinationIds([1, 'fr']));
$this->assertEquals([[102, 'en']], $id_map->lookupDestinationIds([2, 'en']));
$this->assertEquals([], $id_map->lookupDestinationIds([2, 'fr']));
$this->assertEquals([], $id_map->lookupDestinationIds([99, 'en']));
// Lookup by complete associative list.
$this->assertEquals([[101, 'en']], $id_map->lookupDestinationIds(['nid' => 1, 'language' => 'en']));
$this->assertEquals([[101, 'fr']], $id_map->lookupDestinationIds(['nid' => 1, 'language' => 'fr']));
$this->assertEquals([[102, 'en']], $id_map->lookupDestinationIds(['nid' => 2, 'language' => 'en']));
$this->assertEquals([], $id_map->lookupDestinationIds(['nid' => 2, 'language' => 'fr']));
$this->assertEquals([], $id_map->lookupDestinationIds(['nid' => 99, 'language' => 'en']));
// Lookup by partial non-associative list.
$this->assertEquals([[101, 'en'], [101, 'fr'], [101, 'de']], $id_map->lookupDestinationIds([1]));
$this->assertEquals([[102, 'en']], $id_map->lookupDestinationIds([2]));
$this->assertEquals([], $id_map->lookupDestinationIds([99]));
// Lookup by partial associative list.
$this->assertEquals([[101, 'en'], [101, 'fr'], [101, 'de']], $id_map->lookupDestinationIds(['nid' => 1]));
$this->assertEquals([[102, 'en']], $id_map->lookupDestinationIds(['nid' => 2]));
$this->assertEquals([], $id_map->lookupDestinationIds(['nid' => 99]));
// Out-of-order partial associative list.
$this->assertEquals([[101, 'en'], [102, 'en']], $id_map->lookupDestinationIds(['language' => 'en']));
$this->assertEquals([[101, 'fr']], $id_map->lookupDestinationIds(['language' => 'fr']));
$this->assertEquals([], $id_map->lookupDestinationIds(['language' => 'zh']));
// Error conditions.
try {
$id_map->lookupDestinationIds([1, 2, 3]);
$this->fail('Too many source IDs should throw');
}
catch (MigrateException $e) {
$this->assertEquals("Extra unknown items in source IDs", $e->getMessage());
}
try {
$id_map->lookupDestinationIds(['nid' => 1, 'aaa' => '2']);
$this->fail('Unknown source ID key should throw');
}
catch (MigrateException $e) {
$this->assertEquals("Extra unknown items in source IDs", $e->getMessage());
}
// Verify that we are looking up by source_id_hash when all source IDs are
// passed in.
$id_map->getDatabase()->update($id_map->mapTableName())
->condition('sourceid1', 1)
->condition('sourceid2', 'en')
->fields([TestSqlIdMap::SOURCE_IDS_HASH => uniqid()])
->execute();
$this->assertNotEquals([[101, 'en']], $id_map->lookupDestinationIds([1, 'en']));
}
/**
* Tests the getRowByDestination method.
*/
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment