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 }