'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 \Psr\Log\LoggerInterface $logger * The logger channel service. */ public function __construct(MigrateFieldPluginManagerInterface $field_plugin_manager, MigrationPluginManagerInterface $migration_plugin_manager, LoggerInterface $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 = $plugin_definition['field_plugin_method'] ?? 'defineValueProcessPipeline'; 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')) { $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\MigrateFieldInterface|bool * The appropriate field 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) { $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 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., 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 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"); } }