X-Git-Url: http://www.aleph1.co.uk/gitweb/?a=blobdiff_plain;f=web%2Fcore%2Fmodules%2Fmigrate%2Fsrc%2FPlugin%2Fmigrate%2Fid_map%2FSql.php;h=0de83485ffa9a1c9f1bc38f1bb732ace7d4a637e;hb=0bf8d09d2542548982e81a441b1f16e75873a04f;hp=27b756904a97934655a35d65f1943fd0f229c87e;hpb=a2bd1bf0c2c1f1a17d188f4dc0726a45494cefae;p=yaffs-website diff --git a/web/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php b/web/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php index 27b756904..0de83485f 100644 --- a/web/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php +++ b/web/core/modules/migrate/src/Plugin/migrate/id_map/Sql.php @@ -2,10 +2,12 @@ namespace Drupal\migrate\Plugin\migrate\id_map; -use Drupal\Component\Utility\Unicode; +use Drupal\Core\Database\DatabaseException; use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\PluginBase; +use Drupal\migrate\MigrateMessage; +use Drupal\migrate\Audit\HighestIdInterface; use Drupal\migrate\Plugin\MigrationInterface; use Drupal\migrate\Event\MigrateIdMapMessageEvent; use Drupal\migrate\MigrateException; @@ -26,7 +28,7 @@ use Symfony\Component\EventDispatcher\EventDispatcherInterface; * * @PluginID("sql") */ -class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryPluginInterface { +class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryPluginInterface, HighestIdInterface { /** * Column name of hashed source id values. @@ -55,7 +57,7 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP protected $messageTableName; /** - * The migrate message. + * The migrate message service. * * @var \Drupal\migrate\MigrateMessageInterface */ @@ -151,11 +153,26 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP * The configuration for the plugin. * @param \Drupal\migrate\Plugin\MigrationInterface $migration * The migration to do. + * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher + * The event dispatcher. */ public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EventDispatcherInterface $event_dispatcher) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->migration = $migration; $this->eventDispatcher = $event_dispatcher; + $this->message = new MigrateMessage(); + + if (!isset($this->database)) { + $this->database = \Drupal::database(); + } + + // Default generated table names, limited to 63 characters. + $machine_name = str_replace(':', '__', $this->migration->id()); + $prefix_length = strlen($this->database->tablePrefix()); + $this->mapTableName = 'migrate_map_' . mb_strtolower($machine_name); + $this->mapTableName = mb_substr($this->mapTableName, 0, 63 - $prefix_length); + $this->messageTableName = 'migrate_message_' . mb_strtolower($machine_name); + $this->messageTableName = mb_substr($this->messageTableName, 0, 63 - $prefix_length); } /** @@ -182,7 +199,7 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP * @return string * An hash containing the hashed values of the source identifiers. */ - public function getSourceIDsHash(array $source_id_values) { + public function getSourceIdsHash(array $source_id_values) { // When looking up the destination ID we require an array with both the // source key and value, e.g. ['nid' => 41]. In this case, $source_id_values // need to be ordered the same order as $this->sourceIdFields(). @@ -241,7 +258,6 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP * The map table name. */ public function mapTableName() { - $this->init(); return $this->mapTableName; } @@ -252,7 +268,6 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP * The message table name. */ public function messageTableName() { - $this->init(); return $this->messageTableName; } @@ -273,9 +288,6 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP * The database connection object. */ public function getDatabase() { - if (!isset($this->database)) { - $this->database = \Drupal::database(); - } $this->init(); return $this->database; } @@ -286,13 +298,6 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP protected function init() { if (!$this->initialized) { $this->initialized = TRUE; - // Default generated table names, limited to 63 characters. - $machine_name = str_replace(':', '__', $this->migration->id()); - $prefix_length = strlen($this->getDatabase()->tablePrefix()); - $this->mapTableName = 'migrate_map_' . Unicode::strtolower($machine_name); - $this->mapTableName = Unicode::substr($this->mapTableName, 0, 63 - $prefix_length); - $this->messageTableName = 'migrate_message_' . Unicode::strtolower($machine_name); - $this->messageTableName = Unicode::substr($this->messageTableName, 0, 63 - $prefix_length); $this->ensureTables(); } } @@ -486,7 +491,7 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP public function getRowBySource(array $source_id_values) { $query = $this->getDatabase()->select($this->mapTableName(), 'map') ->fields('map'); - $query->condition(static::SOURCE_IDS_HASH, $this->getSourceIDsHash($source_id_values)); + $query->condition(static::SOURCE_IDS_HASH, $this->getSourceIdsHash($source_id_values)); $result = $query->execute(); return $result->fetchAssoc(); } @@ -523,7 +528,7 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP /** * {@inheritdoc} */ - public function lookupSourceID(array $destination_id_values) { + public function lookupSourceId(array $destination_id_values) { $source_id_fields = $this->sourceIdFields(); $query = $this->getDatabase()->select($this->mapTableName(), 'map'); foreach ($source_id_fields as $source_field_name => $idmap_field_name) { @@ -557,9 +562,13 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP $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]; + // Ensure to handle array elements with a NULL value. + if (array_key_exists($field_name, $source_id_values)) { + // Associative $source_id_values can have fields out of order. + if (isset($source_id_values[$field_name])) { + // Only add a condition if the value is not NULL. + $conditions[$db_field] = $source_id_values[$field_name]; + } unset($source_id_values[$field_name]); } } @@ -574,14 +583,15 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP } if (!empty($source_id_values)) { - throw new MigrateException("Extra unknown items in source IDs"); + $var_dump = var_export($source_id_values, TRUE); + throw new MigrateException(sprintf("Extra unknown items in source IDs: %s", $var_dump)); } $query = $this->getDatabase()->select($this->mapTableName(), 'map') ->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))); + $query->condition(self::SOURCE_IDS_HASH, $this->getSourceIdsHash(array_values($conditions))); } else { foreach ($conditions as $db_field => $value) { @@ -631,7 +641,7 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP if ($this->migration->getTrackLastImported()) { $fields['last_imported'] = time(); } - $keys = [static::SOURCE_IDS_HASH => $this->getSourceIDsHash($source_id_values)]; + $keys = [static::SOURCE_IDS_HASH => $this->getSourceIdsHash($source_id_values)]; // Notify anyone listening of the map row we're about to save. $this->eventDispatcher->dispatch(MigrateEvents::MAP_SAVE, new MigrateMapSaveEvent($this, $fields)); $this->getDatabase()->merge($this->mapTableName()) @@ -650,7 +660,7 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP return; } } - $fields[static::SOURCE_IDS_HASH] = $this->getSourceIDsHash($source_id_values); + $fields[static::SOURCE_IDS_HASH] = $this->getSourceIdsHash($source_id_values); $fields['level'] = $level; $fields['message'] = $message; $this->getDatabase()->insert($this->messageTableName()) @@ -669,7 +679,7 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP $query = $this->getDatabase()->select($this->messageTableName(), 'msg') ->fields('msg'); if ($source_id_values) { - $query->condition(static::SOURCE_IDS_HASH, $this->getSourceIDsHash($source_id_values)); + $query->condition(static::SOURCE_IDS_HASH, $this->getSourceIdsHash($source_id_values)); } if ($level) { @@ -691,21 +701,17 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP * {@inheritdoc} */ public function processedCount() { - return $this->getDatabase()->select($this->mapTableName()) - ->countQuery() - ->execute() - ->fetchField(); + return $this->countHelper(NULL, $this->mapTableName()); } /** * {@inheritdoc} */ public function importedCount() { - return $this->getDatabase()->select($this->mapTableName()) - ->condition('source_row_status', [MigrateIdMapInterface::STATUS_IMPORTED, MigrateIdMapInterface::STATUS_NEEDS_UPDATE], 'IN') - ->countQuery() - ->execute() - ->fetchField(); + return $this->countHelper([ + MigrateIdMapInterface::STATUS_IMPORTED, + MigrateIdMapInterface::STATUS_NEEDS_UPDATE, + ]); } /** @@ -732,20 +738,28 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP /** * Counts records in a table. * - * @param int $status - * An integer for the source_row_status column. + * @param int|array $status + * (optional) Status code(s) to filter the source_row_status column. * @param string $table * (optional) The table to work. Defaults to NULL. * * @return int * The number of records. */ - protected function countHelper($status, $table = NULL) { - $query = $this->getDatabase()->select($table ?: $this->mapTableName()); + protected function countHelper($status = NULL, $table = NULL) { + // Use database directly to avoid creating tables. + $query = $this->database->select($table ?: $this->mapTableName()); if (isset($status)) { - $query->condition('source_row_status', $status); + $query->condition('source_row_status', $status, is_array($status) ? 'IN' : '='); } - return $query->countQuery()->execute()->fetchField(); + try { + $count = (int) $query->countQuery()->execute()->fetchField(); + } + catch (DatabaseException $e) { + // The table does not exist, therefore there are no records. + $count = 0; + } + return $count; } /** @@ -758,13 +772,13 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP if (!$messages_only) { $map_query = $this->getDatabase()->delete($this->mapTableName()); - $map_query->condition(static::SOURCE_IDS_HASH, $this->getSourceIDsHash($source_id_values)); + $map_query->condition(static::SOURCE_IDS_HASH, $this->getSourceIdsHash($source_id_values)); // Notify anyone listening of the map row we're about to delete. $this->eventDispatcher->dispatch(MigrateEvents::MAP_DELETE, new MigrateMapDeleteEvent($this, $source_id_values)); $map_query->execute(); } $message_query = $this->getDatabase()->delete($this->messageTableName()); - $message_query->condition(static::SOURCE_IDS_HASH, $this->getSourceIDsHash($source_id_values)); + $message_query->condition(static::SOURCE_IDS_HASH, $this->getSourceIdsHash($source_id_values)); $message_query->execute(); } @@ -774,7 +788,7 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP public function deleteDestination(array $destination_id_values) { $map_query = $this->getDatabase()->delete($this->mapTableName()); $message_query = $this->getDatabase()->delete($this->messageTableName()); - $source_id_values = $this->lookupSourceID($destination_id_values); + $source_id_values = $this->lookupSourceId($destination_id_values); if (!empty($source_id_values)) { foreach ($this->destinationIdFields() as $field_name => $destination_id) { $map_query->condition($destination_id, $destination_id_values[$field_name]); @@ -783,7 +797,7 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP $this->eventDispatcher->dispatch(MigrateEvents::MAP_DELETE, new MigrateMapDeleteEvent($this, $source_id_values)); $map_query->execute(); - $message_query->condition(static::SOURCE_IDS_HASH, $this->getSourceIDsHash($source_id_values)); + $message_query->condition(static::SOURCE_IDS_HASH, $this->getSourceIdsHash($source_id_values)); $message_query->execute(); } } @@ -923,4 +937,70 @@ class Sql extends PluginBase implements MigrateIdMapInterface, ContainerFactoryP return $this->currentRow !== FALSE; } + /** + * Returns the migration plugin manager. + * + * @todo Inject as a dependency in https://www.drupal.org/node/2919158. + * + * @return \Drupal\migrate\Plugin\MigrationPluginManagerInterface + * The migration plugin manager. + */ + protected function getMigrationPluginManager() { + return \Drupal::service('plugin.manager.migration'); + } + + /** + * {@inheritdoc} + */ + public function getHighestId() { + array_filter( + $this->migration->getDestinationPlugin()->getIds(), + function (array $id) { + if ($id['type'] !== 'integer') { + throw new \LogicException('Cannot determine the highest migrated ID without an integer ID column'); + } + } + ); + + // List of mapping tables to look in for the highest ID. + $map_tables = [ + $this->migration->id() => $this->mapTableName(), + ]; + + // If there's a bundle, it means we have a derived migration and we need to + // find all the mapping tables from the related derived migrations. + if ($base_id = substr($this->migration->id(), 0, strpos($this->migration->id(), static::DERIVATIVE_SEPARATOR))) { + $migration_manager = $this->getMigrationPluginManager(); + $migrations = $migration_manager->getDefinitions(); + foreach ($migrations as $migration_id => $migration) { + if ($migration['id'] === $base_id) { + // Get this derived migration's mapping table and add it to the list + // of mapping tables to look in for the highest ID. + $stub = $migration_manager->createInstance($migration_id); + $map_tables[$migration_id] = $stub->getIdMap()->mapTableName(); + } + } + } + + // Get the highest id from the list of map tables. + $ids = [0]; + foreach ($map_tables as $map_table) { + // If the map_table does not exist then continue on to the next map_table. + if (!$this->getDatabase()->schema()->tableExists($map_table)) { + continue; + } + + $query = $this->getDatabase()->select($map_table, 'map') + ->fields('map', $this->destinationIdFields()) + ->range(0, 1); + foreach (array_values($this->destinationIdFields()) as $order_field) { + $query->orderBy($order_field, 'DESC'); + } + $ids[] = $query->execute()->fetchField(); + } + + // Return the highest of all the mapped IDs. + return (int) max($ids); + } + }