3 namespace Drupal\migrate\Plugin\migrate\process;
5 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
6 use Drupal\migrate\MigrateSkipProcessException;
7 use Drupal\migrate\Plugin\MigratePluginManagerInterface;
8 use Drupal\migrate\Plugin\MigrationPluginManagerInterface;
9 use Drupal\migrate\Plugin\MigrateIdMapInterface;
10 use Drupal\migrate\ProcessPluginBase;
11 use Drupal\migrate\Plugin\MigrationInterface;
12 use Drupal\migrate\MigrateExecutableInterface;
13 use Drupal\migrate\Row;
14 use Symfony\Component\DependencyInjection\ContainerInterface;
17 * Looks up the value of a property based on a previous migration.
19 * It is important to maintain relationships among content coming from the
20 * source site. For example, on the source site, a given user account may
21 * have an ID of 123, but the Drupal user account created from it may have
22 * a uid of 456. The migration process maintains the relationships between
23 * source and destination identifiers in map tables, and this information
24 * is leveraged by the migration_lookup process plugin.
26 * Available configuration keys
27 * - migration: A single migration ID, or an array of migration IDs.
28 * - source_ids: (optional) An array keyed by migration IDs with values that are
29 * a list of source properties.
30 * - stub_id: (optional) Identifies the migration which will be used to create
32 * - no_stub: (optional) Prevents the creation of a stub entity when no
33 * relationship is found in the migration map.
37 * Consider a node migration, where you want to maintain authorship. Let's
38 * assume that users are previously migrated in a migration named 'users'. The
39 * 'users' migration saved the mapping between the source and destination IDs in
40 * a map table. The node migration example below maps the node 'uid' property so
41 * that we first take the source 'author' value and then do a lookup for the
42 * corresponding Drupal user ID from the map table.
46 * plugin: migration_lookup
51 * The value of 'migration' can be a list of migration IDs. When using multiple
52 * migrations it is possible each use different source identifiers. In this
53 * case one can use source_ids which is an array keyed by the migration IDs
54 * and the value is a list of source properties. See example below.
58 * plugin: migration_lookup
69 * If the migration_lookup plugin does not find the source ID in the migration
70 * map it will create a stub entity for the relationship to use. This stub is
71 * generated by the migration provided. In the case of multiple migrations the
72 * first value of the migration list will be used, but you can select the
73 * migration you wish to use by using the stub_id configuration key. The example
74 * below uses 'members' migration to create stub entities.
78 * plugin: migration_lookup
85 * To prevent the creation of a stub entity when no relationship is found in the
86 * migration map, 'no_stub' configuration can be used as shown below.
90 * plugin: migration_lookup
96 * @see \Drupal\migrate\Plugin\MigrateProcessInterface
98 * @MigrateProcessPlugin(
99 * id = "migration_lookup"
102 class MigrationLookup extends ProcessPluginBase implements ContainerFactoryPluginInterface {
105 * The process plugin manager.
107 * @var \Drupal\migrate\Plugin\MigratePluginManager
109 protected $processPluginManager;
112 * The migration plugin manager.
114 * @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface
116 protected $migrationPluginManager;
119 * The migration to be executed.
121 * @var \Drupal\migrate\Plugin\MigrationInterface
123 protected $migration;
128 public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, MigrationPluginManagerInterface $migration_plugin_manager, MigratePluginManagerInterface $process_plugin_manager) {
129 parent::__construct($configuration, $plugin_id, $plugin_definition);
130 $this->migrationPluginManager = $migration_plugin_manager;
131 $this->migration = $migration;
132 $this->processPluginManager = $process_plugin_manager;
138 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
144 $container->get('plugin.manager.migration'),
145 $container->get('plugin.manager.migrate.process')
152 public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
153 $migration_ids = $this->configuration['migration'];
154 if (!is_array($migration_ids)) {
155 $migration_ids = [$migration_ids];
158 /** @var \Drupal\migrate\Plugin\MigrationInterface[] $migrations */
159 $destination_ids = NULL;
160 $source_id_values = [];
161 $migrations = $this->migrationPluginManager->createInstances($migration_ids);
162 foreach ($migrations as $migration_id => $migration) {
163 if ($migration_id == $this->migration->id()) {
166 if (isset($this->configuration['source_ids'][$migration_id])) {
167 $configuration = ['source' => $this->configuration['source_ids'][$migration_id]];
168 $value = $this->processPluginManager
169 ->createInstance('get', $configuration, $this->migration)
170 ->transform(NULL, $migrate_executable, $row, $destination_property);
172 if (!is_array($value)) {
175 $this->skipOnEmpty($value);
176 $source_id_values[$migration_id] = $value;
177 // Break out of the loop as soon as a destination ID is found.
178 if ($destination_ids = $migration->getIdMap()->lookupDestinationId($source_id_values[$migration_id])) {
183 if (!$destination_ids && !empty($this->configuration['no_stub'])) {
187 if (!$destination_ids && ($self || isset($this->configuration['stub_id']) || count($migrations) == 1)) {
188 // If the lookup didn't succeed, figure out which migration will do the
191 $migration = $this->migration;
193 elseif (isset($this->configuration['stub_id'])) {
194 $migration = $migrations[$this->configuration['stub_id']];
197 $migration = reset($migrations);
199 $destination_plugin = $migration->getDestinationPlugin(TRUE);
200 // Only keep the process necessary to produce the destination ID.
201 $process = $migration->getProcess();
203 // We already have the source ID values but need to key them for the Row
205 $source_ids = $migration->getSourcePlugin()->getIds();
207 foreach (array_keys($source_ids) as $index => $source_id) {
208 $values[$source_id] = $source_id_values[$migration->id()][$index];
211 $stub_row = $this->createStubRow($values + $migration->getSourceConfiguration(), $source_ids);
213 // Do a normal migration with the stub row.
214 $migrate_executable->processRow($stub_row, $process);
215 $destination_ids = [];
216 $id_map = $migration->getIdMap();
218 $destination_ids = $destination_plugin->import($stub_row);
220 catch (\Exception $e) {
221 $id_map->saveMessage($stub_row->getSourceIdValues(), $e->getMessage());
224 if ($destination_ids) {
225 $id_map->saveIdMapping($stub_row, $destination_ids, MigrateIdMapInterface::STATUS_NEEDS_UPDATE);
228 if ($destination_ids) {
229 if (count($destination_ids) == 1) {
230 return reset($destination_ids);
233 return $destination_ids;
239 * Skips the migration process entirely if the value is FALSE.
241 * @param array $value
242 * The incoming value to transform.
244 * @throws \Drupal\migrate\MigrateSkipProcessException
246 protected function skipOnEmpty(array $value) {
247 if (!array_filter($value)) {
248 throw new MigrateSkipProcessException();
253 * Create a stub row source for later import as stub data.
255 * This simple wrapper of the Row constructor allows sub-classing plugins to
256 * have more control over the row.
258 * @param array $values
259 * An array of values to add as properties on the object.
260 * @param array $source_ids
261 * An array containing the IDs of the source using the keys as the field
264 * @return \Drupal\migrate\Row
267 protected function createStubRow(array $values, array $source_ids) {
268 return new Row($values, $source_ids, TRUE);