Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
50.00% |
5 / 10 |
CRAP | |
44.74% |
34 / 76 |
SqlBase | |
0.00% |
0 / 1 |
|
54.55% |
6 / 11 |
204.83 | |
44.74% |
34 / 76 |
__construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
create | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
__toString | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
getDatabase | |
0.00% |
0 / 1 |
4.94 | |
40.00% |
2 / 5 |
|||
setUpDatabase | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 9 |
|||
select | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
prepareQuery | |
100.00% |
1 / 1 |
1 | |
100.00% |
5 / 5 |
|||
initializeIterator | |
0.00% |
0 / 1 |
72.75 | |
25.00% |
9 / 36 |
|||
query | |
100.00% |
1 / 1 |
1 | |
100.00% |
0 / 0 |
|||
count | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
mapJoinable | |
100.00% |
1 / 1 |
6 | |
100.00% |
12 / 12 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\migrate\Plugin\migrate\source\SqlBase. | |
*/ | |
namespace Drupal\migrate\Plugin\migrate\source; | |
use Drupal\Core\Database\Database; | |
use Drupal\Core\Plugin\ContainerFactoryPluginInterface; | |
use Drupal\Core\State\StateInterface; | |
use Drupal\migrate\Entity\MigrationInterface; | |
use Drupal\migrate\Plugin\migrate\id_map\Sql; | |
use Drupal\migrate\Plugin\MigrateIdMapInterface; | |
use Symfony\Component\DependencyInjection\ContainerInterface; | |
/** | |
* Sources whose data may be fetched via DBTNG. | |
* | |
* By default, an existing database connection with key 'migrate' and target | |
* 'default' is used. These may be overridden with explicit 'key' and/or | |
* 'target' configuration keys. In addition, if the configuration key 'database' | |
* is present, it is used as a database connection information array to define | |
* the connection. | |
*/ | |
abstract class SqlBase extends SourcePluginBase implements ContainerFactoryPluginInterface { | |
/** | |
* The query string. | |
* | |
* @var \Drupal\Core\Database\Query\SelectInterface | |
*/ | |
protected $query; | |
/** | |
* The database object. | |
* | |
* @var \Drupal\Core\Database\Connection | |
*/ | |
protected $database; | |
/** | |
* State service for retrieving database info. | |
* | |
* @var \Drupal\Core\State\StateInterface | |
*/ | |
protected $state; | |
/** | |
* {@inheritdoc} | |
*/ | |
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, StateInterface $state) { | |
parent::__construct($configuration, $plugin_id, $plugin_definition, $migration); | |
$this->state = $state; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) { | |
return new static( | |
$configuration, | |
$plugin_id, | |
$plugin_definition, | |
$migration, | |
$container->get('state') | |
); | |
} | |
/** | |
* Prints the query string when the object is used as a string. | |
* | |
* @return string | |
* The query string. | |
*/ | |
public function __toString() { | |
return (string) $this->query(); | |
} | |
/** | |
* Gets the database connection object. | |
* | |
* @return \Drupal\Core\Database\Connection | |
* The database connection. | |
*/ | |
public function getDatabase() { | |
if (!isset($this->database)) { | |
// See if the database info is in state - if not, fallback to | |
// configuration. | |
if (isset($this->configuration['database_state_key'])) { | |
$this->database = $this->setUpDatabase($this->state->get($this->configuration['database_state_key'])); | |
} | |
else { | |
$this->database = $this->setUpDatabase($this->configuration); | |
} | |
} | |
return $this->database; | |
} | |
/** | |
* Gets a connection to the referenced database. | |
* | |
* This method will add the database connection if necessary. | |
* | |
* @param array $database_info | |
* Configuration for the source database connection. The keys are: | |
* 'key' - The database connection key. | |
* 'target' - The database connection target. | |
* 'database' - Database configuration array as accepted by | |
* Database::addConnectionInfo. | |
* | |
* @return \Drupal\Core\Database\Connection | |
* The connection to use for this plugin's queries. | |
*/ | |
protected function setUpDatabase(array $database_info) { | |
if (isset($database_info['key'])) { | |
$key = $database_info['key']; | |
} | |
else { | |
$key = 'migrate'; | |
} | |
if (isset($database_info['target'])) { | |
$target = $database_info['target']; | |
} | |
else { | |
$target = 'default'; | |
} | |
if (isset($database_info['database'])) { | |
Database::addConnectionInfo($key, $target, $database_info['database']); | |
} | |
return Database::getConnection($target, $key); | |
} | |
/** | |
* Wrapper for database select. | |
*/ | |
protected function select($table, $alias = NULL, array $options = array()) { | |
$options['fetch'] = \PDO::FETCH_ASSOC; | |
return $this->getDatabase()->select($table, $alias, $options); | |
} | |
/** | |
* Adds tags and metadata to the query. | |
* | |
* @return \Drupal\Core\Database\Query\SelectInterface | |
* The query with additional tags and metadata. | |
*/ | |
protected function prepareQuery() { | |
$this->query = clone $this->query(); | |
$this->query->addTag('migrate'); | |
$this->query->addTag('migrate_' . $this->migration->id()); | |
$this->query->addMetaData('migration', $this->migration); | |
return $this->query; | |
} | |
/** | |
* Implementation of MigrateSource::performRewind(). | |
* | |
* We could simply execute the query and be functionally correct, but | |
* we will take advantage of the PDO-based API to optimize the query up-front. | |
*/ | |
protected function initializeIterator() { | |
$this->prepareQuery(); | |
$high_water_property = $this->migration->get('highWaterProperty'); | |
// Get the key values, for potential use in joining to the map table. | |
$keys = array(); | |
// The rules for determining what conditions to add to the query are as | |
// follows (applying first applicable rule): | |
// 1. If the map is joinable, join it. We will want to accept all rows | |
// which are either not in the map, or marked in the map as NEEDS_UPDATE. | |
// Note that if high water fields are in play, we want to accept all rows | |
// above the high water mark in addition to those selected by the map | |
// conditions, so we need to OR them together (but AND with any existing | |
// conditions in the query). So, ultimately the SQL condition will look | |
// like (original conditions) AND (map IS NULL OR map needs update | |
// OR above high water). | |
$conditions = $this->query->orConditionGroup(); | |
$condition_added = FALSE; | |
if (empty($this->configuration['ignore_map']) && $this->mapJoinable()) { | |
// Build the join to the map table. Because the source key could have | |
// multiple fields, we need to build things up. | |
$count = 1; | |
$map_join = ''; | |
$delimiter = ''; | |
foreach ($this->getIds() as $field_name => $field_schema) { | |
if (isset($field_schema['alias'])) { | |
$field_name = $field_schema['alias'] . '.' . $this->query->escapeField($field_name); | |
} | |
$map_join .= "$delimiter$field_name = map.sourceid" . $count++; | |
$delimiter = ' AND '; | |
} | |
$alias = $this->query->leftJoin($this->migration->getIdMap()->getQualifiedMapTableName(), 'map', $map_join); | |
$conditions->isNull($alias . '.sourceid1'); | |
$conditions->condition($alias . '.source_row_status', MigrateIdMapInterface::STATUS_NEEDS_UPDATE); | |
$condition_added = TRUE; | |
// And as long as we have the map table, add its data to the row. | |
$n = count($this->getIds()); | |
for ($count = 1; $count <= $n; $count++) { | |
$map_key = 'sourceid' . $count; | |
$this->query->addField($alias, $map_key, "migrate_map_$map_key"); | |
} | |
if ($n = count($this->migration->get('destinationIds'))) { | |
for ($count = 1; $count <= $n; $count++) { | |
$map_key = 'destid' . $count++; | |
$this->query->addField($alias, $map_key, "migrate_map_$map_key"); | |
} | |
} | |
$this->query->addField($alias, 'source_row_status', 'migrate_map_source_row_status'); | |
} | |
// 2. If we are using high water marks, also include rows above the mark. | |
// But, include all rows if the high water mark is not set. | |
if (isset($high_water_property['name']) && ($high_water = $this->migration->getHighWater()) !== '') { | |
if (isset($high_water_property['alias'])) { | |
$high_water = $high_water_property['alias'] . '.' . $high_water_property['name']; | |
} | |
else { | |
$high_water = $high_water_property['name']; | |
} | |
$conditions->condition($high_water, $high_water, '>'); | |
$condition_added = TRUE; | |
} | |
if ($condition_added) { | |
$this->query->condition($conditions); | |
} | |
return new \IteratorIterator($this->query->execute()); | |
} | |
/** | |
* @return \Drupal\Core\Database\Query\SelectInterface | |
*/ | |
abstract public function query(); | |
/** | |
* {@inheritdoc} | |
*/ | |
public function count() { | |
return $this->query()->countQuery()->execute()->fetchField(); | |
} | |
/** | |
* Checks if we can join against the map table. | |
* | |
* This function specifically catches issues when we're migrating with | |
* unique sets of credentials for the source and destination database. | |
* | |
* @return bool | |
* TRUE if we can join against the map table otherwise FALSE. | |
*/ | |
protected function mapJoinable() { | |
if (!$this->getIds()) { | |
return FALSE; | |
} | |
$id_map = $this->migration->getIdMap(); | |
if (!$id_map instanceof Sql) { | |
return FALSE; | |
} | |
$id_map_database_options = $id_map->getDatabase()->getConnectionOptions(); | |
$source_database_options = $this->getDatabase()->getConnectionOptions(); | |
foreach (array('username', 'password', 'host', 'port', 'namespace', 'driver') as $key) { | |
if (isset($source_database_options[$key])) { | |
if ($id_map_database_options[$key] != $source_database_options[$key]) { | |
return FALSE; | |
} | |
} | |
} | |
return TRUE; | |
} | |
} |