From a916e4d4fa9ca546b5e8f6b0b6f26446b7cbe266 Mon Sep 17 00:00:00 2001 From: Alex Pott <alex.a.pott@googlemail.com> Date: Fri, 8 Mar 2019 06:44:20 +0000 Subject: [PATCH] Issue #2951550 by mikelutz, Jo Fitzgerald, quietone, heddn, alexpott, phenaproxima: Make service for field discovery for use in migrate entity derivers --- .../comment/src/Plugin/migrate/D7Comment.php | 31 +- .../migrate_drupal.services.yml | 9 + .../migrate_drupal/src/FieldDiscovery.php | 385 ++++++++++++++++++ .../src/FieldDiscoveryInterface.php | 68 ++++ .../src/Plugin/migrate/FieldMigration.php | 96 +---- .../field_discovery_test.info.yml | 6 + .../src/FieldDiscoveryTestClass.php | 93 +++++ .../src/Kernel/d6/FieldDiscoveryTest.php | 304 ++++++++++++++ .../src/Kernel/d7/FieldDiscoveryTest.php | 364 +++++++++++++++++ .../src/Traits/FieldDiscoveryTestTrait.php | 103 +++++ .../tests/src/Unit/FieldDiscoveryTest.php | 358 ++++++++++++++++ .../node/src/Plugin/migrate/D6NodeDeriver.php | 106 +---- .../node/src/Plugin/migrate/D7NodeDeriver.php | 99 +---- .../Plugin/migrate/D7TaxonomyTermDeriver.php | 92 +---- core/modules/user/src/Plugin/migrate/User.php | 54 +-- 15 files changed, 1760 insertions(+), 408 deletions(-) create mode 100644 core/modules/migrate_drupal/src/FieldDiscovery.php create mode 100644 core/modules/migrate_drupal/src/FieldDiscoveryInterface.php create mode 100644 core/modules/migrate_drupal/tests/modules/field_discovery_test/field_discovery_test.info.yml create mode 100644 core/modules/migrate_drupal/tests/modules/field_discovery_test/src/FieldDiscoveryTestClass.php create mode 100644 core/modules/migrate_drupal/tests/src/Kernel/d6/FieldDiscoveryTest.php create mode 100644 core/modules/migrate_drupal/tests/src/Kernel/d7/FieldDiscoveryTest.php create mode 100644 core/modules/migrate_drupal/tests/src/Traits/FieldDiscoveryTestTrait.php create mode 100644 core/modules/migrate_drupal/tests/src/Unit/FieldDiscoveryTest.php diff --git a/core/modules/comment/src/Plugin/migrate/D7Comment.php b/core/modules/comment/src/Plugin/migrate/D7Comment.php index a162400688fe..82590b4559b7 100644 --- a/core/modules/comment/src/Plugin/migrate/D7Comment.php +++ b/core/modules/comment/src/Plugin/migrate/D7Comment.php @@ -13,34 +13,9 @@ class D7Comment extends FieldMigration { * {@inheritdoc} */ public function getProcess() { - if ($this->init) { - return parent::getProcess(); - } - $this->init = TRUE; - if (!\Drupal::moduleHandler()->moduleExists('field')) { - return parent::getProcess(); - } - $definition['source'] = [ - 'ignore_map' => TRUE, - ] + $this->getSourceConfiguration(); - $definition['source']['plugin'] = 'd7_field_instance'; - $definition['destination']['plugin'] = 'null'; - $definition['idMap']['plugin'] = 'null'; - $field_migration = $this->migrationPluginManager->createStubMigration($definition); - foreach ($field_migration->getSourcePlugin() as $row) { - $field_name = $row->getSourceProperty('field_name'); - $field_type = $row->getSourceProperty('type'); - if ($this->fieldPluginManager->hasDefinition($field_type)) { - if (!isset($this->fieldPluginCache[$field_type])) { - $plugin_id = $this->fieldPluginManager->getPluginIdFromFieldType($field_type, [], $this); - $this->fieldPluginCache[$field_type] = $this->fieldPluginManager->createInstance($plugin_id, [], $this); - } - $info = $row->getSource(); - $this->fieldPluginCache[$field_type]->defineValueProcessPipeline($this, $field_name, $info); - } - else { - $this->setProcessOfProperty($field_name, $field_name); - } + if (!$this->init) { + $this->init = TRUE; + $this->fieldDiscovery->addEntityFieldProcesses($this, 'comment'); } return parent::getProcess(); } diff --git a/core/modules/migrate_drupal/migrate_drupal.services.yml b/core/modules/migrate_drupal/migrate_drupal.services.yml index 23b3492baf90..7594b465fefb 100644 --- a/core/modules/migrate_drupal/migrate_drupal.services.yml +++ b/core/modules/migrate_drupal/migrate_drupal.services.yml @@ -16,3 +16,12 @@ services: - '@module_handler' - '\Drupal\migrate_drupal\Annotation\MigrateCckField' deprecated: The "%service_id%" service is deprecated. You should use the 'plugin.manager.migrate.field' service instead. See https://www.drupal.org/node/2751897 + logger.channel.migrate_drupal: + parent: logger.channel_base + arguments: ['migrate_drupal'] + migrate_drupal.field_discovery: + class: Drupal\migrate_drupal\FieldDiscovery + arguments: + - '@plugin.manager.migrate.field' + - '@plugin.manager.migration' + - '@logger.channel.migrate_drupal' diff --git a/core/modules/migrate_drupal/src/FieldDiscovery.php b/core/modules/migrate_drupal/src/FieldDiscovery.php new file mode 100644 index 000000000000..6f6fdba45d69 --- /dev/null +++ b/core/modules/migrate_drupal/src/FieldDiscovery.php @@ -0,0 +1,385 @@ +<?php + +namespace Drupal\migrate_drupal; + +use Drupal\Component\Plugin\Exception\PluginNotFoundException; +use Drupal\Core\Logger\LoggerChannelInterface; +use Drupal\migrate\Exception\RequirementsException; +use Drupal\migrate\Plugin\MigrationInterface; +use Drupal\migrate\Plugin\MigrationPluginManagerInterface; +use Drupal\migrate\Plugin\RequirementsInterface; +use Drupal\migrate_drupal\Plugin\MigrateCckFieldInterface; +use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface; + +/** + * Provides field discovery for Drupal 6 & 7 migrations. + */ +class FieldDiscovery implements FieldDiscoveryInterface { + + /** + * The CCK plugin manager. + * + * @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface + */ + protected $cckPluginManager; + + /** + * An array of already discovered field plugin information. + * + * @var array + */ + protected $fieldPluginCache; + + /** + * The field plugin manager. + * + * @var \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface + */ + protected $fieldPluginManager; + + /** + * The migration plugin manager. + * + * @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface + */ + protected $migrationPluginManager; + + /** + * The logger channel service. + * + * @var \Drupal\Core\Logger\LoggerChannelInterface + */ + protected $logger; + + /** + * A cache of discovered fields. + * + * It is an array of arrays. If the entity type is bundleable, a third level + * of arrays is added to account for fields discovered at the bundle level. + * + * [{core}][{entity_type}][{bundle}] + * + * @var array + */ + protected $discoveredFieldsCache = []; + + /** + * An array of bundle keys, keyed by drupal core version. + * + * In Drupal 6, only nodes were fieldable, and the bundles were called + * 'type_name'. In Drupal 7, everything became entities, and the more + * generic 'bundle' was used. + * + * @var array + */ + protected $bundleKeys = [ + FieldDiscoveryInterface::DRUPAL_6 => 'type_name', + FieldDiscoveryInterface::DRUPAL_7 => 'bundle', + ]; + + /** + * An array of source plugin ids, keyed by Drupal core version. + * + * @var array + */ + protected $sourcePluginIds = [ + FieldDiscoveryInterface::DRUPAL_6 => 'd6_field_instance', + FieldDiscoveryInterface::DRUPAL_7 => 'd7_field_instance', + ]; + + /** + * An array of supported Drupal core versions. + * + * @var array + */ + protected $supportedCoreVersions = [ + FieldDiscoveryInterface::DRUPAL_6, + FieldDiscoveryInterface::DRUPAL_7, + ]; + + /** + * Constructs a FieldDiscovery object. + * + * @param \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface $field_plugin_manager + * The field plugin manager. + * @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migration_plugin_manager + * The migration plugin manager. + * @param \Drupal\Core\Logger\LoggerChannelInterface $logger + * The logger channel service. + */ + public function __construct(MigrateFieldPluginManagerInterface $field_plugin_manager, MigrationPluginManagerInterface $migration_plugin_manager, LoggerChannelInterface $logger) { + $this->fieldPluginManager = $field_plugin_manager; + $this->migrationPluginManager = $migration_plugin_manager; + $this->logger = $logger; + } + + /** + * {@inheritdoc} + */ + public function addAllFieldProcesses(MigrationInterface $migration) { + $core = $this->getCoreVersion($migration); + $fields = $this->getAllFields($core); + foreach ($fields as $entity_type_id => $bundle) { + $this->addEntityFieldProcesses($migration, $entity_type_id); + } + } + + /** + * {@inheritdoc} + */ + public function addEntityFieldProcesses(MigrationInterface $migration, $entity_type_id) { + $core = $this->getCoreVersion($migration); + $fields = $this->getAllFields($core); + if (!empty($fields[$entity_type_id]) && is_array($fields[$entity_type_id])) { + foreach ($fields[$entity_type_id] as $bundle => $fields) { + $this->addBundleFieldProcesses($migration, $entity_type_id, $bundle); + } + } + } + + /** + * {@inheritdoc} + */ + public function addBundleFieldProcesses(MigrationInterface $migration, $entity_type_id, $bundle) { + $core = $this->getCoreVersion($migration); + $fields = $this->getAllFields($core); + $plugin_definition = $migration->getPluginDefinition(); + if (empty($fields[$entity_type_id][$bundle])) { + return; + } + $bundle_fields = $fields[$entity_type_id][$bundle]; + foreach ($bundle_fields as $field_name => $field_info) { + $plugin = $this->getFieldPlugin($field_info['type'], $migration); + if ($plugin) { + $method = isset($plugin_definition['field_plugin_method']) ? $plugin_definition['field_plugin_method'] : 'defineValueProcessPipeline'; + + // @todo Remove the following 3 lines of code prior to Drupal 9.0.0. + // https://www.drupal.org/node/3032317 + if ($plugin instanceof MigrateCckFieldInterface) { + $method = isset($plugin_definition['cck_plugin_method']) ? $plugin_definition['cck_plugin_method'] : 'processCckFieldValues'; + } + + call_user_func_array([ + $plugin, + $method, + ], [ + $migration, + $field_name, + $field_info, + ]); + } + else { + // Default to a get process plugin if this is a value migration. + if ((empty($plugin_definition['field_plugin_method']) || $plugin_definition['field_plugin_method'] === 'defineValueProcessPipeline') && (empty($plugin_definition['cck_plugin_method']) || $plugin_definition['cck_plugin_method'] === 'processCckFieldValues')) { + $migration->setProcessOfProperty($field_name, $field_name); + } + } + } + } + + /** + * Returns the appropriate field plugin for a given field type. + * + * @param string $field_type + * The field type. + * @param \Drupal\migrate\Plugin\MigrationInterface $migration + * The migration to retrieve the plugin for. + * + * @return \Drupal\migrate_drupal\Plugin\MigrateCckFieldInterface|\Drupal\migrate_drupal\Plugin\MigrateFieldInterface|bool + * The appropriate field or cck plugin to process this field type. + * + * @throws \Drupal\Component\Plugin\Exception\PluginException + * @throws \InvalidArgumentException + */ + protected function getFieldPlugin($field_type, MigrationInterface $migration) { + $core = $this->getCoreVersion($migration); + if (!isset($this->fieldPluginCache[$core][$field_type])) { + try { + $plugin_id = $this->fieldPluginManager->getPluginIdFromFieldType($field_type, ['core' => $core], $migration); + $plugin = $this->fieldPluginManager->createInstance($plugin_id, ['core' => $core], $migration); + } + catch (PluginNotFoundException $ex) { + // @todo Replace try/catch block with $plugin = FALSE for Drupal 9. + // https://www.drupal.org/project/drupal/issues/3033733 + try { + /** @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManager $cck_plugin_manager */ + $cck_plugin_manager = $this->getCckPluginManager(); + $plugin_id = $cck_plugin_manager->getPluginIdFromFieldType($field_type, ['core' => $core], $migration); + $plugin = $cck_plugin_manager->createInstance($plugin_id, ['core' => $core], $migration); + } + catch (PluginNotFoundException $ex) { + $plugin = FALSE; + } + } + $this->fieldPluginCache[$core][$field_type] = $plugin; + } + return $this->fieldPluginCache[$core][$field_type]; + } + + /** + * Gets all field information related to this migration. + * + * @param string $core + * The Drupal core version to get fields for. + * + * @return array + * A multidimensional array of source data from the relevant field instance + * migration, keyed first by entity type, then by bundle and finally by + * field name. + */ + protected function getAllFields($core) { + if (empty($this->discoveredFieldsCache[$core])) { + $this->discoveredFieldsCache[$core] = []; + $source_plugin = $this->getSourcePlugin($core); + foreach ($source_plugin as $row) { + /** @var \Drupal\migrate\Row $row */ + if ($core === FieldDiscoveryInterface::DRUPAL_7) { + $entity_type_id = $row->get('entity_type'); + } + else { + $entity_type_id = 'node'; + } + $bundle = $row->getSourceProperty($this->bundleKeys[$core]); + $this->discoveredFieldsCache[$core][$entity_type_id][$bundle][$row->getSourceProperty('field_name')] = $row->getSource(); + } + } + return $this->discoveredFieldsCache[$core]; + } + + /** + * Gets all field information for a particular entity type. + * + * @param string $core + * The Drupal core version. + * @param string $entity_type_id + * The legacy entity type ID. + * + * @return array + * A multidimensional array of source data from the relevant field instance + * migration for the entity type, keyed first by bundle and then by field + * name. + */ + protected function getEntityFields($core, $entity_type_id) { + $fields = $this->getAllFields($core); + if (!empty($fields[$entity_type_id])) { + return $fields[$entity_type_id]; + } + return []; + } + + /** + * Gets all field information for a particular entity type and bundle. + * + * @param string $core + * The Drupal core version. + * @param string $entity_type_id + * The legacy entity type ID. + * @param string $bundle + * The legacy bundle (or content_type). + * + * @return array + * An array of source data from the relevant field instance migration for + * the bundle, keyed by field name. + */ + protected function getBundleFields($core, $entity_type_id, $bundle) { + $fields = $this->getEntityFields($core, $entity_type_id); + if (!empty($fields[$bundle])) { + return $fields[$bundle]; + } + return []; + } + + /** + * Gets the deprecated CCK Plugin Manager service as a BC shim. + * + * We don't inject this service directly because it is deprecated, and we + * don't want to instantiate the plugin manager unless we have to, to avoid + * triggering deprecation errors. + * + * @return \Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface + * The CCK Plugin Manager. + */ + protected function getCckPluginManager() { + if (!$this->cckPluginManager) { + $this->cckPluginManager = \Drupal::service('plugin.manager.migrate.cckfield'); + } + return $this->cckPluginManager; + } + + /** + * Gets the source plugin to use to gather field information. + * + * @param string $core + * The Drupal core version. + * + * @return array|\Drupal\migrate\Plugin\MigrateSourceInterface + * The source plugin, or an empty array if none can be found that meets + * requirements. + */ + protected function getSourcePlugin($core) { + $definition = $this->getFieldInstanceStubMigrationDefinition($core); + $source_plugin = $this->migrationPluginManager + ->createStubMigration($definition) + ->getSourcePlugin(); + if ($source_plugin instanceof RequirementsInterface) { + try { + $source_plugin->checkRequirements(); + } + catch (RequirementsException $e) { + // If checkRequirements() failed, the source database did not support + // fields (i.e., CCK is not installed in D6 or Field is not installed in + // D7). Therefore, $fields will be empty and below we'll return an empty + // array. The migration will proceed without adding fields. + $this->logger->notice('Field discovery failed for Drupal core version @core. Did this site have the CCK or Field module installed? Error: @message', [ + '@core' => $core, + '@message' => $e->getMessage(), + ]); + return []; + } + } + return $source_plugin; + } + + /** + * Provides the stub migration definition for a given Drupal core version. + * + * @param string $core + * The Drupal core version. + * + * @return array + * The stub migration definition. + */ + protected function getFieldInstanceStubMigrationDefinition($core) { + return [ + 'destination' => ['plugin' => 'null'], + 'idMap' => ['plugin' => 'null'], + 'source' => [ + 'ignore_map' => TRUE, + 'plugin' => $this->sourcePluginIds[$core], + ], + ]; + } + + /** + * Finds the core version of a Drupal migration. + * + * @param \Drupal\migrate\Plugin\MigrationInterface $migration + * The migration. + * + * @return string|bool + * A string representation of the Drupal version, or FALSE. + * + * @throws \InvalidArgumentException + */ + protected function getCoreVersion(MigrationInterface $migration) { + $tags = $migration->getMigrationTags(); + if (in_array('Drupal 7', $tags, TRUE)) { + return FieldDiscoveryInterface::DRUPAL_7; + } + elseif (in_array('Drupal 6', $tags, TRUE)) { + return FieldDiscoveryInterface::DRUPAL_6; + } + throw new \InvalidArgumentException("Drupal Core version not found for this migration"); + } + +} diff --git a/core/modules/migrate_drupal/src/FieldDiscoveryInterface.php b/core/modules/migrate_drupal/src/FieldDiscoveryInterface.php new file mode 100644 index 000000000000..5648f219eb69 --- /dev/null +++ b/core/modules/migrate_drupal/src/FieldDiscoveryInterface.php @@ -0,0 +1,68 @@ +<?php + +namespace Drupal\migrate_drupal; + +use Drupal\migrate\Plugin\MigrationInterface; + +/** + * Provides field discovery for Drupal 6 & 7 migrations. + */ +interface FieldDiscoveryInterface { + + const DRUPAL_6 = '6'; + + const DRUPAL_7 = '7'; + + /** + * Adds the field processes to a migration. + * + * This method is used in field migrations to execute the migration process + * alter method specified by the 'field_plugin_method' key of the migration + * for all field plugins applicable to this Drupal to Drupal migration. This + * method is used internally for field, field instance, widget, and formatter + * migrations to allow field plugins to alter the process for these + * migrations. + * + * @param \Drupal\migrate\Plugin\MigrationInterface $migration + * The migration to add process plugins to. + * + * @throws \InvalidArgumentException + * + * @internal + */ + public function addAllFieldProcesses(MigrationInterface $migration); + + /** + * Adds the field processes for an entity to a migration. + * + * This method is used in field migrations to execute the migration process + * alter method specified by the 'field_plugin_method' key of the migration + * for all field plugins applicable to this Drupal to Drupal migration. This + * method is used internally for field, field instance, widget, and formatter + * migrations to allow field plugins to alter the process for these + * migrations. + * + * @param \Drupal\migrate\Plugin\MigrationInterface $migration + * The migration to add processes to. + * @param string $entity_type_id + * The legacy entity type to add processes for. + * + * @throws \InvalidArgumentException + */ + public function addEntityFieldProcesses(MigrationInterface $migration, $entity_type_id); + + /** + * Adds the field processes for a bundle to a migration. + * + * @param \Drupal\migrate\Plugin\MigrationInterface $migration + * The migration to add processes to. + * @param string $entity_type_id + * The legacy entity type to add processes for. + * @param string $bundle + * The legacy bundle (or content_type) to add processes for. + * + * @throws \InvalidArgumentException + */ + public function addBundleFieldProcesses(MigrationInterface $migration, $entity_type_id, $bundle); + +} diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/FieldMigration.php b/core/modules/migrate_drupal/src/Plugin/migrate/FieldMigration.php index 3ca8f26a8bbe..f84bdb4a0382 100644 --- a/core/modules/migrate_drupal/src/Plugin/migrate/FieldMigration.php +++ b/core/modules/migrate_drupal/src/Plugin/migrate/FieldMigration.php @@ -2,16 +2,12 @@ namespace Drupal\migrate_drupal\Plugin\migrate; -use Drupal\Component\Plugin\Exception\PluginNotFoundException; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; -use Drupal\migrate\Exception\RequirementsException; use Drupal\migrate\Plugin\MigrateDestinationPluginManager; use Drupal\migrate\Plugin\MigratePluginManager; use Drupal\migrate\Plugin\Migration; use Drupal\migrate\Plugin\MigrationPluginManagerInterface; -use Drupal\migrate\Plugin\RequirementsInterface; -use Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface; -use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface; +use Drupal\migrate_drupal\FieldDiscoveryInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -27,6 +23,7 @@ class FieldMigration extends Migration implements ContainerFactoryPluginInterfac * fallback to the old 'cck_plugin_method'. * * @const string + * @deprecated This constant is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use the migrate_drupal.field_discovery service instead. See https://www.drupal.org/node/3006076. */ const PLUGIN_METHOD = 'field_plugin_method'; @@ -38,39 +35,11 @@ class FieldMigration extends Migration implements ContainerFactoryPluginInterfac protected $init = FALSE; /** - * List of field plugin IDs which have already run. + * The migration field discovery service. * - * @var string[] + * @var \Drupal\migrate_drupal\FieldDiscoveryInterface */ - protected $processedFieldTypes = []; - - /** - * Already-instantiated field plugins, keyed by ID. - * - * @var \Drupal\migrate_drupal\Plugin\MigrateFieldInterface[] - */ - protected $fieldPluginCache; - - /** - * The field plugin manager. - * - * @var \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface - */ - protected $fieldPluginManager; - - /** - * Already-instantiated cckfield plugins, keyed by ID. - * - * @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldInterface[] - */ - protected $cckPluginCache; - - /** - * The cckfield plugin manager. - * - * @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface - */ - protected $cckPluginManager; + protected $fieldDiscovery; /** * Constructs a FieldMigration. @@ -81,10 +50,6 @@ class FieldMigration extends Migration implements ContainerFactoryPluginInterfac * The plugin ID. * @param mixed $plugin_definition * The plugin definition. - * @param \Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface $cck_manager - * The cckfield plugin manager. - * @param \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface $field_manager - * The field plugin manager. * @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migration_plugin_manager * The migration plugin manager. * @param \Drupal\migrate\Plugin\MigratePluginManager $source_plugin_manager @@ -95,11 +60,12 @@ class FieldMigration extends Migration implements ContainerFactoryPluginInterfac * The destination migration plugin manager. * @param \Drupal\migrate\Plugin\MigratePluginManager $idmap_plugin_manager * The ID map migration plugin manager. + * @param \Drupal\migrate_drupal\FieldDiscoveryInterface $field_discovery + * The migration field discovery service. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrateCckFieldPluginManagerInterface $cck_manager, MigrateFieldPluginManagerInterface $field_manager, MigrationPluginManagerInterface $migration_plugin_manager, MigratePluginManager $source_plugin_manager, MigratePluginManager $process_plugin_manager, MigrateDestinationPluginManager $destination_plugin_manager, MigratePluginManager $idmap_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, FieldDiscoveryInterface $field_discovery) { parent::__construct($configuration, $plugin_id, $plugin_definition, $migration_plugin_manager, $source_plugin_manager, $process_plugin_manager, $destination_plugin_manager, $idmap_plugin_manager); - $this->cckPluginManager = $cck_manager; - $this->fieldPluginManager = $field_manager; + $this->fieldDiscovery = $field_discovery; } /** @@ -110,13 +76,12 @@ public static function create(ContainerInterface $container, array $configuratio $configuration, $plugin_id, $plugin_definition, - $container->get('plugin.manager.migrate.cckfield'), - $container->get('plugin.manager.migrate.field'), $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') + $container->get('plugin.manager.migrate.id_map'), + $container->get('migrate_drupal.field_discovery') ); } @@ -126,44 +91,7 @@ public static function create(ContainerInterface $container, array $configuratio public function getProcess() { if (!$this->init) { $this->init = TRUE; - $source_plugin = $this->migrationPluginManager->createInstance($this->pluginId)->getSourcePlugin(); - if ($source_plugin instanceof RequirementsInterface) { - try { - $source_plugin->checkRequirements(); - } - catch (RequirementsException $e) { - // Kill the rest of the method. - $source_plugin = []; - } - } - foreach ($source_plugin as $row) { - $field_type = $row->getSourceProperty('type'); - - try { - $plugin_id = $this->fieldPluginManager->getPluginIdFromFieldType($field_type, [], $this); - $manager = $this->fieldPluginManager; - } - catch (PluginNotFoundException $ex) { - try { - $plugin_id = $this->cckPluginManager->getPluginIdFromFieldType($field_type, [], $this); - $manager = $this->cckPluginManager; - } - catch (PluginNotFoundException $ex) { - continue; - } - } - - if (!isset($this->processedFieldTypes[$field_type]) && $manager->hasDefinition($plugin_id)) { - $this->processedFieldTypes[$field_type] = TRUE; - // Allow the field plugin to alter the migration as necessary so that - // it knows how to handle fields of this type. - if (!isset($this->fieldPluginCache[$field_type])) { - $this->fieldPluginCache[$field_type] = $manager->createInstance($plugin_id, [], $this); - } - } - $method = $this->pluginDefinition[static::PLUGIN_METHOD]; - call_user_func([$this->fieldPluginCache[$field_type], $method], $this); - } + $this->fieldDiscovery->addAllFieldProcesses($this); } return parent::getProcess(); } diff --git a/core/modules/migrate_drupal/tests/modules/field_discovery_test/field_discovery_test.info.yml b/core/modules/migrate_drupal/tests/modules/field_discovery_test/field_discovery_test.info.yml new file mode 100644 index 000000000000..effa82da7752 --- /dev/null +++ b/core/modules/migrate_drupal/tests/modules/field_discovery_test/field_discovery_test.info.yml @@ -0,0 +1,6 @@ +name: 'Migrate drupal field discovery tet' +type: module +description: 'Module containing a test class exposing protected field discovery methods' +package: Testing +version: VERSION +core: 8.x diff --git a/core/modules/migrate_drupal/tests/modules/field_discovery_test/src/FieldDiscoveryTestClass.php b/core/modules/migrate_drupal/tests/modules/field_discovery_test/src/FieldDiscoveryTestClass.php new file mode 100644 index 000000000000..708f1d199062 --- /dev/null +++ b/core/modules/migrate_drupal/tests/modules/field_discovery_test/src/FieldDiscoveryTestClass.php @@ -0,0 +1,93 @@ +<?php + +namespace Drupal\field_discovery_test; + +use Drupal\Core\Logger\LoggerChannelInterface; +use Drupal\migrate\Plugin\MigrationPluginManagerInterface; +use Drupal\migrate_drupal\FieldDiscovery; +use Drupal\migrate\Plugin\MigrationInterface; +use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface; + +/** + * A test class to expose protected methods. + */ +class FieldDiscoveryTestClass extends FieldDiscovery { + + /** + * An array of test data. + * + * @var array + */ + protected $testData; + + /** + * Constructs a FieldDiscoveryTestClass object. + * + * @param \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface $field_plugin_manager + * The field plugin manager. + * @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migration_plugin_manager + * The migration plugin manager. + * @param \Drupal\Core\Logger\LoggerChannelInterface $logger + * The logger. + * @param array $test_data + * An array of test data, keyed by method name, for overridden methods to + * return for the purposes of testing other methods. + */ + public function __construct(MigrateFieldPluginManagerInterface $field_plugin_manager, MigrationPluginManagerInterface $migration_plugin_manager, LoggerChannelInterface $logger, array $test_data = []) { + parent::__construct($field_plugin_manager, $migration_plugin_manager, $logger); + $this->testData = $test_data; + } + + /** + * {@inheritdoc} + */ + public function getAllFields($core) { + if (!empty($this->testData['getAllFields'][$core])) { + return $this->testData['getAllFields'][$core]; + } + return parent::getAllFields($core); + } + + /** + * {@inheritdoc} + */ + public function getBundleFields($core, $entity_type_id, $bundle) { + return parent::getBundleFields($core, $entity_type_id, $bundle); + } + + /** + * {@inheritdoc} + */ + public function getEntityFields($core, $entity_type_id) { + return parent::getEntityFields($core, $entity_type_id); + } + + /** + * {@inheritdoc} + */ + public function getFieldInstanceStubMigrationDefinition($core) { + return parent::getFieldInstanceStubMigrationDefinition($core); + } + + /** + * {@inheritdoc} + */ + public function getCoreVersion(MigrationInterface $migration) { + return parent::getCoreVersion($migration); + } + + /** + * {@inheritdoc} + */ + public function getFieldPlugin($field_type, MigrationInterface $migration) { + return parent::getFieldPlugin($field_type, $migration); + } + + /** + * {@inheritdoc} + */ + public function getSourcePlugin($core) { + return parent::getSourcePlugin($core); + } + +} diff --git a/core/modules/migrate_drupal/tests/src/Kernel/d6/FieldDiscoveryTest.php b/core/modules/migrate_drupal/tests/src/Kernel/d6/FieldDiscoveryTest.php new file mode 100644 index 000000000000..cc2fb8c373cd --- /dev/null +++ b/core/modules/migrate_drupal/tests/src/Kernel/d6/FieldDiscoveryTest.php @@ -0,0 +1,304 @@ +<?php + +namespace Drupal\Tests\migrate_drupal\Kernel\d6; + +use Drupal\field\Plugin\migrate\source\d6\FieldInstance; +use Drupal\field_discovery_test\FieldDiscoveryTestClass; +use Drupal\migrate_drupal\FieldDiscoveryInterface; +use Drupal\Tests\migrate_drupal\Traits\FieldDiscoveryTestTrait; + +/** + * Tests FieldDiscovery service against Drupal 6. + * + * @group migrate_drupal + * @coversDefaultClass \Drupal\migrate_drupal\FieldDiscovery + */ +class FieldDiscoveryTest extends MigrateDrupal6TestBase { + + use FieldDiscoveryTestTrait; + /** + * {@inheritdoc} + */ + public static $modules = [ + 'menu_ui', + 'comment', + 'datetime', + 'file', + 'image', + 'link', + 'node', + 'system', + 'taxonomy', + 'telephone', + 'text', + ]; + + /** + * The Field discovery service. + * + * @var \Drupal\migrate_drupal\FieldDiscoveryInterface + */ + protected $fieldDiscovery; + + /** + * The field plugin manager. + * + * @var \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface + */ + protected $fieldPluginManager; + + /** + * The migration plugin manager. + * + * @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface + */ + protected $migrationPluginManager; + /** + * The logger. + * + * @var \Drupal\Core\Logger\LoggerChannelInterface + */ + protected $logger; + + /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp(); + $this->installConfig(['node']); + $this->executeMigration('d6_node_type'); + $this->executeMigration('d6_field'); + $this->executeMigration('d6_field_instance'); + $this->fieldDiscovery = $this->container->get('migrate_drupal.field_discovery'); + $this->migrationPluginManager = $this->container->get('plugin.manager.migration'); + $this->fieldPluginManager = $this->container->get('plugin.manager.migrate.field'); + $this->logger = $this->container->get('logger.channel.migrate_drupal'); + + } + + /** + * Tests the addAllFieldProcesses method. + * + * @covers ::addAllFieldProcesses + */ + public function testAddAllFieldProcesses() { + $expected_process_keys = [ + 'field_commander', + 'field_company', + 'field_company_2', + 'field_company_3', + 'field_sync', + 'field_multivalue', + 'field_test_text_single_checkbox', + 'field_reference', + 'field_reference_2', + 'field_test', + 'field_test_date', + 'field_test_datestamp', + 'field_test_datetime', + 'field_test_decimal_radio_buttons', + 'field_test_email', + 'field_test_exclude_unset', + 'field_test_filefield', + 'field_test_float_single_checkbox', + 'field_test_four', + 'field_test_identical1', + 'field_test_identical2', + 'field_test_imagefield', + 'field_test_integer_selectlist', + 'field_test_link', + 'field_test_phone', + 'field_test_string_selectlist', + 'field_test_text_single_checkbox2', + 'field_test_three', + 'field_test_two', + ]; + $this->assertFieldProcessKeys($this->fieldDiscovery, $this->migrationPluginManager, FieldDiscoveryInterface::DRUPAL_6, $expected_process_keys); + } + + /** + * Tests the addAllFieldProcesses method for field migrations. + * + * @covers ::addAllFieldProcesses + * @dataProvider addAllFieldProcessesAltersData + */ + public function testAddAllFieldProcessesAlters($field_plugin_method, $expected_process) { + $this->assertFieldProcess($this->fieldDiscovery, $this->migrationPluginManager, FieldDiscoveryInterface::DRUPAL_6, $field_plugin_method, $expected_process); + } + + /** + * Provides data for testAddAllFieldProcessesAlters. + * + * @return array + * The data. + */ + public function addAllFieldProcessesAltersData() { + return [ + 'Field Formatter' => [ + 'field_plugin_method' => 'alterFieldFormatterMigration', + 'expected_process' => [ + 'options/type' => [ + 0 => [ + 'map' => [ + 'email' => [ + 'email_formatter_default' => 'email_mailto', + 'email_formatter_contact' => 'basic_string', + 'email_formatter_plain' => 'basic_string', + 'email_formatter_spamspan' => 'basic_string', + 'email_default' => 'email_mailto', + 'email_contact' => 'basic_string', + 'email_plain' => 'basic_string', + 'email_spamspan' => 'basic_string', + ], + 'text' => [ + 'default' => 'text_default', + 'trimmed' => 'text_trimmed', + 'plain' => 'basic_string', + ], + 'datetime' => [ + 'date_default' => 'datetime_default', + ], + 'filefield' => [ + 'default' => 'file_default', + 'url_plain' => 'file_url_plain', + 'path_plain' => 'file_url_plain', + 'image_plain' => 'image', + 'image_nodelink' => 'image', + 'image_imagelink' => 'image', + ], + 'link' => [ + 'default' => 'link', + 'plain' => 'link', + 'absolute' => 'link', + 'title_plain' => 'link', + 'url' => 'link', + 'short' => 'link', + 'label' => 'link', + 'separate' => 'link_separate', + ], + ], + ], + ], + ], + ], + 'Field Widget' => [ + 'field_plugin_method' => 'alterFieldWidgetMigration', + 'expected_process' => [ + 'options/type' => [ + 'type' => [ + 'map' => [ + 'userreference' => 'userreference_default', + 'nodereference' => 'nodereference_default', + 'email_textfield' => 'email_default', + 'text_textfield' => 'text_textfield', + 'date' => 'datetime_default', + 'datetime' => 'datetime_default', + 'datestamp' => 'datetime_timestamp', + 'filefield_widget' => 'file_generic', + 'link' => 'link_default', + ], + ], + ], + ], + ], + ]; + } + + /** + * Tests the addFields method. + * + * @covers ::addAllFieldProcesses + */ + public function testAddFields() { + $this->migrateFields(); + $field_discovery = $this->container->get('migrate_drupal.field_discovery'); + $migration_plugin_manager = $this->container->get('plugin.manager.migration'); + $definition = [ + 'migration_tags' => ['Drupal 6'], + ]; + $migration = $migration_plugin_manager->createStubMigration($definition); + $field_discovery->addBundleFieldProcesses($migration, 'node', 'test_planet'); + $actual_process = $migration->getProcess(); + $expected_process = [ + 'field_multivalue' => [ + 0 => [ + 'plugin' => 'get', + 'source' => 'field_multivalue', + ], + ], + 'field_test_text_single_checkbox' => [ + 0 => [ + 'plugin' => 'sub_process', + 'source' => 'field_test_text_single_checkbox', + 'process' => [ + 'value' => 'value', + 'format' => [ + 0 => [ + 'plugin' => 'static_map', + 'bypass' => TRUE, + 'source' => 'format', + 'map' => [ + 0 => NULL, + ], + ], + 1 => [ + 'plugin' => 'skip_on_empty', + 'method' => 'process', + ], + 2 => [ + 'plugin' => 'migration', + 'migration' => [ + 0 => 'd6_filter_format', + 1 => 'd7_filter_format', + ], + 'source' => 'format', + ], + ], + ], + ], + ], + ]; + $this->assertEquals($expected_process, $actual_process); + } + + /** + * Tests the getAllFields method. + * + * @covers ::getAllFields + */ + public function testGetAllFields() { + $field_discovery_test = new FieldDiscoveryTestClass($this->fieldPluginManager, $this->migrationPluginManager, $this->logger); + $actual_fields = $field_discovery_test->getAllFields('6'); + $this->assertSame(['node'], array_keys($actual_fields)); + $this->assertSame(['employee', 'test_planet', 'page', 'story', 'test_page'], array_keys($actual_fields['node'])); + $this->assertSame(21, count($actual_fields['node']['story'])); + foreach ($actual_fields['node'] as $bundle => $fields) { + foreach ($fields as $field_name => $field_info) { + $this->assertArrayHasKey('type', $field_info); + $this->assertSame(22, count($field_info)); + $this->assertEquals($bundle, $field_info['type_name']); + } + } + } + + /** + * Tests the getSourcePlugin method. + * + * @covers ::getSourcePlugin + */ + public function testGetSourcePlugin() { + $this->assertSourcePlugin('6', FieldInstance::class, [ + 'requirements_met' => TRUE, + 'id' => 'd6_field_instance', + 'source_module' => 'content', + 'class' => 'Drupal\\field\\Plugin\\migrate\\source\\d6\\FieldInstance', + 'provider' => [ + 0 => 'field', + 1 => 'migrate_drupal', + 2 => 'migrate', + 4 => 'core', + ], + ]); + } + +} diff --git a/core/modules/migrate_drupal/tests/src/Kernel/d7/FieldDiscoveryTest.php b/core/modules/migrate_drupal/tests/src/Kernel/d7/FieldDiscoveryTest.php new file mode 100644 index 000000000000..a36a994e71e5 --- /dev/null +++ b/core/modules/migrate_drupal/tests/src/Kernel/d7/FieldDiscoveryTest.php @@ -0,0 +1,364 @@ +<?php + +namespace Drupal\Tests\migrate_drupal\Kernel\d7; + +use Drupal\comment\Entity\CommentType; +use Drupal\Component\Plugin\Exception\PluginNotFoundException; +use Drupal\field\Plugin\migrate\source\d7\FieldInstance; +use Drupal\migrate_drupal\FieldDiscovery; +use Drupal\migrate_drupal\FieldDiscoveryInterface; +use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface; +use Drupal\node\Entity\NodeType; +use Drupal\taxonomy\Entity\Vocabulary; +use Drupal\Tests\migrate_drupal\Traits\FieldDiscoveryTestTrait; +use Drupal\field_discovery_test\FieldDiscoveryTestClass; + +/** + * Test FieldDiscovery Service against Drupal 7. + * + * @group migrate_drupal + * @coversDefaultClass \Drupal\migrate_drupal\FieldDiscovery + */ +class FieldDiscoveryTest extends MigrateDrupal7TestBase { + + use FieldDiscoveryTestTrait; + + /** + * {@inheritdoc} + */ + public static $modules = [ + 'comment', + 'datetime', + 'file', + 'image', + 'link', + 'node', + 'system', + 'taxonomy', + 'telephone', + 'text', + ]; + + /** + * The Field discovery service. + * + * @var \Drupal\migrate_drupal\FieldDiscoveryInterface + */ + protected $fieldDiscovery; + + /** + * The field plugin manager. + * + * @var \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface + */ + protected $fieldPluginManager; + + /** + * The migration plugin manager. + * + * @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface + */ + protected $migrationPluginManager; + + /** + * The logger. + * + * @var \Drupal\Core\Logger\LoggerChannelInterface + */ + protected $logger; + + /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp(); + $this->installConfig(static::$modules); + $node_types = [ + 'page' => 'comment_node_page', + 'article' => 'comment_node_article', + 'blog' => 'comment_node_blog', + 'book' => 'comment_node_book', + 'forum' => 'comment_forum', + 'test_content_type' => 'comment_node_test_content_type', + ]; + foreach ($node_types as $node_type => $comment_type) { + NodeType::create([ + 'type' => $node_type, + 'label' => $this->randomString(), + ])->save(); + + CommentType::create([ + 'id' => $comment_type, + 'label' => $this->randomString(), + 'target_entity_type_id' => 'node', + ])->save(); + } + + Vocabulary::create(['vid' => 'test_vocabulary'])->save(); + $this->executeMigrations(['d7_field', 'd7_field_instance']); + + $this->fieldDiscovery = $this->container->get('migrate_drupal.field_discovery'); + $this->migrationPluginManager = $this->container->get('plugin.manager.migration'); + $this->fieldPluginManager = $this->container->get('plugin.manager.migrate.field'); + $this->logger = $this->container->get('logger.channel.migrate_drupal'); + } + + /** + * Tests the addAllFieldProcesses method. + * + * @covers ::addAllFieldProcesses + */ + public function testAddAllFieldProcesses() { + $expected_process_keys = [ + 'comment_body', + 'field_integer', + 'body', + 'field_text_plain', + 'field_text_filtered', + 'field_text_plain_filtered', + 'field_text_long_plain', + 'field_text_long_filtered', + 'field_text_long_plain_filtered', + 'field_text_sum_plain', + 'field_text_sum_filtered', + 'field_text_sum_plain_filtered', + 'field_tags', + 'field_image', + 'field_link', + 'field_reference', + 'field_reference_2', + 'taxonomy_forums', + 'field_boolean', + 'field_email', + 'field_phone', + 'field_date', + 'field_date_with_end_time', + 'field_file', + 'field_float', + 'field_images', + 'field_text_list', + 'field_integer_list', + 'field_long_text', + 'field_term_reference', + 'field_text', + 'field_node_entityreference', + 'field_user_entityreference', + 'field_term_entityreference', + 'field_private_file', + 'field_datetime_without_time', + 'field_date_without_time', + 'field_float_list', + ]; + $this->assertFieldProcessKeys($this->fieldDiscovery, $this->migrationPluginManager, '7', $expected_process_keys); + } + + /** + * Tests the addAllFieldProcesses method for field migrations. + * + * @covers ::addAllFieldProcesses + * @dataProvider addAllFieldProcessesAltersData + */ + public function testAddAllFieldProcessesAlters($field_plugin_method, $expected_process) { + $this->assertFieldProcess($this->fieldDiscovery, $this->migrationPluginManager, FieldDiscoveryInterface::DRUPAL_7, $field_plugin_method, $expected_process); + } + + /** + * Provides data for testAddAllFieldProcessesAlters. + * + * @return array + * The data. + */ + public function addAllFieldProcessesAltersData() { + return [ + 'Field Instance' => [ + 'field_plugin_method' => 'alterFieldInstanceMigration', + 'expected_process' => [ + 'settings/title' => [ + 0 => [ + 'plugin' => 'static_map', + 'source' => 'settings/title', + 'bypass' => TRUE, + 'map' => [ + 'disabled' => 0, + 'optional' => 1, + 'required' => 2, + ], + ], + ], + ], + ], + 'Field Formatter' => [ + 'field_plugin_method' => 'alterFieldFormatterMigration', + 'expected_process' => [ + 'options/type' => [ + 0 => [ + 'map' => [ + 'taxonomy_term_reference' => [ + 'taxonomy_term_reference_link' => 'entity_reference_label', + ], + 'link_field' => [ + 'link_default' => 'link', + ], + 'entityreference' => [ + 'entityreference_label' => 'entity_reference_label', + 'entityreference_entity_id' => 'entity_reference_entity_id', + 'entityreference_entity_view' => 'entity_reference_entity_view', + ], + 'email' => [ + 'email_formatter_default' => 'email_mailto', + 'email_formatter_contact' => 'basic_string', + 'email_formatter_plain' => 'basic_string', + 'email_formatter_spamspan' => 'basic_string', + 'email_default' => 'email_mailto', + 'email_contact' => 'basic_string', + 'email_plain' => 'basic_string', + 'email_spamspan' => 'basic_string', + ], + 'phone' => [ + 'phone' => 'basic_string', + ], + 'datetime' => [ + 'date_default' => 'datetime_default', + ], + 'file' => [ + 'default' => 'file_default', + 'url_plain' => 'file_url_plain', + 'path_plain' => 'file_url_plain', + 'image_plain' => 'image', + 'image_nodelink' => 'image', + 'image_imagelink' => 'image', + ], + ], + ], + ], + ], + ], + 'Field Widget' => [ + 'field_plugin_method' => 'alterFieldWidgetMigration', + 'expected_process' => [ + 'options/type' => [ + 'type' => [ + 'map' => [ + 'd7_text' => 'd7_text_default', + 'number_default' => 'number_default_default', + 'taxonomy_term_reference' => 'taxonomy_term_reference_default', + 'image' => 'image_default', + 'link_field' => 'link_default', + 'entityreference' => 'entityreference_default', + 'list' => 'list_default', + 'email_textfield' => 'email_default', + 'phone' => 'phone_default', + 'date' => 'datetime_default', + 'datetime' => 'datetime_default', + 'datestamp' => 'datetime_timestamp', + 'filefield_widget' => 'file_generic', + ], + ], + ], + ], + ], + ]; + } + + /** + * Tests the getAllFields method. + * + * @covers ::getAllFields + */ + public function testGetAllFields() { + $field_discovery_test = new FieldDiscoveryTestClass($this->fieldPluginManager, $this->migrationPluginManager, $this->logger); + $actual_fields = $field_discovery_test->getAllFields('7'); + $this->assertSame(['comment', 'node', 'user', 'taxonomy_term'], array_keys($actual_fields)); + $this->assertArrayHasKey('test_vocabulary', $actual_fields['taxonomy_term']); + $this->assertArrayHasKey('user', $actual_fields['user']); + $this->assertArrayHasKey('test_content_type', $actual_fields['node']); + $this->assertSame(6, count($actual_fields['node'])); + $this->assertSame(6, count($actual_fields['comment'])); + $this->assertSame(22, count($actual_fields['node']['test_content_type'])); + foreach ($actual_fields as $entity_type_id => $bundles) { + foreach ($bundles as $bundle => $fields) { + foreach ($fields as $field_name => $field_info) { + $this->assertArrayHasKey('field_definition', $field_info); + $this->assertEquals($entity_type_id, $field_info['entity_type']); + $this->assertEquals($bundle, $field_info['bundle']); + } + } + } + } + + /** + * Tests the getSourcePlugin method. + * + * @covers ::getSourcePlugin + */ + public function testGetSourcePlugin() { + $this->assertSourcePlugin('7', FieldInstance::class, [ + 'requirements_met' => TRUE, + 'id' => 'd7_field_instance', + 'source_module' => 'field', + 'class' => 'Drupal\\field\\Plugin\\migrate\\source\\d7\\FieldInstance', + 'provider' => [ + 0 => 'field', + 1 => 'migrate_drupal', + 2 => 'migrate', + 4 => 'core', + ], + ]); + } + + /** + * Tests the fallback to deprecated CCK Plugin Manager. + * + * @covers ::getCckPluginManager + * @group legacy + * @expectedDeprecation TextField is deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.x. Use \Drupal\text\Plugin\migrate\field\d6\TextField or \Drupal\text\Plugin\migrate\field\d7\TextField instead. + * @expectedDeprecation CckFieldPluginBase is deprecated in Drupal 8.3.x and will be be removed before Drupal 9.0.x. Use \Drupal\migrate_drupal\Plugin\migrate\field\FieldPluginBase instead. + * @expectedDeprecation MigrateCckFieldInterface is deprecated in Drupal 8.3.x and will be removed before Drupal 9.0.x. Use \Drupal\migrate_drupal\Annotation\MigrateField instead. + */ + public function testGetCckPluginManager() { + $definition = [ + 'migration_tags' => ['Drupal 7'], + ]; + $migration = $this->migrationPluginManager->createStubMigration($definition); + $field_plugin_manager = $this->prophesize(MigrateFieldPluginManagerInterface::class); + $field_plugin_manager->getPluginIdFromFieldType('text_long', ['core' => '7'], $migration)->willThrow(PluginNotFoundException::class); + $field_discovery = new FieldDiscovery($field_plugin_manager->reveal(), $this->migrationPluginManager, $this->logger); + $field_discovery->addBundleFieldProcesses($migration, 'comment', 'comment_node_page'); + $actual_process = $migration->getProcess(); + $expected_process = [ + 'comment_body' => [ + 0 => [ + 'plugin' => 'sub_process', + 'source' => 'comment_body', + 'process' => [ + 'value' => 'value', + 'format' => [ + 0 => [ + 'plugin' => 'static_map', + 'bypass' => TRUE, + 'source' => 'format', + 'map' => [ + 0 => NULL, + ], + ], + 1 => [ + 'plugin' => 'skip_on_empty', + 'method' => 'process', + ], + 2 => [ + 'plugin' => 'migration', + 'migration' => [ + 0 => 'd6_filter_format', + 1 => 'd7_filter_format', + ], + 'source' => 'format', + ], + ], + ], + ], + ], + ]; + $this->assertEquals($expected_process, $actual_process); + } + +} diff --git a/core/modules/migrate_drupal/tests/src/Traits/FieldDiscoveryTestTrait.php b/core/modules/migrate_drupal/tests/src/Traits/FieldDiscoveryTestTrait.php new file mode 100644 index 000000000000..34a16ad85ea7 --- /dev/null +++ b/core/modules/migrate_drupal/tests/src/Traits/FieldDiscoveryTestTrait.php @@ -0,0 +1,103 @@ +<?php + +namespace Drupal\Tests\migrate_drupal\Traits; + +use Drupal\field_discovery_test\FieldDiscoveryTestClass; +use Drupal\migrate\Plugin\MigrationPluginManagerInterface; +use Drupal\migrate_drupal\FieldDiscoveryInterface; + +/** + * Helper functions to test field discovery. + */ +trait FieldDiscoveryTestTrait { + + /** + * Asserts the field discovery returns the expected processes. + * + * @param \Drupal\migrate_drupal\FieldDiscoveryInterface $field_discovery + * The Field Discovery service. + * @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migration_plugin_manager + * The migration plugin manager service. + * @param string $core + * The Drupal core version, either '6', or '7'. + * @param string $field_plugin_method + * (optional) The field plugin method to use. + * @param array $expected_process + * (optional) The expected resulting process. + * @param string $entity_type_id + * (optional) The entity type id. + * @param string $bundle + * (optional) The bundle. + */ + public function assertFieldProcess(FieldDiscoveryInterface $field_discovery, MigrationPluginManagerInterface $migration_plugin_manager, $core, $field_plugin_method = NULL, array $expected_process = [], $entity_type_id = NULL, $bundle = NULL) { + $definition = [ + 'migration_tags' => ['Drupal ' . $core], + 'field_plugin_method' => $field_plugin_method, + ]; + $migration = $migration_plugin_manager->createStubMigration($definition); + if ($bundle) { + $field_discovery->addBundleFieldProcesses($migration, $entity_type_id, $bundle); + } + elseif ($entity_type_id) { + $field_discovery->addEntityFieldProcesses($migration, $entity_type_id); + } + else { + $field_discovery->addAllFieldProcesses($migration); + } + $actual_process = $migration->getProcess(); + $this->assertSame($expected_process, $actual_process); + } + + /** + * Asserts the field discovery returns the expected processes. + * + * @param \Drupal\migrate_drupal\FieldDiscoveryInterface $field_discovery + * The Field Discovery service. + * @param \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migration_plugin_manager + * The migration plugin manager service. + * @param string $core + * The Drupal core version, either '6', or '7'. + * @param array $expected_process_keys + * (optional) The expected resulting process_keys. + * @param string $entity_type_id + * (optional) The entity type id. + * @param string $bundle + * (optional) The bundle. + */ + public function assertFieldProcessKeys(FieldDiscoveryInterface $field_discovery, MigrationPluginManagerInterface $migration_plugin_manager, $core, array $expected_process_keys, $entity_type_id = NULL, $bundle = NULL) { + $definition = [ + 'migration_tags' => ['Drupal ' . $core], + ]; + $migration = $migration_plugin_manager->createStubMigration($definition); + if ($bundle) { + $field_discovery->addBundleFieldProcesses($migration, $entity_type_id, $bundle); + } + elseif ($entity_type_id) { + $field_discovery->addEntityFieldProcesses($migration, $entity_type_id); + } + else { + $field_discovery->addAllFieldProcesses($migration); + } + $actual_process = $migration->getProcess(); + $actual = array_keys($actual_process); + $this->assertSame(sort($expected_process_keys), sort($actual)); + } + + /** + * Asserts a migrate source plugin. + * + * @param string $core + * The Drupal core version. + * @param string $class + * The expected class of the source plugin. + * @param array $expected_definition + * The expected source plugin definition. + */ + public function assertSourcePlugin($core, $class, array $expected_definition) { + $field_discovery = new FieldDiscoveryTestClass($this->fieldPluginManager, $this->migrationPluginManager, $this->logger); + $source = $field_discovery->getSourcePlugin($core); + $this->assertInstanceOf($class, $source); + $this->assertSame($expected_definition, $source->getPluginDefinition()); + } + +} diff --git a/core/modules/migrate_drupal/tests/src/Unit/FieldDiscoveryTest.php b/core/modules/migrate_drupal/tests/src/Unit/FieldDiscoveryTest.php new file mode 100644 index 000000000000..852d4fa31961 --- /dev/null +++ b/core/modules/migrate_drupal/tests/src/Unit/FieldDiscoveryTest.php @@ -0,0 +1,358 @@ +<?php + +namespace Drupal\Tests\migrate_drupal\Unit; + +use Drupal\Core\Logger\LoggerChannelInterface; +use Drupal\migrate\Plugin\MigrationInterface; +use Drupal\migrate\Plugin\MigrationPluginManagerInterface; +use Drupal\field_discovery_test\FieldDiscoveryTestClass; +use Drupal\migrate_drupal\FieldDiscoveryInterface; +use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface; +use Drupal\Tests\UnitTestCase; + +/** + * Tests the FieldDiscovery Class. + * + * @group migrate_drupal + * @coversDefaultClass \Drupal\migrate_drupal\FieldDiscovery + */ +class FieldDiscoveryTest extends UnitTestCase { + + /** + * A MigrateFieldPluginManager prophecy. + * + * @var \Prophecy\Prophecy\ObjectProphecy + */ + protected $fieldPluginManager; + + /** + * A MigrationPluginManager prophecy. + * + * @var \Prophecy\Prophecy\ObjectProphecy + */ + protected $migrationPluginManager; + + /** + * A LoggerChannelInterface prophecy. + * + * @var \Prophecy\Prophecy\ObjectProphecy + */ + protected $logger; + + /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp(); + $this->fieldPluginManager = $this->prophesize(MigrateFieldPluginManagerInterface::class); + $this->migrationPluginManager = $this->prophesize(MigrationPluginManagerInterface::class); + $this->logger = $this->prophesize(LoggerChannelInterface::class); + } + + /** + * Tests the protected getEntityFields method. + * + * @param string $entity_type_id + * The entity type ID. + * @param array $expected_fields + * The expected fields. + * + * @covers ::getEntityFields + * @dataProvider getEntityFieldsData + */ + public function testGetEntityFields($entity_type_id, array $expected_fields) { + $test_data = [ + 'getAllFields' => [ + '7' => $this->getAllFieldData(), + ], + ]; + $field_discovery = new FieldDiscoveryTestClass($this->fieldPluginManager->reveal(), $this->migrationPluginManager->reveal(), $this->logger->reveal(), $test_data); + $actual_fields = $field_discovery->getEntityFields('7', $entity_type_id); + $this->assertSame($expected_fields, $actual_fields); + } + + /** + * Provides data for testGetEntityFields. + * + * @return array + * The data. + */ + public function getEntityFieldsData() { + return [ + 'Node' => [ + 'entity_type_id' => 'node', + 'expected_fields' => [ + 'content_type_1' => [ + 'field_1' => ['field_info_key' => 'field_1_data'], + 'field_2' => ['field_info_key' => 'field_2_data'], + 'field_3' => ['field_info_key' => 'field_3_data'], + ], + 'content_type_2' => [ + 'field_1' => ['field_info_key' => 'field_1_data'], + 'field_4' => ['field_info_key' => 'field_4_data'], + 'field_5' => ['field_info_key' => 'field_5_data'], + ], + ], + ], + 'User' => [ + 'entity_type_id' => 'user', + 'expected_fields' => [ + 'user' => [ + 'user_field_1' => ['field_info_key' => 'user_field_1_data'], + ], + ], + ], + 'Comment' => [ + 'entity_type_id' => 'comment', + 'expected_fields' => [ + 'comment_node_content_type_1' => [ + 'cfield_1' => ['field_info_key' => 'field_1_data'], + 'cfield_2' => ['field_info_key' => 'field_2_data'], + 'cfield_3' => ['field_info_key' => 'field_3_data'], + ], + 'comment_node_content_type_2' => [ + 'cfield_1' => ['field_info_key' => 'field_1_data'], + 'cfield_4' => ['field_info_key' => 'field_4_data'], + 'cfield_5' => ['field_info_key' => 'field_5_data'], + ], + ], + ], + 'Non-existent Entity' => [ + 'entity_type_id' => 'custom_entity', + 'expected_fields' => [], + ], + ]; + } + + /** + * Tests the protected getEntityFields method. + * + * @param string $entity_type_id + * The entity type ID. + * @param string $bundle + * The bundle. + * @param array $expected_fields + * The expected fields. + * + * @covers ::getBundleFields + * @dataProvider getBundleFieldsData + */ + public function testGetBundleFields($entity_type_id, $bundle, array $expected_fields) { + $test_data = [ + 'getAllFields' => [ + '7' => $this->getAllFieldData(), + ], + ]; + $field_discovery = new FieldDiscoveryTestClass($this->fieldPluginManager->reveal(), $this->migrationPluginManager->reveal(), $this->logger->reveal(), $test_data); + $actual_fields = $field_discovery->getBundleFields('7', $entity_type_id, $bundle); + $this->assertSame($expected_fields, $actual_fields); + } + + /** + * Provides data for testGetBundleFields. + * + * @return array + * The data. + */ + public function getBundleFieldsData() { + return [ + 'Node - Content Type 1' => [ + 'entity_type_id' => 'node', + 'bundle' => 'content_type_1', + 'expected_fields' => [ + 'field_1' => ['field_info_key' => 'field_1_data'], + 'field_2' => ['field_info_key' => 'field_2_data'], + 'field_3' => ['field_info_key' => 'field_3_data'], + ], + ], + 'Node - Content Type 2' => [ + 'entity_type_id' => 'node', + 'bundle' => 'content_type_2', + 'expected_fields' => [ + 'field_1' => ['field_info_key' => 'field_1_data'], + 'field_4' => ['field_info_key' => 'field_4_data'], + 'field_5' => ['field_info_key' => 'field_5_data'], + ], + ], + 'User' => [ + 'entity_type_id' => 'user', + 'bundle' => 'user', + 'expected_fields' => [ + 'user_field_1' => ['field_info_key' => 'user_field_1_data'], + ], + ], + 'Comment - Content Type 1' => [ + 'entity_type_id' => 'comment', + 'bundle' => 'comment_node_content_type_1', + 'expected_fields' => [ + 'cfield_1' => ['field_info_key' => 'field_1_data'], + 'cfield_2' => ['field_info_key' => 'field_2_data'], + 'cfield_3' => ['field_info_key' => 'field_3_data'], + ], + ], + 'Comment - Content Type 2' => [ + 'entity_type_id' => 'comment', + 'bundle' => 'comment_node_content_type_2', + 'expected_fields' => [ + 'cfield_1' => ['field_info_key' => 'field_1_data'], + 'cfield_4' => ['field_info_key' => 'field_4_data'], + 'cfield_5' => ['field_info_key' => 'field_5_data'], + ], + ], + 'Non-existent Entity Type' => [ + 'entity_type_id' => 'custom_entity', + 'bundle' => 'content_type_1', + 'expected_fields' => [], + ], + 'Non-existent Bundle' => [ + 'entity_type_id' => 'node', + 'bundle' => 'content_type_3', + 'expected_fields' => [], + ], + ]; + } + + /** + * Test the protected getCoreVersion method. + * + * @param string[] $tags + * The migration tags. + * @param string|bool $expected_result + * The expected return value of the method. + * + * @covers ::getCoreVersion + * @dataProvider getCoreVersionData + */ + public function testGetCoreVersion(array $tags, $expected_result) { + $migration = $this->prophesize(MigrationInterface::class); + $migration->getMigrationTags()->willReturn($tags); + $field_discovery = new FieldDiscoveryTestClass($this->fieldPluginManager->reveal(), $this->migrationPluginManager->reveal(), $this->logger->reveal()); + if (!$expected_result) { + $this->setExpectedException(\InvalidArgumentException::class); + } + $actual_result = $field_discovery->getCoreVersion($migration->reveal()); + $this->assertEquals($expected_result, $actual_result); + } + + /** + * Provides data for testGetCoreVersion() + * + * @return array + * The test data. + */ + public function getCoreVersionData() { + return [ + 'Drupal 7' => [ + 'tags' => ['Drupal 7'], + 'result' => '7', + ], + 'Drupal 6' => [ + 'tags' => ['Drupal 6'], + 'result' => '6', + ], + 'D7 with others' => [ + 'tags' => ['Drupal 7', 'Translation', 'Other Tag'], + 'result' => '7', + ], + 'Both (d7 has priority)' => [ + 'tags' => ['Drupal 6', 'Drupal 7'], + 'result' => '7', + ], + 'Neither' => [ + 'tags' => ['drupal 6', 'Drupal_6', 'This contains Drupal 7 but is not'], + 'result' => FALSE, + ], + ]; + } + + /** + * Returns dummy data to test the field getters. + */ + protected function getAllFieldData() { + return [ + 'node' => [ + 'content_type_1' => [ + 'field_1' => ['field_info_key' => 'field_1_data'], + 'field_2' => ['field_info_key' => 'field_2_data'], + 'field_3' => ['field_info_key' => 'field_3_data'], + ], + 'content_type_2' => [ + 'field_1' => ['field_info_key' => 'field_1_data'], + 'field_4' => ['field_info_key' => 'field_4_data'], + 'field_5' => ['field_info_key' => 'field_5_data'], + ], + ], + 'user' => [ + 'user' => [ + 'user_field_1' => ['field_info_key' => 'user_field_1_data'], + ], + ], + 'comment' => [ + 'comment_node_content_type_1' => [ + 'cfield_1' => ['field_info_key' => 'field_1_data'], + 'cfield_2' => ['field_info_key' => 'field_2_data'], + 'cfield_3' => ['field_info_key' => 'field_3_data'], + ], + 'comment_node_content_type_2' => [ + 'cfield_1' => ['field_info_key' => 'field_1_data'], + 'cfield_4' => ['field_info_key' => 'field_4_data'], + 'cfield_5' => ['field_info_key' => 'field_5_data'], + ], + ], + ]; + } + + /** + * Tests the getFieldInstanceStubMigration method. + * + * @param mixed $core + * The Drupal core version. + * @param array|bool $expected_definition + * The expected migration definition, or false if an exception is expected. + * + * @covers ::getFieldInstanceStubMigrationDefinition + * @dataProvider getFieldInstanceStubMigrationDefinition + */ + public function testGetFieldInstanceStubMigrationDefinition($core, $expected_definition) { + $field_discovery = new FieldDiscoveryTestClass($this->fieldPluginManager->reveal(), $this->migrationPluginManager->reveal(), $this->logger->reveal()); + if (!$expected_definition) { + $this->setExpectedException(\InvalidArgumentException::class, sprintf("Drupal version %s is not supported. Valid values for Drupal core version are '6' and '7'.", $core)); + } + $actual_definition = $field_discovery->getFieldInstanceStubMigrationDefinition($core); + $this->assertSame($expected_definition, $actual_definition); + } + + /** + * Provides data for testGetFieldInstanceStubMigrationDefinition. + * + * @return array + * The data. + */ + public function getFieldInstanceStubMigrationDefinition() { + return [ + 'Drupal 6' => [ + 'core' => FieldDiscoveryInterface::DRUPAL_6, + 'expected_definition' => [ + 'destination' => ['plugin' => 'null'], + 'idMap' => ['plugin' => 'null'], + 'source' => [ + 'ignore_map' => TRUE, + 'plugin' => 'd6_field_instance', + ], + ], + ], + 'Drupal 7' => [ + 'core' => FieldDiscoveryInterface::DRUPAL_7, + 'expected_definition' => [ + 'destination' => ['plugin' => 'null'], + 'idMap' => ['plugin' => 'null'], + 'source' => [ + 'ignore_map' => TRUE, + 'plugin' => 'd7_field_instance', + ], + ], + ], + ]; + } + +} diff --git a/core/modules/node/src/Plugin/migrate/D6NodeDeriver.php b/core/modules/node/src/Plugin/migrate/D6NodeDeriver.php index 94c759bea45b..a8fa1d0bbe1b 100644 --- a/core/modules/node/src/Plugin/migrate/D6NodeDeriver.php +++ b/core/modules/node/src/Plugin/migrate/D6NodeDeriver.php @@ -3,13 +3,11 @@ namespace Drupal\node\Plugin\migrate; use Drupal\Component\Plugin\Derivative\DeriverBase; -use Drupal\Component\Plugin\Exception\PluginNotFoundException; use Drupal\Core\Database\DatabaseExceptionWrapper; use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface; use Drupal\migrate\Exception\RequirementsException; use Drupal\migrate\Plugin\MigrationDeriverTrait; -use Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface; -use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface; +use Drupal\migrate_drupal\FieldDiscoveryInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -26,57 +24,33 @@ class D6NodeDeriver extends DeriverBase implements ContainerDeriverInterface { protected $basePluginId; /** - * Already-instantiated cckfield plugins, keyed by ID. - * - * @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldInterface[] - */ - protected $cckPluginCache; - - /** - * The CCK plugin manager. - * - * @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface - */ - protected $cckPluginManager; - - /** - * Already-instantiated field plugins, keyed by ID. - * - * @var \Drupal\migrate_drupal\Plugin\MigrateFieldInterface[] - */ - protected $fieldPluginCache; - - /** - * The field plugin manager. + * Whether or not to include translations. * - * @var \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface + * @var bool */ - protected $fieldPluginManager; + protected $includeTranslations; /** - * Whether or not to include translations. + * The migration field discovery service. * - * @var bool + * @var \Drupal\migrate_drupal\FieldDiscoveryInterface */ - protected $includeTranslations; + protected $fieldDiscovery; /** * D6NodeDeriver constructor. * * @param string $base_plugin_id * The base plugin ID for the plugin ID. - * @param \Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface $cck_manager - * The CCK plugin manager. - * @param \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface $field_manager - * The field plugin manager. * @param bool $translations * Whether or not to include translations. + * @param \Drupal\migrate_drupal\FieldDiscoveryInterface $field_discovery + * The migration field discovery service. */ - public function __construct($base_plugin_id, MigrateCckFieldPluginManagerInterface $cck_manager, MigrateFieldPluginManagerInterface $field_manager, $translations) { + public function __construct($base_plugin_id, $translations, FieldDiscoveryInterface $field_discovery) { $this->basePluginId = $base_plugin_id; - $this->cckPluginManager = $cck_manager; - $this->fieldPluginManager = $field_manager; $this->includeTranslations = $translations; + $this->fieldDiscovery = $field_discovery; } /** @@ -86,22 +60,13 @@ public static function create(ContainerInterface $container, $base_plugin_id) { // Translations don't make sense unless we have content_translation. return new static( $base_plugin_id, - $container->get('plugin.manager.migrate.cckfield'), - $container->get('plugin.manager.migrate.field'), - $container->get('module_handler')->moduleExists('content_translation') + $container->get('module_handler')->moduleExists('content_translation'), + $container->get('migrate_drupal.field_discovery') ); } /** - * Gets the definition of all derivatives of a base plugin. - * - * @param array $base_plugin_definition - * The definition array of the base plugin. - * - * @return array - * An array of full derivative definitions keyed on derivative id. - * - * @see \Drupal\Component\Plugin\Derivative\DeriverBase::getDerivativeDefinition() + * {@inheritdoc} */ public function getDerivativeDefinitions($base_plugin_definition) { if ($base_plugin_definition['id'] == 'd6_node_translation' && !$this->includeTranslations) { @@ -119,22 +84,6 @@ public function getDerivativeDefinitions($base_plugin_definition) { return $this->derivatives; } - // Read all field instance definitions in the source database. - $fields = []; - try { - $source_plugin = static::getSourcePlugin('d6_field_instance'); - $source_plugin->checkRequirements(); - - foreach ($source_plugin as $row) { - $fields[$row->getSourceProperty('type_name')][$row->getSourceProperty('field_name')] = $row->getSource(); - } - } - catch (RequirementsException $e) { - // If checkRequirements() failed then the content module did not exist and - // we do not have any fields. Therefore, $fields will be empty and - // below we'll create a migration just for the node properties. - } - try { foreach ($node_types as $row) { $node_type = $row->getSourceProperty('type'); @@ -156,32 +105,7 @@ public function getDerivativeDefinitions($base_plugin_definition) { /** @var \Drupal\migrate\Plugin\Migration $migration */ $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($values); - if (isset($fields[$node_type])) { - foreach ($fields[$node_type] as $field_name => $info) { - $field_type = $info['type']; - try { - $plugin_id = $this->fieldPluginManager->getPluginIdFromFieldType($field_type, ['core' => 6], $migration); - if (!isset($this->fieldPluginCache[$field_type])) { - $this->fieldPluginCache[$field_type] = $this->fieldPluginManager->createInstance($plugin_id, ['core' => 6], $migration); - } - $this->fieldPluginCache[$field_type] - ->defineValueProcessPipeline($migration, $field_name, $info); - } - catch (PluginNotFoundException $ex) { - try { - $plugin_id = $this->cckPluginManager->getPluginIdFromFieldType($field_type, ['core' => 6], $migration); - if (!isset($this->cckPluginCache[$field_type])) { - $this->cckPluginCache[$field_type] = $this->cckPluginManager->createInstance($plugin_id, ['core' => 6], $migration); - } - $this->cckPluginCache[$field_type] - ->processCckFieldValues($migration, $field_name, $info); - } - catch (PluginNotFoundException $ex) { - $migration->setProcessOfProperty($field_name, $field_name); - } - } - } - } + $this->fieldDiscovery->addBundleFieldProcesses($migration, 'node', $node_type); $this->derivatives[$node_type] = $migration->getPluginDefinition(); } } diff --git a/core/modules/node/src/Plugin/migrate/D7NodeDeriver.php b/core/modules/node/src/Plugin/migrate/D7NodeDeriver.php index 25099cf178db..380aab81e4ef 100644 --- a/core/modules/node/src/Plugin/migrate/D7NodeDeriver.php +++ b/core/modules/node/src/Plugin/migrate/D7NodeDeriver.php @@ -3,13 +3,11 @@ namespace Drupal\node\Plugin\migrate; use Drupal\Component\Plugin\Derivative\DeriverBase; -use Drupal\Component\Plugin\Exception\PluginNotFoundException; use Drupal\Core\Database\DatabaseExceptionWrapper; use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface; use Drupal\migrate\Exception\RequirementsException; use Drupal\migrate\Plugin\MigrationDeriverTrait; -use Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface; -use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface; +use Drupal\migrate_drupal\FieldDiscoveryInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -26,57 +24,33 @@ class D7NodeDeriver extends DeriverBase implements ContainerDeriverInterface { protected $basePluginId; /** - * Already-instantiated cckfield plugins, keyed by ID. - * - * @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldInterface[] - */ - protected $cckPluginCache; - - /** - * The CCK plugin manager. - * - * @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface - */ - protected $cckPluginManager; - - /** - * Already-instantiated field plugins, keyed by ID. - * - * @var \Drupal\migrate_drupal\Plugin\MigrateFieldInterface[] - */ - protected $fieldPluginCache; - - /** - * The field plugin manager. + * Whether or not to include translations. * - * @var \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface + * @var bool */ - protected $fieldPluginManager; + protected $includeTranslations; /** - * Whether or not to include translations. + * The migration field discovery service. * - * @var bool + * @var \Drupal\migrate_drupal\FieldDiscoveryInterface */ - protected $includeTranslations; + protected $fieldDiscovery; /** * D7NodeDeriver constructor. * * @param string $base_plugin_id * The base plugin ID for the plugin ID. - * @param \Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface $cck_manager - * The CCK plugin manager. - * @param \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface $field_manager - * The field plugin manager. * @param bool $translations * Whether or not to include translations. + * @param \Drupal\migrate_drupal\FieldDiscoveryInterface $field_discovery + * The migration field discovery service. */ - public function __construct($base_plugin_id, MigrateCckFieldPluginManagerInterface $cck_manager, MigrateFieldPluginManagerInterface $field_manager, $translations) { + public function __construct($base_plugin_id, $translations, FieldDiscoveryInterface $field_discovery) { $this->basePluginId = $base_plugin_id; - $this->cckPluginManager = $cck_manager; - $this->fieldPluginManager = $field_manager; $this->includeTranslations = $translations; + $this->fieldDiscovery = $field_discovery; } /** @@ -86,9 +60,8 @@ public static function create(ContainerInterface $container, $base_plugin_id) { // Translations don't make sense unless we have content_translation. return new static( $base_plugin_id, - $container->get('plugin.manager.migrate.cckfield'), - $container->get('plugin.manager.migrate.field'), - $container->get('module_handler')->moduleExists('content_translation') + $container->get('module_handler')->moduleExists('content_translation'), + $container->get('migrate_drupal.field_discovery') ); } @@ -111,24 +84,6 @@ public function getDerivativeDefinitions($base_plugin_definition) { return $this->derivatives; } - $fields = []; - try { - $source_plugin = static::getSourcePlugin('d7_field_instance'); - $source_plugin->checkRequirements(); - - // Read all field instance definitions in the source database. - foreach ($source_plugin as $row) { - if ($row->getSourceProperty('entity_type') == 'node') { - $fields[$row->getSourceProperty('bundle')][$row->getSourceProperty('field_name')] = $row->getSource(); - } - } - } - catch (RequirementsException $e) { - // If checkRequirements() failed then the field module did not exist and - // we do not have any fields. Therefore, $fields will be empty and below - // we'll create a migration just for the node properties. - } - try { foreach ($node_types as $row) { $node_type = $row->getSourceProperty('type'); @@ -158,33 +113,9 @@ public function getDerivativeDefinitions($base_plugin_definition) { $values['migration_dependencies']['required'][] = 'd7_node:' . $node_type; } + /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */ $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($values); - if (isset($fields[$node_type])) { - foreach ($fields[$node_type] as $field_name => $info) { - $field_type = $info['type']; - try { - $plugin_id = $this->fieldPluginManager->getPluginIdFromFieldType($field_type, ['core' => 7], $migration); - if (!isset($this->fieldPluginCache[$field_type])) { - $this->fieldPluginCache[$field_type] = $this->fieldPluginManager->createInstance($plugin_id, ['core' => 7], $migration); - } - $this->fieldPluginCache[$field_type] - ->defineValueProcessPipeline($migration, $field_name, $info); - } - catch (PluginNotFoundException $ex) { - try { - $plugin_id = $this->cckPluginManager->getPluginIdFromFieldType($field_type, ['core' => 7], $migration); - if (!isset($this->cckPluginCache[$field_type])) { - $this->cckPluginCache[$field_type] = $this->cckPluginManager->createInstance($plugin_id, ['core' => 7], $migration); - } - $this->cckPluginCache[$field_type] - ->processCckFieldValues($migration, $field_name, $info); - } - catch (PluginNotFoundException $ex) { - $migration->setProcessOfProperty($field_name, $field_name); - } - } - } - } + $this->fieldDiscovery->addBundleFieldProcesses($migration, 'node', $node_type); $this->derivatives[$node_type] = $migration->getPluginDefinition(); } } diff --git a/core/modules/taxonomy/src/Plugin/migrate/D7TaxonomyTermDeriver.php b/core/modules/taxonomy/src/Plugin/migrate/D7TaxonomyTermDeriver.php index 566cf3b3da22..c116f813ede1 100644 --- a/core/modules/taxonomy/src/Plugin/migrate/D7TaxonomyTermDeriver.php +++ b/core/modules/taxonomy/src/Plugin/migrate/D7TaxonomyTermDeriver.php @@ -3,20 +3,17 @@ namespace Drupal\taxonomy\Plugin\migrate; use Drupal\Component\Plugin\Derivative\DeriverBase; -use Drupal\Component\Plugin\Exception\PluginNotFoundException; use Drupal\Core\Database\DatabaseExceptionWrapper; use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface; use Drupal\migrate\Exception\RequirementsException; use Drupal\migrate\Plugin\MigrationDeriverTrait; -use Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface; -use Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface; +use Drupal\migrate_drupal\FieldDiscoveryInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Deriver for Drupal 7 taxonomy term migrations based on vocabularies. */ class D7TaxonomyTermDeriver extends DeriverBase implements ContainerDeriverInterface { - use MigrationDeriverTrait; /** @@ -27,47 +24,23 @@ class D7TaxonomyTermDeriver extends DeriverBase implements ContainerDeriverInter protected $basePluginId; /** - * Already-instantiated cckfield plugins, keyed by ID. - * - * @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldInterface[] - */ - protected $cckPluginCache; - - /** - * The CCK plugin manager. + * The migration field discovery service. * - * @var \Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface + * @var \Drupal\migrate_drupal\FieldDiscoveryInterface */ - protected $cckPluginManager; - - /** - * Already-instantiated field plugins, keyed by ID. - * - * @var \Drupal\migrate_drupal\Plugin\MigrateFieldInterface[] - */ - protected $fieldPluginCache; - - /** - * The field plugin manager. - * - * @var \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface - */ - protected $fieldPluginManager; + protected $fieldDiscovery; /** * D7TaxonomyTermDeriver constructor. * * @param string $base_plugin_id * The base plugin ID for the plugin ID. - * @param \Drupal\migrate_drupal\Plugin\MigrateCckFieldPluginManagerInterface $cck_manager - * The CCK plugin manager. - * @param \Drupal\migrate_drupal\Plugin\MigrateFieldPluginManagerInterface $field_manager - * The field plugin manager. + * @param \Drupal\migrate_drupal\FieldDiscoveryInterface $field_discovery + * The migration field discovery service. */ - public function __construct($base_plugin_id, MigrateCckFieldPluginManagerInterface $cck_manager, MigrateFieldPluginManagerInterface $field_manager) { + public function __construct($base_plugin_id, FieldDiscoveryInterface $field_discovery) { $this->basePluginId = $base_plugin_id; - $this->cckPluginManager = $cck_manager; - $this->fieldPluginManager = $field_manager; + $this->fieldDiscovery = $field_discovery; } /** @@ -76,8 +49,7 @@ public function __construct($base_plugin_id, MigrateCckFieldPluginManagerInterfa public static function create(ContainerInterface $container, $base_plugin_id) { return new static( $base_plugin_id, - $container->get('plugin.manager.migrate.cckfield'), - $container->get('plugin.manager.migrate.field') + $container->get('migrate_drupal.field_discovery') ); } @@ -85,23 +57,6 @@ public static function create(ContainerInterface $container, $base_plugin_id) { * {@inheritdoc} */ public function getDerivativeDefinitions($base_plugin_definition) { - $fields = []; - try { - $source_plugin = static::getSourcePlugin('d7_field_instance'); - $source_plugin->checkRequirements(); - - // Read all field instance definitions in the source database. - foreach ($source_plugin as $row) { - if ($row->getSourceProperty('entity_type') == 'taxonomy_term') { - $fields[$row->getSourceProperty('bundle')][$row->getSourceProperty('field_name')] = $row->getSource(); - } - } - } - catch (RequirementsException $e) { - // If checkRequirements() failed then the field module did not exist and - // we do not have any fields. Therefore, $fields will be empty and below - // we'll create a migration just for the node properties. - } $vocabulary_source_plugin = static::getSourcePlugin('d7_taxonomy_vocabulary'); try { @@ -126,34 +81,9 @@ public function getDerivativeDefinitions($base_plugin_definition) { $values['source']['bundle'] = $bundle; $values['destination']['default_bundle'] = $bundle; - /** @var Migration $migration */ + /** @var \Drupal\migrate\Plugin\MigrationInterface $migration */ $migration = \Drupal::service('plugin.manager.migration')->createStubMigration($values); - if (isset($fields[$bundle])) { - foreach ($fields[$bundle] as $field_name => $info) { - $field_type = $info['type']; - try { - $plugin_id = $this->fieldPluginManager->getPluginIdFromFieldType($field_type, ['core' => 7], $migration); - if (!isset($this->fieldPluginCache[$field_type])) { - $this->fieldPluginCache[$field_type] = $this->fieldPluginManager->createInstance($plugin_id, ['core' => 7], $migration); - } - $this->fieldPluginCache[$field_type] - ->defineValueProcessPipeline($migration, $field_name, $info); - } - catch (PluginNotFoundException $ex) { - try { - $plugin_id = $this->cckPluginManager->getPluginIdFromFieldType($field_type, ['core' => 7], $migration); - if (!isset($this->cckPluginCache[$field_type])) { - $this->cckPluginCache[$field_type] = $this->cckPluginManager->createInstance($plugin_id, ['core' => 7], $migration); - } - $this->cckPluginCache[$field_type] - ->processCckFieldValues($migration, $field_name, $info); - } - catch (PluginNotFoundException $ex) { - $migration->setProcessOfProperty($field_name, $field_name); - } - } - } - } + $this->fieldDiscovery->addBundleFieldProcesses($migration, 'taxonomy_term', $bundle); $this->derivatives[$bundle] = $migration->getPluginDefinition(); } } diff --git a/core/modules/user/src/Plugin/migrate/User.php b/core/modules/user/src/Plugin/migrate/User.php index 4cf8f1491924..356c4e3b4431 100644 --- a/core/modules/user/src/Plugin/migrate/User.php +++ b/core/modules/user/src/Plugin/migrate/User.php @@ -16,47 +16,21 @@ class User extends FieldMigration { public function getProcess() { if (!$this->init) { $this->init = TRUE; - $definition['source'] = [ - 'entity_type' => 'user', - 'ignore_map' => TRUE, - ] + $this->source; - $definition['destination']['plugin'] = 'null'; - $definition['idMap']['plugin'] = 'null'; - if (\Drupal::moduleHandler()->moduleExists('field')) { - $definition['source']['plugin'] = 'd7_field_instance'; - $field_migration = $this->migrationPluginManager->createStubMigration($definition); - foreach ($field_migration->getSourcePlugin() as $row) { - $field_name = $row->getSourceProperty('field_name'); - $field_type = $row->getSourceProperty('type'); - if (empty($field_type)) { - continue; - } - if ($this->fieldPluginManager->hasDefinition($field_type)) { - if (!isset($this->fieldPluginCache[$field_type])) { - $plugin_id = $this->fieldPluginManager->getPluginIdFromFieldType($field_type, [], $this); - $this->fieldPluginCache[$field_type] = $this->fieldPluginManager->createInstance($plugin_id, [], $this); - } - $info = $row->getSource(); - $this->fieldPluginCache[$field_type] - ->defineValueProcessPipeline($this, $field_name, $info); - } - else { - if ($this->cckPluginManager->hasDefinition($field_type)) { - if (!isset($this->cckPluginCache[$field_type])) { - $this->cckPluginCache[$field_type] = $this->cckPluginManager->createInstance($field_type, [], $this); - } - $info = $row->getSource(); - $this->cckPluginCache[$field_type] - ->processCckFieldValues($this, $field_name, $info); - } - else { - $this->process[$field_name] = $field_name; - } - } - } - } + $this->fieldDiscovery->addEntityFieldProcesses($this, 'user'); + + $definition = [ + 'source' => [ + 'plugin' => 'profile_field', + 'ignore_map' => TRUE, + ], + 'idMap' => [ + 'plugin' => 'null', + ], + 'destination' => [ + 'plugin' => 'null', + ], + ]; try { - $definition['source']['plugin'] = 'profile_field'; $profile_migration = $this->migrationPluginManager->createStubMigration($definition); // Ensure that Profile is enabled in the source DB. $profile_migration->checkRequirements(); -- GitLab