Mercurial > hg > isophonics-drupal-site
comparison core/modules/migrate/src/Plugin/Migration.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 7a779792577d |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\migrate\Plugin; | |
4 | |
5 use Drupal\Core\Plugin\ContainerFactoryPluginInterface; | |
6 use Drupal\Core\Plugin\PluginBase; | |
7 use Drupal\migrate\Exception\RequirementsException; | |
8 use Drupal\migrate\MigrateException; | |
9 use Drupal\migrate\MigrateSkipRowException; | |
10 use Drupal\Component\Utility\NestedArray; | |
11 use Symfony\Component\DependencyInjection\ContainerInterface; | |
12 | |
13 /** | |
14 * Defines the Migration plugin. | |
15 * | |
16 * The migration process plugin represents one single migration and acts like a | |
17 * container for the information about a single migration such as the source, | |
18 * process and destination plugins. | |
19 */ | |
20 class Migration extends PluginBase implements MigrationInterface, RequirementsInterface, ContainerFactoryPluginInterface { | |
21 | |
22 /** | |
23 * The migration ID (machine name). | |
24 * | |
25 * @var string | |
26 */ | |
27 protected $id; | |
28 | |
29 /** | |
30 * The human-readable label for the migration. | |
31 * | |
32 * @var string | |
33 */ | |
34 protected $label; | |
35 | |
36 /** | |
37 * The plugin ID for the row. | |
38 * | |
39 * @var string | |
40 */ | |
41 protected $row; | |
42 | |
43 /** | |
44 * The source configuration, with at least a 'plugin' key. | |
45 * | |
46 * Used to initialize the $sourcePlugin. | |
47 * | |
48 * @var array | |
49 */ | |
50 protected $source; | |
51 | |
52 /** | |
53 * The source plugin. | |
54 * | |
55 * @var \Drupal\migrate\Plugin\MigrateSourceInterface | |
56 */ | |
57 protected $sourcePlugin; | |
58 | |
59 /** | |
60 * The configuration describing the process plugins. | |
61 * | |
62 * This is a strictly internal property and should not returned to calling | |
63 * code, use getProcess() instead. | |
64 * | |
65 * @var array | |
66 */ | |
67 protected $process = []; | |
68 | |
69 /** | |
70 * The cached process plugins. | |
71 * | |
72 * @var array | |
73 */ | |
74 protected $processPlugins = []; | |
75 | |
76 /** | |
77 * The destination configuration, with at least a 'plugin' key. | |
78 * | |
79 * Used to initialize $destinationPlugin. | |
80 * | |
81 * @var array | |
82 */ | |
83 protected $destination; | |
84 | |
85 /** | |
86 * The destination plugin. | |
87 * | |
88 * @var \Drupal\migrate\Plugin\MigrateDestinationInterface | |
89 */ | |
90 protected $destinationPlugin; | |
91 | |
92 /** | |
93 * The identifier map data. | |
94 * | |
95 * Used to initialize $idMapPlugin. | |
96 * | |
97 * @var string | |
98 */ | |
99 protected $idMap = []; | |
100 | |
101 /** | |
102 * The identifier map. | |
103 * | |
104 * @var \Drupal\migrate\Plugin\MigrateIdMapInterface | |
105 */ | |
106 protected $idMapPlugin; | |
107 | |
108 /** | |
109 * The source identifiers. | |
110 * | |
111 * An array of source identifiers: the keys are the name of the properties, | |
112 * the values are dependent on the ID map plugin. | |
113 * | |
114 * @var array | |
115 */ | |
116 protected $sourceIds = []; | |
117 | |
118 /** | |
119 * The destination identifiers. | |
120 * | |
121 * An array of destination identifiers: the keys are the name of the | |
122 * properties, the values are dependent on the ID map plugin. | |
123 * | |
124 * @var array | |
125 */ | |
126 protected $destinationIds = []; | |
127 | |
128 /** | |
129 * Specify value of source_row_status for current map row. Usually set by | |
130 * MigrateFieldHandler implementations. | |
131 * | |
132 * @var int | |
133 */ | |
134 protected $sourceRowStatus = MigrateIdMapInterface::STATUS_IMPORTED; | |
135 | |
136 /** | |
137 * Track time of last import if TRUE. | |
138 * | |
139 * @var bool | |
140 */ | |
141 protected $trackLastImported = FALSE; | |
142 | |
143 /** | |
144 * These migrations must be already executed before this migration can run. | |
145 * | |
146 * @var array | |
147 */ | |
148 protected $requirements = []; | |
149 | |
150 /** | |
151 * An optional list of tags, used by the plugin manager for filtering. | |
152 * | |
153 * @var array | |
154 */ | |
155 protected $migration_tags = []; | |
156 | |
157 /** | |
158 * These migrations, if run, must be executed before this migration. | |
159 * | |
160 * These are different from the configuration dependencies. Migration | |
161 * dependencies are only used to store relationships between migrations. | |
162 * | |
163 * The migration_dependencies value is structured like this: | |
164 * @code | |
165 * array( | |
166 * 'required' => array( | |
167 * // An array of migration IDs that must be run before this migration. | |
168 * ), | |
169 * 'optional' => array( | |
170 * // An array of migration IDs that, if they exist, must be run before | |
171 * // this migration. | |
172 * ), | |
173 * ); | |
174 * @endcode | |
175 * | |
176 * @var array | |
177 */ | |
178 protected $migration_dependencies = []; | |
179 | |
180 /** | |
181 * The migration's configuration dependencies. | |
182 * | |
183 * These store any dependencies on modules or other configuration (including | |
184 * other migrations) that must be available before the migration can be | |
185 * created. | |
186 * | |
187 * @see \Drupal\Core\Config\Entity\ConfigDependencyManager | |
188 * | |
189 * @var array | |
190 */ | |
191 protected $dependencies = []; | |
192 | |
193 /** | |
194 * The migration plugin manager for loading other migration plugins. | |
195 * | |
196 * @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface | |
197 */ | |
198 protected $migrationPluginManager; | |
199 | |
200 /** | |
201 * The source plugin manager. | |
202 * | |
203 * @var \Drupal\migrate\Plugin\MigratePluginManager | |
204 */ | |
205 protected $sourcePluginManager; | |
206 | |
207 /** | |
208 * Thep process plugin manager. | |
209 * | |
210 * @var \Drupal\migrate\Plugin\MigratePluginManager | |
211 */ | |
212 protected $processPluginManager; | |
213 | |
214 /** | |
215 * The destination plugin manager. | |
216 * | |
217 * @var \Drupal\migrate\Plugin\MigrateDestinationPluginManager | |
218 */ | |
219 protected $destinationPluginManager; | |
220 | |
221 /** | |
222 * The ID map plugin manager. | |
223 * | |
224 * @var \Drupal\migrate\Plugin\MigratePluginManager | |
225 */ | |
226 protected $idMapPluginManager; | |
227 | |
228 /** | |
229 * Labels corresponding to each defined status. | |
230 * | |
231 * @var array | |
232 */ | |
233 protected $statusLabels = [ | |
234 self::STATUS_IDLE => 'Idle', | |
235 self::STATUS_IMPORTING => 'Importing', | |
236 self::STATUS_ROLLING_BACK => 'Rolling back', | |
237 self::STATUS_STOPPING => 'Stopping', | |
238 self::STATUS_DISABLED => 'Disabled', | |
239 ]; | |
240 | |
241 /** | |
242 * Constructs a Migration. | |
243 * | |
244 * @param array $configuration | |
245 * Plugin configuration. | |
246 * @param string $plugin_id | |
247 * The plugin ID. | |
248 * @param mixed $plugin_definition | |
249 * The plugin definition. | |
250 * @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migration_plugin_manager | |
251 * The migration plugin manager. | |
252 * @param \Drupal\migrate\Plugin\MigratePluginManagerInterface $source_plugin_manager | |
253 * The source migration plugin manager. | |
254 * @param \Drupal\migrate\Plugin\MigratePluginManagerInterface $process_plugin_manager | |
255 * The process migration plugin manager. | |
256 * @param \Drupal\migrate\Plugin\MigrateDestinationPluginManager $destination_plugin_manager | |
257 * The destination migration plugin manager. | |
258 * @param \Drupal\migrate\Plugin\MigratePluginManagerInterface $idmap_plugin_manager | |
259 * The ID map migration plugin manager. | |
260 */ | |
261 public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationPluginManagerInterface $migration_plugin_manager, MigratePluginManagerInterface $source_plugin_manager, MigratePluginManagerInterface $process_plugin_manager, MigrateDestinationPluginManager $destination_plugin_manager, MigratePluginManagerInterface $idmap_plugin_manager) { | |
262 parent::__construct($configuration, $plugin_id, $plugin_definition); | |
263 $this->migrationPluginManager = $migration_plugin_manager; | |
264 $this->sourcePluginManager = $source_plugin_manager; | |
265 $this->processPluginManager = $process_plugin_manager; | |
266 $this->destinationPluginManager = $destination_plugin_manager; | |
267 $this->idMapPluginManager = $idmap_plugin_manager; | |
268 | |
269 foreach (NestedArray::mergeDeep($plugin_definition, $configuration) as $key => $value) { | |
270 $this->$key = $value; | |
271 } | |
272 } | |
273 | |
274 /** | |
275 * {@inheritdoc} | |
276 */ | |
277 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { | |
278 return new static( | |
279 $configuration, | |
280 $plugin_id, | |
281 $plugin_definition, | |
282 $container->get('plugin.manager.migration'), | |
283 $container->get('plugin.manager.migrate.source'), | |
284 $container->get('plugin.manager.migrate.process'), | |
285 $container->get('plugin.manager.migrate.destination'), | |
286 $container->get('plugin.manager.migrate.id_map') | |
287 ); | |
288 } | |
289 | |
290 /** | |
291 * {@inheritdoc} | |
292 */ | |
293 public function id() { | |
294 return $this->pluginId; | |
295 } | |
296 | |
297 /** | |
298 * {@inheritdoc} | |
299 */ | |
300 public function label() { | |
301 return $this->label; | |
302 } | |
303 | |
304 /** | |
305 * Gets any arbitrary property's value. | |
306 * | |
307 * @param string $property | |
308 * The property to retrieve. | |
309 * | |
310 * @return mixed | |
311 * The value for that property, or NULL if the property does not exist. | |
312 * | |
313 * @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.x. Use | |
314 * more specific getters instead. | |
315 * | |
316 * @see https://www.drupal.org/node/2873795 | |
317 */ | |
318 public function get($property) { | |
319 return isset($this->$property) ? $this->$property : NULL; | |
320 } | |
321 | |
322 /** | |
323 * Retrieves the ID map plugin. | |
324 * | |
325 * @return \Drupal\migrate\Plugin\MigrateIdMapInterface | |
326 * The ID map plugin. | |
327 */ | |
328 public function getIdMapPlugin() { | |
329 return $this->idMapPlugin; | |
330 } | |
331 | |
332 /** | |
333 * {@inheritdoc} | |
334 */ | |
335 public function getSourcePlugin() { | |
336 if (!isset($this->sourcePlugin)) { | |
337 $this->sourcePlugin = $this->sourcePluginManager->createInstance($this->source['plugin'], $this->source, $this); | |
338 } | |
339 return $this->sourcePlugin; | |
340 } | |
341 | |
342 /** | |
343 * {@inheritdoc} | |
344 */ | |
345 public function getProcessPlugins(array $process = NULL) { | |
346 if (!isset($process)) { | |
347 $process = $this->getProcess(); | |
348 } | |
349 $index = serialize($process); | |
350 if (!isset($this->processPlugins[$index])) { | |
351 $this->processPlugins[$index] = []; | |
352 foreach ($this->getProcessNormalized($process) as $property => $configurations) { | |
353 $this->processPlugins[$index][$property] = []; | |
354 foreach ($configurations as $configuration) { | |
355 if (isset($configuration['source'])) { | |
356 $this->processPlugins[$index][$property][] = $this->processPluginManager->createInstance('get', $configuration, $this); | |
357 } | |
358 // Get is already handled. | |
359 if ($configuration['plugin'] != 'get') { | |
360 $this->processPlugins[$index][$property][] = $this->processPluginManager->createInstance($configuration['plugin'], $configuration, $this); | |
361 } | |
362 if (!$this->processPlugins[$index][$property]) { | |
363 throw new MigrateException("Invalid process configuration for $property"); | |
364 } | |
365 } | |
366 } | |
367 } | |
368 return $this->processPlugins[$index]; | |
369 } | |
370 | |
371 /** | |
372 * Resolve shorthands into a list of plugin configurations. | |
373 * | |
374 * @param array $process | |
375 * A process configuration array. | |
376 * | |
377 * @return array | |
378 * The normalized process configuration. | |
379 */ | |
380 protected function getProcessNormalized(array $process) { | |
381 $normalized_configurations = []; | |
382 foreach ($process as $destination => $configuration) { | |
383 if (is_string($configuration)) { | |
384 $configuration = [ | |
385 'plugin' => 'get', | |
386 'source' => $configuration, | |
387 ]; | |
388 } | |
389 if (isset($configuration['plugin'])) { | |
390 $configuration = [$configuration]; | |
391 } | |
392 $normalized_configurations[$destination] = $configuration; | |
393 } | |
394 return $normalized_configurations; | |
395 } | |
396 | |
397 /** | |
398 * {@inheritdoc} | |
399 */ | |
400 public function getDestinationPlugin($stub_being_requested = FALSE) { | |
401 if ($stub_being_requested && !empty($this->destination['no_stub'])) { | |
402 throw new MigrateSkipRowException(); | |
403 } | |
404 if (!isset($this->destinationPlugin)) { | |
405 $this->destinationPlugin = $this->destinationPluginManager->createInstance($this->destination['plugin'], $this->destination, $this); | |
406 } | |
407 return $this->destinationPlugin; | |
408 } | |
409 | |
410 /** | |
411 * {@inheritdoc} | |
412 */ | |
413 public function getIdMap() { | |
414 if (!isset($this->idMapPlugin)) { | |
415 $configuration = $this->idMap; | |
416 $plugin = isset($configuration['plugin']) ? $configuration['plugin'] : 'sql'; | |
417 $this->idMapPlugin = $this->idMapPluginManager->createInstance($plugin, $configuration, $this); | |
418 } | |
419 return $this->idMapPlugin; | |
420 } | |
421 | |
422 /** | |
423 * {@inheritdoc} | |
424 */ | |
425 public function checkRequirements() { | |
426 // Check whether the current migration source and destination plugin | |
427 // requirements are met or not. | |
428 if ($this->getSourcePlugin() instanceof RequirementsInterface) { | |
429 $this->getSourcePlugin()->checkRequirements(); | |
430 } | |
431 if ($this->getDestinationPlugin() instanceof RequirementsInterface) { | |
432 $this->getDestinationPlugin()->checkRequirements(); | |
433 } | |
434 | |
435 if (empty($this->requirements)) { | |
436 // There are no requirements to check. | |
437 return; | |
438 } | |
439 /** @var \Drupal\migrate\Plugin\MigrationInterface[] $required_migrations */ | |
440 $required_migrations = $this->getMigrationPluginManager()->createInstances($this->requirements); | |
441 | |
442 $missing_migrations = array_diff($this->requirements, array_keys($required_migrations)); | |
443 // Check if the dependencies are in good shape. | |
444 foreach ($required_migrations as $migration_id => $required_migration) { | |
445 if (!$required_migration->allRowsProcessed()) { | |
446 $missing_migrations[] = $migration_id; | |
447 } | |
448 } | |
449 if ($missing_migrations) { | |
450 throw new RequirementsException('Missing migrations ' . implode(', ', $missing_migrations) . '.', ['requirements' => $missing_migrations]); | |
451 } | |
452 } | |
453 | |
454 /** | |
455 * Gets the migration plugin manager. | |
456 * | |
457 * @return \Drupal\migrate\Plugin\MigratePluginManager | |
458 * The plugin manager. | |
459 */ | |
460 protected function getMigrationPluginManager() { | |
461 return $this->migrationPluginManager; | |
462 } | |
463 | |
464 /** | |
465 * {@inheritdoc} | |
466 */ | |
467 public function setStatus($status) { | |
468 \Drupal::keyValue('migrate_status')->set($this->id(), $status); | |
469 } | |
470 | |
471 /** | |
472 * {@inheritdoc} | |
473 */ | |
474 public function getStatus() { | |
475 return \Drupal::keyValue('migrate_status')->get($this->id(), static::STATUS_IDLE); | |
476 } | |
477 | |
478 /** | |
479 * {@inheritdoc} | |
480 */ | |
481 public function getStatusLabel() { | |
482 $status = $this->getStatus(); | |
483 if (isset($this->statusLabels[$status])) { | |
484 return $this->statusLabels[$status]; | |
485 } | |
486 else { | |
487 return ''; | |
488 } | |
489 } | |
490 | |
491 /** | |
492 * {@inheritdoc} | |
493 */ | |
494 public function getInterruptionResult() { | |
495 return \Drupal::keyValue('migrate_interruption_result')->get($this->id(), static::RESULT_INCOMPLETE); | |
496 } | |
497 | |
498 /** | |
499 * {@inheritdoc} | |
500 */ | |
501 public function clearInterruptionResult() { | |
502 \Drupal::keyValue('migrate_interruption_result')->delete($this->id()); | |
503 } | |
504 | |
505 /** | |
506 * {@inheritdoc} | |
507 */ | |
508 public function interruptMigration($result) { | |
509 $this->setStatus(MigrationInterface::STATUS_STOPPING); | |
510 \Drupal::keyValue('migrate_interruption_result')->set($this->id(), $result); | |
511 } | |
512 | |
513 /** | |
514 * {@inheritdoc} | |
515 */ | |
516 public function allRowsProcessed() { | |
517 $source_count = $this->getSourcePlugin()->count(); | |
518 // If the source is uncountable, we have no way of knowing if it's | |
519 // complete, so stipulate that it is. | |
520 if ($source_count < 0) { | |
521 return TRUE; | |
522 } | |
523 $processed_count = $this->getIdMap()->processedCount(); | |
524 // We don't use == because in some circumstances (like unresolved stubs | |
525 // being created), the processed count may be higher than the available | |
526 // source rows. | |
527 return $source_count <= $processed_count; | |
528 } | |
529 | |
530 /** | |
531 * {@inheritdoc} | |
532 */ | |
533 public function set($property_name, $value) { | |
534 if ($property_name == 'source') { | |
535 // Invalidate the source plugin. | |
536 unset($this->sourcePlugin); | |
537 } | |
538 elseif ($property_name === 'destination') { | |
539 // Invalidate the destination plugin. | |
540 unset($this->destinationPlugin); | |
541 } | |
542 $this->{$property_name} = $value; | |
543 return $this; | |
544 } | |
545 | |
546 | |
547 /** | |
548 * {@inheritdoc} | |
549 */ | |
550 public function getProcess() { | |
551 return $this->getProcessNormalized($this->process); | |
552 } | |
553 | |
554 /** | |
555 * {@inheritdoc} | |
556 */ | |
557 public function setProcess(array $process) { | |
558 $this->process = $process; | |
559 return $this; | |
560 } | |
561 | |
562 /** | |
563 * {@inheritdoc} | |
564 */ | |
565 public function setProcessOfProperty($property, $process_of_property) { | |
566 $this->process[$property] = $process_of_property; | |
567 return $this; | |
568 } | |
569 | |
570 /** | |
571 * {@inheritdoc} | |
572 */ | |
573 public function mergeProcessOfProperty($property, array $process_of_property) { | |
574 // If we already have a process value then merge the incoming process array | |
575 // otherwise simply set it. | |
576 $current_process = $this->getProcess(); | |
577 if (isset($current_process[$property])) { | |
578 $this->process = NestedArray::mergeDeepArray([$current_process, $this->getProcessNormalized([$property => $process_of_property])], TRUE); | |
579 } | |
580 else { | |
581 $this->setProcessOfProperty($property, $process_of_property); | |
582 } | |
583 | |
584 return $this; | |
585 } | |
586 | |
587 /** | |
588 * {@inheritdoc} | |
589 */ | |
590 public function isTrackLastImported() { | |
591 return $this->trackLastImported; | |
592 } | |
593 | |
594 /** | |
595 * {@inheritdoc} | |
596 */ | |
597 public function setTrackLastImported($track_last_imported) { | |
598 $this->trackLastImported = (bool) $track_last_imported; | |
599 return $this; | |
600 } | |
601 | |
602 /** | |
603 * {@inheritdoc} | |
604 */ | |
605 public function getMigrationDependencies() { | |
606 $this->migration_dependencies = ($this->migration_dependencies ?: []) + ['required' => [], 'optional' => []]; | |
607 $this->migration_dependencies['optional'] = array_unique(array_merge($this->migration_dependencies['optional'], $this->findMigrationDependencies($this->process))); | |
608 return $this->migration_dependencies; | |
609 } | |
610 | |
611 /** | |
612 * Find migration dependencies from the migration and the iterator plugins. | |
613 * | |
614 * @param $process | |
615 * @return array | |
616 */ | |
617 protected function findMigrationDependencies($process) { | |
618 $return = []; | |
619 foreach ($this->getProcessNormalized($process) as $process_pipeline) { | |
620 foreach ($process_pipeline as $plugin_configuration) { | |
621 if ($plugin_configuration['plugin'] == 'migration') { | |
622 $return = array_merge($return, (array) $plugin_configuration['migration']); | |
623 } | |
624 if ($plugin_configuration['plugin'] == 'iterator') { | |
625 $return = array_merge($return, $this->findMigrationDependencies($plugin_configuration['process'])); | |
626 } | |
627 } | |
628 } | |
629 return $return; | |
630 } | |
631 | |
632 /** | |
633 * {@inheritdoc} | |
634 */ | |
635 public function getPluginDefinition() { | |
636 $definition = []; | |
637 // While normal plugins do not change their definitions on the fly, this | |
638 // one does so accommodate for that. | |
639 foreach (parent::getPluginDefinition() as $key => $value) { | |
640 $definition[$key] = isset($this->$key) ? $this->$key : $value; | |
641 } | |
642 return $definition; | |
643 } | |
644 | |
645 /** | |
646 * {@inheritdoc} | |
647 */ | |
648 public function getDestinationConfiguration() { | |
649 return $this->destination; | |
650 } | |
651 | |
652 /** | |
653 * {@inheritdoc} | |
654 */ | |
655 public function getSourceConfiguration() { | |
656 return $this->source; | |
657 } | |
658 | |
659 /** | |
660 * {@inheritdoc} | |
661 */ | |
662 public function getTrackLastImported() { | |
663 return $this->trackLastImported; | |
664 } | |
665 | |
666 /** | |
667 * {@inheritdoc} | |
668 */ | |
669 public function getDestinationIds() { | |
670 return $this->destinationIds; | |
671 } | |
672 | |
673 /** | |
674 * {@inheritdoc} | |
675 */ | |
676 public function getMigrationTags() { | |
677 return $this->migration_tags; | |
678 } | |
679 | |
680 } |