Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 29 |
CRAP | |
15.74% |
17 / 108 |
Migration | |
0.00% |
0 / 1 |
|
0.00% |
0 / 29 |
2070.37 | |
15.74% |
17 / 108 |
getSourcePlugin | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 3 |
|||
getProcessPlugins | |
0.00% |
0 / 1 |
21.82 | |
40.00% |
6 / 15 |
|||
getProcessNormalized | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 9 |
|||
getDestinationPlugin | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 5 |
|||
getIdMap | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 5 |
|||
getHighWaterStorage | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 3 |
|||
getHighWater | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
saveHighWater | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
checkRequirements | |
0.00% |
0 / 1 |
6.02 | |
91.67% |
11 / 12 |
|||
getEntityManager | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 3 |
|||
setStatus | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
getStatus | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
getStatusLabel | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 4 |
|||
getInterruptionResult | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
clearInterruptionResult | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
interruptMigration | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
allRowsProcessed | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 5 |
|||
set | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 5 |
|||
getProcess | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
setProcess | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
setProcessOfProperty | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
mergeProcessOfProperty | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 5 |
|||
getSystemOfRecord | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
setSystemOfRecord | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
isTrackLastImported | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
setTrackLastImported | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
getMigrationDependencies | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
trustData | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
calculateDependencies | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 8 |
<?php | |
/** | |
* @file | |
* Contains \Drupal\migrate\Entity\Migration. | |
*/ | |
namespace Drupal\migrate\Entity; | |
use Drupal\Core\Config\Entity\ConfigEntityBase; | |
use Drupal\migrate\Exception\RequirementsException; | |
use Drupal\migrate\MigrateException; | |
use Drupal\migrate\MigrateSkipRowException; | |
use Drupal\migrate\Plugin\MigrateIdMapInterface; | |
use Drupal\migrate\Plugin\RequirementsInterface; | |
use Drupal\Component\Utility\NestedArray; | |
/** | |
* Defines the Migration entity. | |
* | |
* The migration entity stores the information about a single migration, like | |
* the source, process and destination plugins. | |
* | |
* @ConfigEntityType( | |
* id = "migration", | |
* label = @Translation("Migration"), | |
* handlers = { | |
* "storage" = "Drupal\migrate\MigrationStorage" | |
* }, | |
* entity_keys = { | |
* "id" = "id", | |
* "label" = "label", | |
* "weight" = "weight" | |
* } | |
* ) | |
*/ | |
class Migration extends ConfigEntityBase implements MigrationInterface, RequirementsInterface { | |
/** | |
* The migration ID (machine name). | |
* | |
* @var string | |
*/ | |
protected $id; | |
/** | |
* The human-readable label for the migration. | |
* | |
* @var string | |
*/ | |
protected $label; | |
/** | |
* The plugin ID for the row. | |
* | |
* @var string | |
*/ | |
protected $row; | |
/** | |
* The source configuration, with at least a 'plugin' key. | |
* | |
* Used to initialize the $sourcePlugin. | |
* | |
* @var array | |
*/ | |
protected $source; | |
/** | |
* The source plugin. | |
* | |
* @var \Drupal\migrate\Plugin\MigrateSourceInterface | |
*/ | |
protected $sourcePlugin; | |
/** | |
* The configuration describing the process plugins. | |
* | |
* This is a strictly internal property and should not returned to calling | |
* code, use getProcess() instead. | |
* | |
* @var array | |
*/ | |
protected $process = []; | |
/** | |
* The cached process plugins. | |
* | |
* @var array | |
*/ | |
protected $processPlugins = []; | |
/** | |
* The destination configuration, with at least a 'plugin' key. | |
* | |
* Used to initialize $destinationPlugin. | |
* | |
* @var array | |
*/ | |
protected $destination; | |
/** | |
* The destination plugin. | |
* | |
* @var \Drupal\migrate\Plugin\MigrateDestinationInterface | |
*/ | |
protected $destinationPlugin; | |
/** | |
* The identifier map data. | |
* | |
* Used to initialize $idMapPlugin. | |
* | |
* @var string | |
*/ | |
protected $idMap = []; | |
/** | |
* The identifier map. | |
* | |
* @var \Drupal\migrate\Plugin\MigrateIdMapInterface | |
*/ | |
protected $idMapPlugin; | |
/** | |
* The source identifiers. | |
* | |
* An array of source identifiers: the keys are the name of the properties, | |
* the values are dependent on the ID map plugin. | |
* | |
* @var array | |
*/ | |
protected $sourceIds = []; | |
/** | |
* The destination identifiers. | |
* | |
* An array of destination identifiers: the keys are the name of the | |
* properties, the values are dependent on the ID map plugin. | |
* | |
* @var array | |
*/ | |
protected $destinationIds = []; | |
/** | |
* Information on the high water mark. | |
* | |
* @var array | |
*/ | |
protected $highWaterProperty; | |
/** | |
* Indicate whether the primary system of record for this migration is the | |
* source, or the destination (Drupal). In the source case, migration of | |
* an existing object will completely replace the Drupal object with data from | |
* the source side. In the destination case, the existing Drupal object will | |
* be loaded, then changes from the source applied; also, rollback will not be | |
* supported. | |
* | |
* @var string | |
*/ | |
protected $systemOfRecord = self::SOURCE; | |
/** | |
* Specify value of source_row_status for current map row. Usually set by | |
* MigrateFieldHandler implementations. | |
* | |
* @var int | |
*/ | |
protected $sourceRowStatus = MigrateIdMapInterface::STATUS_IMPORTED; | |
/** | |
* @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface | |
*/ | |
protected $highWaterStorage; | |
/** | |
* Track time of last import if TRUE. | |
* | |
* @var bool | |
*/ | |
protected $trackLastImported = FALSE; | |
/** | |
* These migrations must be already executed before this migration can run. | |
* | |
* @var array | |
*/ | |
protected $requirements = []; | |
/** | |
* These migrations, if run, must be executed before this migration. | |
* | |
* These are different from the configuration dependencies. Migration | |
* dependencies are only used to store relationships between migrations. | |
* | |
* The migration_dependencies value is structured like this: | |
* @code | |
* array( | |
* 'required' => array( | |
* // An array of migration IDs that must be run before this migration. | |
* ), | |
* 'optional' => array( | |
* // An array of migration IDs that, if they exist, must be run before | |
* // this migration. | |
* ), | |
* ); | |
* @endcode | |
* | |
* @var array | |
*/ | |
protected $migration_dependencies = []; | |
/** | |
* The migration's configuration dependencies. | |
* | |
* These store any dependencies on modules or other configuration (including | |
* other migrations) that must be available before the migration can be | |
* created. | |
* | |
* @see \Drupal\Core\Config\Entity\ConfigDependencyManager | |
* | |
* @var array | |
*/ | |
protected $dependencies = []; | |
/** | |
* The ID of the template from which this migration was derived, if any. | |
* | |
* @var string|NULL | |
*/ | |
protected $template; | |
/** | |
* The entity manager. | |
* | |
* @var \Drupal\Core\Entity\EntityManagerInterface | |
*/ | |
protected $entityManager; | |
/** | |
* Labels corresponding to each defined status. | |
* | |
* @var array | |
*/ | |
protected $statusLabels = [ | |
self::STATUS_IDLE => 'Idle', | |
self::STATUS_IMPORTING => 'Importing', | |
self::STATUS_ROLLING_BACK => 'Rolling back', | |
self::STATUS_STOPPING => 'Stopping', | |
self::STATUS_DISABLED => 'Disabled', | |
]; | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getSourcePlugin() { | |
if (!isset($this->sourcePlugin)) { | |
$this->sourcePlugin = \Drupal::service('plugin.manager.migrate.source')->createInstance($this->source['plugin'], $this->source, $this); | |
} | |
return $this->sourcePlugin; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getProcessPlugins(array $process = NULL) { | |
if (!isset($process)) { | |
$process = $this->process; | |
} | |
$index = serialize($process); | |
if (!isset($this->processPlugins[$index])) { | |
$this->processPlugins[$index] = array(); | |
foreach ($this->getProcessNormalized($process) as $property => $configurations) { | |
$this->processPlugins[$index][$property] = array(); | |
foreach ($configurations as $configuration) { | |
if (isset($configuration['source'])) { | |
$this->processPlugins[$index][$property][] = \Drupal::service('plugin.manager.migrate.process')->createInstance('get', $configuration, $this); | |
} | |
// Get is already handled. | |
if ($configuration['plugin'] != 'get') { | |
$this->processPlugins[$index][$property][] = \Drupal::service('plugin.manager.migrate.process')->createInstance($configuration['plugin'], $configuration, $this); | |
} | |
if (!$this->processPlugins[$index][$property]) { | |
throw new MigrateException("Invalid process configuration for $property"); | |
} | |
} | |
} | |
} | |
return $this->processPlugins[$index]; | |
} | |
/** | |
* Resolve shorthands into a list of plugin configurations. | |
* | |
* @param array $process | |
* A process configuration array. | |
* | |
* @return array | |
* The normalized process configuration. | |
*/ | |
protected function getProcessNormalized(array $process) { | |
$normalized_configurations = array(); | |
foreach ($process as $destination => $configuration) { | |
if (is_string($configuration)) { | |
$configuration = array( | |
'plugin' => 'get', | |
'source' => $configuration, | |
); | |
} | |
if (isset($configuration['plugin'])) { | |
$configuration = array($configuration); | |
} | |
$normalized_configurations[$destination] = $configuration; | |
} | |
return $normalized_configurations; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getDestinationPlugin($stub_being_requested = FALSE) { | |
if ($stub_being_requested && !empty($this->destination['no_stub'])) { | |
throw new MigrateSkipRowException; | |
} | |
if (!isset($this->destinationPlugin)) { | |
$this->destinationPlugin = \Drupal::service('plugin.manager.migrate.destination')->createInstance($this->destination['plugin'], $this->destination, $this); | |
} | |
return $this->destinationPlugin; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getIdMap() { | |
if (!isset($this->idMapPlugin)) { | |
$configuration = $this->idMap; | |
$plugin = isset($configuration['plugin']) ? $configuration['plugin'] : 'sql'; | |
$this->idMapPlugin = \Drupal::service('plugin.manager.migrate.id_map')->createInstance($plugin, $configuration, $this); | |
} | |
return $this->idMapPlugin; | |
} | |
/** | |
* Get the high water storage object. | |
* | |
* @return \Drupal\Core\KeyValueStore\KeyValueStoreInterface | |
* The storage object. | |
*/ | |
protected function getHighWaterStorage() { | |
if (!isset($this->highWaterStorage)) { | |
$this->highWaterStorage = \Drupal::keyValue('migrate:high_water'); | |
} | |
return $this->highWaterStorage; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getHighWater() { | |
return $this->getHighWaterStorage()->get($this->id()); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function saveHighWater($high_water) { | |
$this->getHighWaterStorage()->set($this->id(), $high_water); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function checkRequirements() { | |
// Check whether the current migration source and destination plugin | |
// requirements are met or not. | |
if ($this->getSourcePlugin() instanceof RequirementsInterface) { | |
$this->getSourcePlugin()->checkRequirements(); | |
} | |
if ($this->getDestinationPlugin() instanceof RequirementsInterface) { | |
$this->getDestinationPlugin()->checkRequirements(); | |
} | |
/** @var \Drupal\migrate\Entity\MigrationInterface[] $required_migrations */ | |
$required_migrations = $this->getEntityManager()->getStorage('migration')->loadMultiple($this->requirements); | |
$missing_migrations = array_diff($this->requirements, array_keys($required_migrations)); | |
// Check if the dependencies are in good shape. | |
foreach ($required_migrations as $migration_id => $required_migration) { | |
if (!$required_migration->allRowsProcessed()) { | |
$missing_migrations[] = $migration_id; | |
} | |
} | |
if ($missing_migrations) { | |
throw new RequirementsException('Missing migrations ' . implode(', ', $missing_migrations) . '.', ['requirements' => $missing_migrations]); | |
} | |
} | |
/** | |
* Get the entity manager. | |
* | |
* @return \Drupal\Core\Entity\EntityManagerInterface | |
* The entity manager. | |
*/ | |
protected function getEntityManager() { | |
if (!isset($this->entityManager)) { | |
$this->entityManager = \Drupal::entityManager(); | |
} | |
return $this->entityManager; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function setStatus($status) { | |
\Drupal::keyValue('migrate_status')->set($this->id(), $status); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getStatus() { | |
return \Drupal::keyValue('migrate_status')->get($this->id(), static::STATUS_IDLE); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getStatusLabel() { | |
$status = $this->getStatus(); | |
if (isset($this->statusLabels[$status])) { | |
return $this->statusLabels[$status]; | |
} | |
else { | |
return ''; | |
} | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getInterruptionResult() { | |
return \Drupal::keyValue('migrate_interruption_result')->get($this->id(), static::RESULT_INCOMPLETE); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function clearInterruptionResult() { | |
\Drupal::keyValue('migrate_interruption_result')->delete($this->id()); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function interruptMigration($result) { | |
$this->setStatus(MigrationInterface::STATUS_STOPPING); | |
\Drupal::keyValue('migrate_interruption_result')->set($this->id(), $result); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function allRowsProcessed() { | |
$source_count = $this->getSourcePlugin()->count(); | |
// If the source is uncountable, we have no way of knowing if it's | |
// complete, so stipulate that it is. | |
if ($source_count < 0) { | |
return TRUE; | |
} | |
$processed_count = $this->getIdMap()->processedCount(); | |
// We don't use == because in some circumstances (like unresolved stubs | |
// being created), the processed count may be higher than the available | |
// source rows. | |
return $source_count <= $processed_count; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function set($property_name, $value) { | |
if ($property_name == 'source') { | |
// Invalidate the source plugin. | |
unset($this->sourcePlugin); | |
} | |
elseif ($property_name === 'destination') { | |
// Invalidate the destination plugin. | |
unset($this->destinationPlugin); | |
} | |
return parent::set($property_name, $value); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getProcess() { | |
return $this->getProcessNormalized($this->process); | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function setProcess(array $process) { | |
$this->process = $process; | |
return $this; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function setProcessOfProperty($property, $process_of_property) { | |
$this->process[$property] = $process_of_property; | |
return $this; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function mergeProcessOfProperty($property, array $process_of_property) { | |
// If we already have a process value then merge the incoming process array | |
//otherwise simply set it. | |
$current_process = $this->getProcess(); | |
if (isset($current_process[$property])) { | |
$this->process = NestedArray::mergeDeepArray([$current_process, $this->getProcessNormalized([$property => $process_of_property])], TRUE); | |
} | |
else { | |
$this->setProcessOfProperty($property, $process_of_property); | |
} | |
return $this; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getSystemOfRecord() { | |
return $this->systemOfRecord; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function setSystemOfRecord($system_of_record) { | |
$this->systemOfRecord = $system_of_record; | |
return $this; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function isTrackLastImported() { | |
return $this->trackLastImported; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function setTrackLastImported($track_last_imported) { | |
$this->trackLastImported = (bool) $track_last_imported; | |
return $this; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function getMigrationDependencies() { | |
return $this->migration_dependencies + ['required' => [], 'optional' => []]; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function trustData() { | |
// Migrations cannot be trusted since they are often written by hand and not | |
// through a UI. | |
$this->trustedData = FALSE; | |
return $this; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function calculateDependencies() { | |
parent::calculateDependencies(); | |
$this->calculatePluginDependencies($this->getSourcePlugin()); | |
$this->calculatePluginDependencies($this->getDestinationPlugin()); | |
// Add hard dependencies on required migrations. | |
$dependencies = $this->getEntityManager()->getStorage($this->entityTypeId) | |
->getVariantIds($this->getMigrationDependencies()['required']); | |
foreach ($dependencies as $dependency) { | |
$this->addDependency('config', $this->getEntityType()->getConfigPrefix() . '.' . $dependency); | |
} | |
return $this; | |
} | |
} |