2015-08-17 17:00:26 -07:00
< ? php
2016-04-20 09:56:34 -07:00
namespace Drupal\migrate\Plugin ;
2015-08-17 17:00:26 -07:00
2016-04-20 09:56:34 -07:00
use Drupal\Core\Plugin\ContainerFactoryPluginInterface ;
use Drupal\Core\Plugin\PluginBase ;
2015-08-17 17:00:26 -07:00
use Drupal\migrate\Exception\RequirementsException ;
use Drupal\migrate\MigrateException ;
use Drupal\migrate\MigrateSkipRowException ;
use Drupal\Component\Utility\NestedArray ;
2016-04-20 09:56:34 -07:00
use Symfony\Component\DependencyInjection\ContainerInterface ;
2015-08-17 17:00:26 -07:00
/**
2016-04-20 09:56:34 -07:00
* Defines the Migration plugin .
2015-08-17 17:00:26 -07:00
*
2016-04-20 09:56:34 -07:00
* The migration process plugin represents one single migration and acts like a
* container for the information about a single migration such as the source ,
* process and destination plugins .
2015-08-17 17:00:26 -07:00
*/
2016-04-20 09:56:34 -07:00
class Migration extends PluginBase implements MigrationInterface , RequirementsInterface , ContainerFactoryPluginInterface {
2015-08-17 17:00:26 -07:00
/**
* 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
*/
2016-01-06 16:31:26 -08:00
protected $process = [];
2015-08-17 17:00:26 -07:00
/**
* 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 = [];
/**
2016-04-20 09:56:34 -07:00
* Information on the property used as the high watermark .
*
* Array of 'name' & ( optional ) db 'alias' properties used for high watermark .
2015-08-17 17:00:26 -07:00
*
* @ 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 = [];
2015-09-04 13:20:09 -07:00
/**
2016-04-20 09:56:34 -07:00
* The migration plugin manager for loading other migration plugins .
2015-09-04 13:20:09 -07:00
*
2016-04-20 09:56:34 -07:00
* @ var \Drupal\migrate\Plugin\MigrationPluginManagerInterface
2015-09-04 13:20:09 -07:00
*/
2016-04-20 09:56:34 -07:00
protected $migrationPluginManager ;
2015-09-04 13:20:09 -07:00
2015-08-17 17:00:26 -07:00
/**
2016-04-20 09:56:34 -07:00
* The source plugin manager .
2015-08-17 17:00:26 -07:00
*
2016-04-20 09:56:34 -07:00
* @ var \Drupal\migrate\Plugin\MigratePluginManager
2015-08-17 17:00:26 -07:00
*/
2016-04-20 09:56:34 -07:00
protected $sourcePluginManager ;
/**
* Thep process plugin manager .
*
* @ var \Drupal\migrate\Plugin\MigratePluginManager
*/
protected $processPluginManager ;
/**
* The destination plugin manager .
*
* @ var \Drupal\migrate\Plugin\MigrateDestinationPluginManager
*/
protected $destinationPluginManager ;
/**
* The ID map plugin manager .
*
* @ var \Drupal\migrate\Plugin\MigratePluginManager
*/
protected $idMapPluginManager ;
2015-08-17 17:00:26 -07:00
2015-09-04 13:20:09 -07:00
/**
* 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' ,
];
2016-04-20 09:56:34 -07:00
/**
* Constructs a Migration .
*
* @ param array $configuration
* Plugin configuration .
* @ param string $plugin_id
* The plugin ID .
* @ param mixed $plugin_definition
* The plugin definition .
* @ param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migration_plugin_manager
* The migration plugin manager .
* @ param \Drupal\migrate\Plugin\MigratePluginManager $source_plugin_manager
* The source migration plugin manager .
* @ param \Drupal\migrate\Plugin\MigratePluginManager $process_plugin_manager
* The process migration plugin manager .
* @ param \Drupal\migrate\Plugin\MigrateDestinationPluginManager $destination_plugin_manager
* The destination migration plugin manager .
* @ param \Drupal\migrate\Plugin\MigratePluginManager $idmap_plugin_manager
* The ID map migration plugin manager .
*/
public function __construct ( array $configuration , $plugin_id , $plugin_definition , MigrationPluginManagerInterface $migration_plugin_manager , MigratePluginManager $source_plugin_manager , MigratePluginManager $process_plugin_manager , MigrateDestinationPluginManager $destination_plugin_manager , MigratePluginManager $idmap_plugin_manager ) {
parent :: __construct ( $configuration , $plugin_id , $plugin_definition );
$this -> migrationPluginManager = $migration_plugin_manager ;
$this -> sourcePluginManager = $source_plugin_manager ;
$this -> processPluginManager = $process_plugin_manager ;
$this -> destinationPluginManager = $destination_plugin_manager ;
$this -> idMapPluginManager = $idmap_plugin_manager ;
foreach ( $plugin_definition as $key => $value ) {
$this -> $key = $value ;
}
}
/**
* { @ inheritdoc }
*/
public static function create ( ContainerInterface $container , array $configuration , $plugin_id , $plugin_definition ) {
return new static (
$configuration ,
$plugin_id ,
$plugin_definition ,
$container -> get ( 'plugin.manager.migration' ),
$container -> get ( 'plugin.manager.migrate.source' ),
$container -> get ( 'plugin.manager.migrate.process' ),
$container -> get ( 'plugin.manager.migrate.destination' ),
$container -> get ( 'plugin.manager.migrate.id_map' )
);
}
/**
* { @ inheritdoc }
*/
public function id () {
return $this -> pluginId ;
}
/**
* { @ inheritdoc }
*/
public function label () {
return $this -> label ;
}
/**
* Gets any arbitrary property ' s value .
*
* @ param string $property
* The property to retrieve .
*
* @ return mixed
* The value for that property , or NULL if the property does not exist .
*
* @ deprecated in Drupal 8.1 . x , will be removed before Drupal 9.0 . x . Use
* more specific getters instead .
*/
public function get ( $property ) {
return isset ( $this -> $property ) ? $this -> $property : NULL ;
}
/**
* Retrieves the ID map plugin .
*
* @ return \Drupal\migrate\Plugin\MigrateIdMapInterface
* The ID map plugin .
*/
public function getIdMapPlugin () {
return $this -> idMapPlugin ;
}
2015-08-17 17:00:26 -07:00
/**
* { @ inheritdoc }
*/
public function getSourcePlugin () {
if ( ! isset ( $this -> sourcePlugin )) {
2016-04-20 09:56:34 -07:00
$this -> sourcePlugin = $this -> sourcePluginManager -> createInstance ( $this -> source [ 'plugin' ], $this -> source , $this );
2015-08-17 17:00:26 -07:00
}
return $this -> sourcePlugin ;
}
/**
* { @ inheritdoc }
*/
public function getProcessPlugins ( array $process = NULL ) {
if ( ! isset ( $process )) {
2016-04-20 09:56:34 -07:00
$process = $this -> getProcess ();
2015-08-17 17:00:26 -07:00
}
$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' ])) {
2016-04-20 09:56:34 -07:00
$this -> processPlugins [ $index ][ $property ][] = $this -> processPluginManager -> createInstance ( 'get' , $configuration , $this );
2015-08-17 17:00:26 -07:00
}
// Get is already handled.
if ( $configuration [ 'plugin' ] != 'get' ) {
2016-04-20 09:56:34 -07:00
$this -> processPlugins [ $index ][ $property ][] = $this -> processPluginManager -> createInstance ( $configuration [ 'plugin' ], $configuration , $this );
2015-08-17 17:00:26 -07:00
}
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 ) {
2016-01-06 16:31:26 -08:00
if ( $stub_being_requested && ! empty ( $this -> destination [ 'no_stub' ])) {
2016-05-04 14:35:41 -07:00
throw new MigrateSkipRowException ();
2016-01-06 16:31:26 -08:00
}
2015-08-17 17:00:26 -07:00
if ( ! isset ( $this -> destinationPlugin )) {
2016-04-20 09:56:34 -07:00
$this -> destinationPlugin = $this -> destinationPluginManager -> createInstance ( $this -> destination [ 'plugin' ], $this -> destination , $this );
2015-08-17 17:00:26 -07:00
}
return $this -> destinationPlugin ;
}
/**
* { @ inheritdoc }
*/
public function getIdMap () {
if ( ! isset ( $this -> idMapPlugin )) {
$configuration = $this -> idMap ;
$plugin = isset ( $configuration [ 'plugin' ]) ? $configuration [ 'plugin' ] : 'sql' ;
2016-04-20 09:56:34 -07:00
$this -> idMapPlugin = $this -> idMapPluginManager -> createInstance ( $plugin , $configuration , $this );
2015-08-17 17:00:26 -07:00
}
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 ();
}
2016-04-20 09:56:34 -07:00
if ( empty ( $this -> requirements )) {
// There are no requirements to check.
return ;
}
/** @var \Drupal\migrate\Plugin\MigrationInterface[] $required_migrations */
$required_migrations = $this -> getMigrationPluginManager () -> createInstances ( $this -> requirements );
2015-08-17 17:00:26 -07:00
$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 ) {
2015-10-08 11:40:12 -07:00
if ( ! $required_migration -> allRowsProcessed ()) {
2015-08-17 17:00:26 -07:00
$missing_migrations [] = $migration_id ;
}
}
if ( $missing_migrations ) {
2015-08-27 12:03:05 -07:00
throw new RequirementsException ( 'Missing migrations ' . implode ( ', ' , $missing_migrations ) . '.' , [ 'requirements' => $missing_migrations ]);
2015-08-17 17:00:26 -07:00
}
}
/**
2016-04-20 09:56:34 -07:00
* Gets the migration plugin manager .
2015-08-17 17:00:26 -07:00
*
2016-04-20 09:56:34 -07:00
* @ return \Drupal\migrate\Plugin\MigratePluginManager
* The plugin manager .
2015-08-17 17:00:26 -07:00
*/
2016-04-20 09:56:34 -07:00
protected function getMigrationPluginManager () {
return $this -> migrationPluginManager ;
2015-08-17 17:00:26 -07:00
}
2015-09-04 13:20:09 -07:00
/**
* { @ 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 '' ;
}
}
2015-08-17 17:00:26 -07:00
/**
* { @ inheritdoc }
*/
2015-10-08 11:40:12 -07:00
public function getInterruptionResult () {
return \Drupal :: keyValue ( 'migrate_interruption_result' ) -> get ( $this -> id (), static :: RESULT_INCOMPLETE );
2015-08-17 17:00:26 -07:00
}
/**
* { @ inheritdoc }
*/
2015-10-08 11:40:12 -07:00
public function clearInterruptionResult () {
\Drupal :: keyValue ( 'migrate_interruption_result' ) -> delete ( $this -> id ());
2015-09-04 13:20:09 -07:00
}
/**
* { @ inheritdoc }
*/
public function interruptMigration ( $result ) {
$this -> setStatus ( MigrationInterface :: STATUS_STOPPING );
2015-10-08 11:40:12 -07:00
\Drupal :: keyValue ( 'migrate_interruption_result' ) -> set ( $this -> id (), $result );
2015-08-17 17:00:26 -07:00
}
/**
* { @ inheritdoc }
*/
2015-10-08 11:40:12 -07:00
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 ;
2015-08-17 17:00:26 -07:00
}
/**
* { @ inheritdoc }
*/
public function set ( $property_name , $value ) {
if ( $property_name == 'source' ) {
// Invalidate the source plugin.
unset ( $this -> sourcePlugin );
}
2015-09-04 13:20:09 -07:00
elseif ( $property_name === 'destination' ) {
// Invalidate the destination plugin.
unset ( $this -> destinationPlugin );
}
2016-04-20 09:56:34 -07:00
$this -> { $property_name } = $value ;
return $this ;
2015-08-17 17:00:26 -07:00
}
/**
* { @ 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 }
*/
2016-04-20 09:56:34 -07:00
public function getPluginDefinition () {
$definition = [];
// While normal plugins do not change their definitions on the fly, this
// one does so accommodate for that.
foreach ( parent :: getPluginDefinition () as $key => $value ) {
$definition [ $key ] = isset ( $this -> $key ) ? $this -> $key : $value ;
}
return $definition ;
2015-08-17 17:00:26 -07:00
}
/**
* { @ inheritdoc }
*/
2016-04-20 09:56:34 -07:00
public function getDestinationConfiguration () {
return $this -> destination ;
}
2015-10-21 21:44:50 -07:00
2016-04-20 09:56:34 -07:00
/**
* { @ inheritdoc }
*/
public function getSourceConfiguration () {
return $this -> source ;
}
2015-08-17 17:00:26 -07:00
2016-04-20 09:56:34 -07:00
/**
* { @ inheritdoc }
*/
public function getHighWaterProperty () {
return $this -> highWaterProperty ;
}
/**
* { @ inheritdoc }
*/
public function getTrackLastImported () {
$this -> trackLastImported ;
}
/**
* { @ inheritdoc }
*/
public function getDestinationIds () {
$this -> destinationIds ;
2015-08-17 17:00:26 -07:00
}
}