diff --git a/core/modules/migrate/src/Plugin/migrate/process/MakeUniqueFieldName.php b/core/modules/migrate/src/Plugin/migrate/process/MakeUniqueFieldName.php new file mode 100644 index 0000000000..eddf9d8e3a --- /dev/null +++ b/core/modules/migrate/src/Plugin/migrate/process/MakeUniqueFieldName.php @@ -0,0 +1,135 @@ +configuration = $configuration; + $this->pluginId = $plugin_id; + $this->pluginDefinition = $plugin_definition; + $this->entityTypeManager = $entity_type_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('entity_type.manager') + ); + } + + /** + * Creates a unique name for this field for the given entity type. + * + * @param string $value + * The input string. + * @param \Drupal\migrate\MigrateExecutableInterface $migrate_executable + * The migration in which this process is being executed. + * @param \Drupal\migrate\Row $row + * The row from the source to process. + * @param string $destination_property + * The destination property currently worked on. This is only used together + * with the $row above. + * + * @return string + * The unique version of the input value. + * + * @throws \Drupal\migrate\MigrateException + */ + public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) { + $i = 1; + $entity_type = isset($this->configuration['entity_type']) ? $this->configuration['entity_type'] : NULL; + $length = isset($this->configuration['length']) ? $this->configuration['length'] : NULL; + if (!is_null($length) && !is_int($length)) { + throw new MigrateException('The character length configuration key should be an integer. Omit this key to allow arbitrary length.'); + } + if (!is_string($entity_type)) { + throw new MigrateException('The entity type configuration key is required and should be a string.'); + } + // Start out by trimming the field name to the maximum allowable length. + $value = Unicode::substr($value, 0, $length); + $new_value = $value; + while ($this->exists($new_value, $entity_type)) { + // If adding a number to the end to try to obtain uniqueness, ensure we + // don't exceed our max length. + $new_value = Unicode::substr($value, 0, $length - strlen($i)) . $i; + $i++; + } + return $new_value; + } + + /** + * Checks if a field name exists on an entity type. + * + * @param string $field_name + * The field name to check. + * @param string $entity_type + * The entity type to check it against. + * + * @return bool + * TRUE if the field name is already in use on the entity type. + */ + protected function exists($field_name, $entity_type) { + $info = $this->entityTypeManager + ->getStorage('field_storage_config') + ->load($entity_type . '.' . $field_name); + if (isset($info)) { + return TRUE; + } + return FALSE; + } +} diff --git a/core/modules/user/migrations/d6_profile_values.yml b/core/modules/user/migrations/d6_profile_values.yml index 5530ca5b45..77d979d3c4 100644 --- a/core/modules/user/migrations/d6_profile_values.yml +++ b/core/modules/user/migrations/d6_profile_values.yml @@ -5,6 +5,7 @@ migration_tags: - Drupal 6 source: plugin: d6_profile_field_values + migration: user_profile_field process: uid: uid destination: diff --git a/core/modules/user/migrations/user_profile_field.yml b/core/modules/user/migrations/user_profile_field.yml index 3ba0eee48c..4e4a517c01 100644 --- a/core/modules/user/migrations/user_profile_field.yml +++ b/core/modules/user/migrations/user_profile_field.yml @@ -9,7 +9,13 @@ source: entity_type: user process: entity_type: 'constants/entity_type' - field_name: name + field_name: + - + plugin: machine_name + source: name + - + plugin: make_unique_field_name + entity_type: user type: plugin: static_map source: type diff --git a/core/modules/user/migrations/user_profile_field_instance.yml b/core/modules/user/migrations/user_profile_field_instance.yml index b9f213b392..bea3f81fce 100644 --- a/core/modules/user/migrations/user_profile_field_instance.yml +++ b/core/modules/user/migrations/user_profile_field_instance.yml @@ -13,7 +13,15 @@ process: bundle: 'constants/bundle' label: title description: explanation - field_name: name + field_name: + - + plugin: migration_lookup + migration: user_profile_field + source: fid + - + plugin: extract + index: + - 1 required: required destination: plugin: entity:field_config diff --git a/core/modules/user/src/Plugin/migrate/source/d6/ProfileFieldValues.php b/core/modules/user/src/Plugin/migrate/source/d6/ProfileFieldValues.php index ac4929092d..0502daac89 100644 --- a/core/modules/user/src/Plugin/migrate/source/d6/ProfileFieldValues.php +++ b/core/modules/user/src/Plugin/migrate/source/d6/ProfileFieldValues.php @@ -2,18 +2,88 @@ namespace Drupal\user\Plugin\migrate\source\d6; +use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Plugin\ContainerFactoryPluginInterface; +use Drupal\Core\State\StateInterface; +use Drupal\migrate\Plugin\MigrationInterface; +use Drupal\migrate\Plugin\MigrationPluginManagerInterface; use Drupal\migrate\Row; use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Drupal 6 profile fields values source. * + * Available configuration keys: + * - migration: A single migration ID, or an array of migration IDs. + * + * Examples: + * + * This example forces a lookup of profile field names in the migration map + * table in case the profile_field migration needed to change the field + * name due to a name collision or an field name that contains characters + * no longer allowed by Drupal. + * + * @code + * source: + * plugin: d6_profile_field_values + * migration: profile_field + * @endcode + * * @MigrateSource( * id = "d6_profile_field_values", * source_module = "profile" * ) */ -class ProfileFieldValues extends DrupalSqlBase { +class ProfileFieldValues extends DrupalSqlBase implements ContainerFactoryPluginInterface { + + /** + * The migration plugin manager. + * + * @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface + */ + protected $migrationPluginManager; + + /** + * Initialize method. + * + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin ID. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param \Drupal\migrate\Plugin\MigrationInterface $migration + * The migration plugin instance. + * @param \Drupal\Core\State\StateInterface $state + * The state service. + * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager + * The entity manager. + * @param \Durpal\migrate\Plugin\MigrationPluginManagerInterface $migration_plugin_manager + * The migration plugin manager. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, StateInterface $state, EntityManagerInterface $entity_manager, MigrationPluginManagerInterface $migration_plugin_manager) { + $configuration += [ + 'migration' => FALSE, + ]; + parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $state, $entity_manager); + $this->migrationPluginManager = $migration_plugin_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $migration, + $container->get('state'), + $container->get('entity.manager'), + $container->get('plugin.manager.migration') + ); + } /** * {@inheritdoc} @@ -39,18 +109,27 @@ public function prepareRow(Row $row) { $results = $query->execute(); foreach ($results as $profile_value) { + // Find the mapped field name for the profile value. + $name = $this->getProfileFieldName($profile_value['fid']); + if ($name === FALSE) { + $name = $profile_value['name']; + } + // Check special case for date. We need to unserialize. if ($profile_value['type'] == 'date') { $date = unserialize($profile_value['value']); $date = date('Y-m-d', mktime(0, 0, 0, $date['month'], $date['day'], $date['year'])); - $row->setSourceProperty($profile_value['name'], ['value' => $date]); + $row->setSourceProperty($name, ['value' => $date]); + $row->setDestinationProperty($name, ['value' => $date]); } elseif ($profile_value['type'] == 'list') { // Explode by newline and comma. - $row->setSourceProperty($profile_value['name'], preg_split("/[\r\n,]+/", $profile_value['value'])); + $row->setSourceProperty($name, preg_split("/[\r\n,]+/", $profile_value['value'])); + $row->setDestinationProperty($name, preg_split("/[\r\n,]+/", $profile_value['value'])); } else { - $row->setSourceProperty($profile_value['name'], [$profile_value['value']]); + $row->setSourceProperty($name, [$profile_value['value']]); + $row->setDestinationProperty($name, [$profile_value['value']]); } } @@ -91,4 +170,42 @@ public function getIds() { ]; } + /** + * Lookup the profile field name based on migration. + * + * @param string $sourceName + * The raw profile field name from the source data. + * + * @return string + * The mapped profile field name + */ + protected function getProfileFieldName($sourceID) { + $migration_ids = $this->configuration['migration']; + if (!is_array($migration_ids)) { + $migration_ids = [$migration_ids]; + } + $sourceValues = [$sourceID]; + /** @var \Drupal\migrate\Plugin\MigrationInterface[] $migrations */ + $migrations = $this->migrationPluginManager->createInstances($migration_ids); + $destination_ids = NULL; + foreach ($migrations as $migration_id => $migration) { + if ($destination_ids = $migration->getIdMap()->lookupDestinationId($sourceValues)) { + break; + } + } + + if (!$destination_ids) { + // Nothing found. + return FALSE; + } + elseif (count($destination_ids) === 2) { + // Destination IDs for fields are an array of the entity type and field + // name in that order. + return $destination_ids[1]; + } + + // Return the first found destination ID. + return reset($destination_ids); + } + }