MigrationLookup.php
Same filename in other branches
Namespace
Drupal\migrate\Plugin\migrate\processFile
-
core/
modules/ migrate/ src/ Plugin/ migrate/ process/ MigrationLookup.php
View source
<?php
namespace Drupal\migrate\Plugin\migrate\process;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\Attribute\MigrateProcess;
use Drupal\migrate\MigrateException;
use Drupal\migrate\MigrateLookupInterface;
use Drupal\migrate\MigrateSkipRowException;
use Drupal\migrate\MigrateStubInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Plugin\MigrationInterface;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\Row;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Looks up the value of a property based on a previous migration.
*
* It is important to maintain relationships among content coming from the
* source site. For example, on the source site, a given user account may
* have an ID of 123, but the Drupal user account created from it may have
* a uid of 456. The migration process maintains the relationships between
* source and destination identifiers in map tables, and this information
* is leveraged by the migration_lookup process plugin.
*
* Available configuration keys
* - migration: A single migration ID, or an array of migration IDs.
* - source_ids: (optional) An array keyed by migration IDs with values that are
* a list of source properties.
* - stub_id: (optional) Identifies the migration which will be used to create
* any stub entities.
* - no_stub: (optional) Prevents the creation of a stub entity when no
* relationship is found in the migration map.
*
* Examples:
*
* Consider a node migration, where you want to maintain authorship. Let's
* assume that users are previously migrated in a migration named 'users'. The
* 'users' migration saved the mapping between the source and destination IDs in
* a map table. The node migration example below maps the node 'uid' property so
* that we first take the source 'author' value and then do a lookup for the
* corresponding Drupal user ID from the map table.
* @code
* process:
* uid:
* plugin: migration_lookup
* migration: users
* source: author
* @endcode
*
* The value of 'migration' can be a list of migration IDs. When using multiple
* migrations it is possible each use different source identifiers. In this
* case one can use source_ids which is an array keyed by the migration IDs
* and the value is a list of source properties. See example below.
* @code
* process:
* uid:
* plugin: migration_lookup
* migration:
* - users
* - members
* source_ids:
* users:
* - author
* members:
* - id
* @endcode
*
* It's not required to describe source identifiers for each migration. If the
* source identifier for a migration is not specified, the default source value
* will be used. In the example below, the 'author' source property will be used
* to do a lookup in the 'users' migration, and the 'uid' property in the
* 'members' migration.
* @code
* process:
* uid:
* plugin: migration_lookup
* source: uid
* migration:
* - users
* - members
* source_ids:
* users:
* - author
* @endcode
*
* If the migration_lookup plugin does not find the source ID in the migration
* map it will create a stub entity for the relationship to use. This stub is
* generated by the migration provided. In the case of multiple migrations the
* first value of the migration list will be used, but you can select the
* migration you wish to use by using the stub_id configuration key. The example
* below uses 'members' migration to create stub entities.
* @code
* process:
* uid:
* plugin: migration_lookup
* migration:
* - users
* - members
* stub_id: members
* @endcode
*
* To prevent the creation of a stub entity when no relationship is found in the
* migration map, 'no_stub' configuration can be used as shown below.
* @code
* process:
* uid:
* plugin: migration_lookup
* migration: users
* no_stub: true
* source: author
* @endcode
*
* If the source value passed in to the plugin is NULL, boolean FALSE, an empty
* array or an empty string, the plugin will return NULL and stop further
* processing on the pipeline. This is done for backwards compatibility reasons,
* and future versions of this plugin should simply return NULL and allow
* processing to continue.
* @see https://www.drupal.org/project/drupal/issues/3246666
*
* @see \Drupal\migrate\Plugin\MigrateProcessInterface
*/
class MigrationLookup extends ProcessPluginBase implements ContainerFactoryPluginInterface {
/**
* The migration to be executed.
*
* @var \Drupal\migrate\Plugin\MigrationInterface
*/
protected $migration;
/**
* The migrate lookup service.
*
* @var \Drupal\migrate\MigrateLookupInterface
*/
protected $migrateLookup;
/**
* The migrate stub service.
*
* @var \Drupal\migrate\MigrateStubInterface
*/
protected $migrateStub;
/**
* Constructs a MigrationLookup object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\migrate\Plugin\MigrationInterface $migration
* The Migration the plugin is being used in.
* @param \Drupal\migrate\MigrateLookupInterface $migrate_lookup
* The migrate lookup service.
* @param \Drupal\migrate\MigrateStubInterface $migrate_stub
* The migrate stub service.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, MigrateLookupInterface $migrate_lookup, MigrateStubInterface $migrate_stub) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->migration = $migration;
$this->migrateLookup = $migrate_lookup;
$this->migrateStub = $migrate_stub;
}
/**
* {@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('migrate.lookup'), $container->get('migrate.stub'));
}
/**
* {@inheritdoc}
*
* @throws \Drupal\migrate\MigrateException
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
$lookup_migration_ids = (array) $this->configuration['migration'];
$self = FALSE;
$destination_ids = NULL;
$source_id_values = [];
foreach ($lookup_migration_ids as $lookup_migration_id) {
$lookup_value = $value;
if ($lookup_migration_id == $this->migration
->id()) {
$self = TRUE;
}
if (isset($this->configuration['source_ids'][$lookup_migration_id])) {
$lookup_value = array_values($row->getMultiple($this->configuration['source_ids'][$lookup_migration_id]));
}
$lookup_value = (array) $lookup_value;
$this->skipInvalid($lookup_value);
if ($this->isPipelineStopped()) {
return NULL;
}
$source_id_values[$lookup_migration_id] = $lookup_value;
// Re-throw any PluginException as a MigrateException so the executable
// can shut down the migration.
try {
$destination_id_array = $this->migrateLookup
->lookup($lookup_migration_id, $lookup_value);
} catch (PluginNotFoundException $e) {
$destination_id_array = [];
} catch (MigrateException $e) {
throw $e;
} catch (\Exception $e) {
throw new MigrateException(sprintf('A %s was thrown while processing this migration lookup', gettype($e)), $e->getCode(), $e);
}
if ($destination_id_array) {
$destination_ids = array_values(reset($destination_id_array));
break;
}
}
if (!$destination_ids && !empty($this->configuration['no_stub'])) {
return NULL;
}
if (!$destination_ids && ($self || isset($this->configuration['stub_id']) || count($lookup_migration_ids) == 1)) {
// If the lookup didn't succeed, figure out which migration will do the
// stubbing.
if ($self) {
$stub_migration = $this->migration
->id();
}
elseif (isset($this->configuration['stub_id'])) {
$stub_migration = $this->configuration['stub_id'];
}
else {
$stub_migration = reset($lookup_migration_ids);
}
// Rethrow any exception as a MigrateException so the executable can shut
// down the migration.
try {
$destination_ids = $this->migrateStub
->createStub($stub_migration, $source_id_values[$stub_migration], [], FALSE);
} catch (\LogicException $e) {
// For BC reasons, we must allow attempting to stub a derived migration.
} catch (PluginNotFoundException $e) {
// For BC reasons, we must allow attempting to stub a non-existent
// migration.
} catch (MigrateException $e) {
throw $e;
} catch (MigrateSkipRowException $e) {
// Build a new message.
$skip_row_exception_message = $e->getMessage();
if (empty($skip_row_exception_message)) {
$new_message = sprintf("Migration lookup for destination '%s' attempted to create a stub using migration %s, which resulted in a row skip", $destination_property, $stub_migration);
}
else {
$new_message = sprintf("Migration lookup for destination '%s' attempted to create a stub using migration %s, which resulted in a row skip, with message '%s'", $destination_property, $stub_migration, $skip_row_exception_message);
}
throw new MigrateSkipRowException($new_message, 0);
} catch (\Exception $e) {
throw new MigrateException(sprintf('%s was thrown while attempting to stub: %s', get_class($e), $e->getMessage()), $e->getCode(), $e);
}
}
if ($destination_ids) {
if (count($destination_ids) == 1) {
return reset($destination_ids);
}
else {
return $destination_ids;
}
}
}
/**
* Skips the migration process entirely if the value is invalid.
*
* @param array $value
* The incoming value to check.
*/
protected function skipInvalid(array $value) {
if (!array_filter($value, [
$this,
'isValid',
])) {
$this->stopPipeline();
}
}
/**
* Determines if the value is valid for lookup.
*
* The only values considered invalid are: NULL, FALSE, [] and "".
*
* @param string $value
* The value to test.
*
* @return bool
* Return true if the value is valid.
*/
protected function isValid($value) {
return !in_array($value, [
NULL,
FALSE,
[],
"",
], TRUE);
}
}
Classes
Title | Deprecated | Summary |
---|---|---|
MigrationLookup | Looks up the value of a property based on a previous migration. |
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.