Problem/Motivation

Drupal core provides an "underlying API" with field migration plugins to specify a D6 / D7 → D9 formatter and widget plugin ID mapping. The problem is that this feature is accessible only with a field migration plugin (and that is field type specific):

If a module has only formatters or widgets (but these are extending a field type which is not provided by the module), then for these modules, it is nearly impossible to tell Drupal's migration system that a specific Drupal 7 field formatter ID should be mapped to something else for Drupal 9.

For example, the Multiple Selects module would have a trivial migration path, but due to the issue described above, this is currently impossible to automate.

See #3188284: [PP-3] Migration support.

Proposed resolution

Create a new plugin ID mapper process plugin, move the related mapping logic into this plugin, and provide a hook for contribs to specify their plugin ID mapping.

Related 'logic':

  • MigrateFieldInterface::alterFieldInstanceMigration()
  • MigrateFieldInterface::getFieldFormatterType()
  • MigrateFieldInterface::getFieldFormatterMap()
  • MigrateFieldInterface::alterFieldWidgetMigration()
  • MigrateFieldInterface::getFieldWidgetType()
  • MigrateFieldInterface::getFieldWidgetMap()

Remaining tasks

User interface changes

Post-migration, my D7 content type that was migrated will now have the enhanced UX (with the appropriate widgets) that I'm used to as a site owner:

Before
After

API changes

Two new hooks:

  1. hook_field_migration_field_formatter_info()
  2. hook_field_migration_field_widget_info()

Data model changes

Release notes snippet

Comments

huzooka created an issue. See original summary.

huzooka’s picture

Status: Active » Needs work
StatusFileSize
new9.38 KB

With this patch, contribs are able to map D7 widgets and plugins to their D9 equivalents.

For multiple_selects:


/**
 * Implements hook_field_migration_widget_plugin_id_info().
 */
function multiple_selects_field_migration_widget_plugin_id_info() {
  // First-level keys are Drupal 7 field type IDs, second level keys are
  // Drupal 7 widget plugin IDs. The values are Drupal 9 widget plugin IDs.
  return [
    'entityreference' => ['multiple_selects' => 'multiple_options_select'],
    'node_reference' => ['multiple_selects' => 'multiple_options_select'],
    'user_reference' => ['multiple_selects' => 'multiple_options_select'],
    'taxonomy_term_reference' => ['multiple_selects' => 'multiple_options_select'],
    'list_integer' => ['multiple_selects' => 'multiple_options_select'],
    'list_float' => ['multiple_selects' => 'multiple_options_select'],
    'list_text' => ['multiple_selects' => 'multiple_options_select'],
    'list_boolean' => ['multiple_selects' => 'multiple_options_select'],
    'commerce_product_reference' => ['multiple_selects' => 'multiple_options_select'],
  ];
}
wim leers’s picture

#2: is the top-level field type key truly necessary? Why can't we just map widget plugin IDs and formatter IDs?

How does this patch relate to #3108302: [PP-2] Field formatter & widget settings: fall back to default if missing plugin?

huzooka’s picture

Assigned: huzooka » Unassigned
StatusFileSize
new19.44 KB

This patch requires #3204212: Convert remaining widget and formatter type migrations to MigrateField plugins .

I don't have any clue why I had to fix \Drupal\file\Plugin\migrate\process\d6\FieldFile – maybe it just suddenly began to work?..

wim leers’s picture

maybe it just suddenly began to work?..

😅🔥

huzooka’s picture

Issue summary: View changes
Status: Needs work » Needs review
Issue tags: -Needs tests
StatusFileSize
new22.86 KB
new9.27 KB
huzooka’s picture

huzooka’s picture

wim leers’s picture

Title: Provide option for contrib modules to map their D6 / D7 field formatter and widget plugin IDs to the equivalent D9 plugin ID » [PP-1] Provide option for contrib modules to map their D6 / D7 field formatter and widget plugin IDs to the equivalent D9 plugin ID
Related issues: +#3204212: Convert remaining widget and formatter type migrations to MigrateField plugins
wim leers’s picture

Issue summary: View changes

The example in #2 is wrong. It's not hook_field_migration_widget_plugin_id_info(), but hook_field_migration_field_widget_info().

Note that there is zero pre-existing documentation about the field migration infrastructure (\Drupal\migrate_drupal\Plugin\migrate\field\FieldPluginBase() and friends), so it is very unclear where to document this. This explains @huzooka's remaining task in the issue summary:

  • Document the new hooks (where?..).

… so I went ahead and created a change record: https://www.drupal.org/node/3208917

wim leers’s picture

Adding a concrete example of the impact to the issue summary.

wim leers’s picture

  1. +++ b/core/modules/field/migrations/d6_field_formatter_settings.yml
    @@ -5,7 +5,7 @@ migration_tags:
    -field_plugin_method: alterFieldFormatterMigration
    +field_plugin_method: false
    
    +++ b/core/modules/migrate_drupal/src/FieldDiscovery.php
    @@ -145,6 +145,11 @@ public function addBundleFieldProcesses(MigrationInterface $migration, $entity_t
    +        // Allow migration plugins skipping alterations.
    +        if (!$method) {
    +          continue;
    +        }
    

    🤔 AFAICT this is how we retain BC?

  2. +++ b/core/modules/field/src/Plugin/migrate/process/FieldMigrationPluginIdMapper.php
    @@ -0,0 +1,173 @@
    +    if (!$this->migrateFieldPluginManager) {
    +      // Exception or return the value? Or NULL? TBD.
    +      return $value;
    +    }
    

    🤔 When could this ever occur? AFAICT this if-test is safe to remove because the constructor guarantees it exists?

  3. +++ b/core/modules/field/src/Plugin/migrate/process/FieldMigrationPluginIdMapper.php
    @@ -0,0 +1,173 @@
    +    if (!is_array($value) || count($value) !== 2) {
    +      // TBD.
    +      return $value;
    +    }
    

    🤔 When can this occur?

  4. +++ b/core/modules/field/src/Plugin/migrate/process/FieldMigrationPluginIdMapper.php
    @@ -0,0 +1,173 @@
    +    if (!is_string($property) || !array_key_exists($property, self::PROPERTY_MAP)) {
    +      // TBD.
    +      return $value;
    +    }
    

    This would be an invalid use of this migration plugin.

    IMHO this should throw a InvalidArgumentException or a MigrateException.

  5. +++ b/core/modules/field/tests/src/Unit/Plugin/migrate/process/FieldMigrationPluginIdMapperTest.php
    @@ -0,0 +1,332 @@
    +        'Hook return' => [],
    +        'Expected' => 'unmapped_preprocessed_field_formatter_id',
    ...
    +        'Hook return' => [
    +          'foo_source_field_type' => [
    +            'foo_source_field_formatter_id' => 'bar_field_formatter_id',
    +          ],
    +        ],
    +        'Expected' => 'bar_field_formatter_id',
    ...
    +        'Hook return' => [],
    +        'Expected' => 'unmapped_foo_source_field_widget_id',
    ...
    +        'Hook return' => [
    +          'foo_source_field_type' => [
    +            'foo_source_field_widget_id' => 'bar_field_widget_id',
    +          ],
    +        ],
    +        'Expected' => 'bar_field_widget_id',
    

    Superb test coverage! 🤩

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

quietone’s picture

Status: Needs review » Postponed

In #9 it is said that this is postponed, so changing status.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

huzooka’s picture

This patch fixes a very ugly BC problem: MigrateFieldInterface::alterFieldFormatterMigration and MigrateFieldInterface::alterFieldWidgetMigration methods weren't invoked with patch #8.

I've also added test coverage for the above, and addressed the review from #12:

  1. Obsolete.
  2. Since the field migration plugin manager is a Migrate Drupal service, it might be missing if only the Migrate module is enabled. But logically, the migrate process plugin belongs to the field module imho.

    But I missed before that we can specify the provider in the annotation, so since the provider is migrate_drupal:

    • The process plugin wont be discovered if Field module isn't installed
    • If Field is installed, but Migrate Drupal isn't, then its plugin definition will be dropped by \Drupal\migrate\Plugin\Discovery\ProviderFilterDecorator
  3. +++ b/core/modules/field/src/Plugin/migrate/process/FieldMigrationPluginIdMapper.php
    @@ -0,0 +1,173 @@
    +    if (!is_array($value) || count($value) !== 2) {
    +      // TBD.
    +      return $value;
    +    }
    

    🤔 When can this occur?

    E.g if someone tries to use this process plugin by writing their own migration. From now on, this will throw a \LogicException with a meaningful message.

  4. Fixed. From now on we throw a \InvalidArgumentException with a message about the config key's requirements.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

anybody’s picture

#16 LGTM! We're using this in several migration projects and it works great!

As this is a "Contributed project blocker" for many contrib issues (all field formatter and widget upgrades), would it perhaps make sense for @Wim Leers (as of #12) or @quietone to already review this for having this RTBC'd as soon as #3204212: Convert remaining widget and formatter type migrations to MigrateField plugins lands?

I think that would help in many many contrib projects and migration projects.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

anybody’s picture

@Wim Leers: Thanks a lot for #20!
As we experienced different behavior (still investigating) from #16 with 9.4.x vs. #20 with 9.5.1 could you perhaps provide an interdiff? I tried, but it didn't work, sorry. :(

And wouldn't it be more transparent to use a MR here to see the changes directly?

grevil’s picture

Update to #21. Patch #20 works great! We just had to regenerate the migration ymls! 🙂👍

grevil’s picture

Using patch from #20 for 3 site migrations now and found a little flaw with field widget configurations (and probably also with field formatter settings).

For example, I am migrating any "entityreference_view_widget" field widgets to "entity_browser_entity_reference" for every entityreference field using:

    'entityreference' => [
      'entityreference_view_widget' => 'entity_browser_entity_reference',
    ],
    'node_reference' => [
      'entityreference_view_widget' => 'entity_browser_entity_reference',
    ],

This works as expected, BUT I have no way to tell the new field widget it's settings. In my example, trying to edit a content using the mentioned field widget will result in the following error:

TypeError: key(): Argument #1 ($array) must be of type array, null given in key() (line 462 of modules/contrib/entity_browser/src/Plugin/Field/FieldWidget/EntityReferenceBrowserWidget)

This happens, because the field widget has no entity browser selected. I am yet unsure if the hooks implemented through this issue should implement a way to map these settings (or set them). Or if this is the module's fault, as it should implement fallback options.

grevil’s picture

Furthermore, if anyone is using this patch together with Media Migration, make sure you implement the mapping for the field type media migration converts it to.

For example, if we have image fields on a few content types, which are using the deprecated "lightbox2" field formatter, and we want to migrate them to the "image" field formatter, we would usually do the following mapping:

'image' => [
    'image' => [
      'lightbox2' => 'image',
    ],

But because "media_migration" will convert all image fields to media fields before "hook_field_migration_field_formatter_info()" runs, we have to map "media_image" instead:

    'media_image' => [
      'lightbox2' => 'image',
    ],
anybody’s picture

@Win Leers: Re #24 should we perhaps allow to pass settings as additional value?

@Grevil: Re #25: Perhaps it would make sense to create an issue over at Media Migration to let them know and perhaps implement a default for that or add it to their documentation? This is important to know, but shouldn't be relevant in this issue.

grevil’s picture

@Anybody already did that a while ago and you already commented that issue! :P
#3317541: [PP-1] Document the use of future implementations "hook_field_migration_field_widget_info()" and "hook_field_migration_field_formatter_info()" with this module

Just thought this information might be relevant here aswell!

Version: 9.5.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

grevil’s picture

Patch from #20 still applies to current 11.x, using latest reroll from https://www.drupal.org/project/drupal/issues/3204212#comment-15475785!

quietone’s picture

marc.bau’s picture

Any chance that this goes in before D7 goes out of support? Migrating from D7 is quite difficult without and I do not like to run D7 in 2026 only because of migration failures.

quietone’s picture

@marc.bau, if you haven't already you can consider seeking support in the #migration channel of Drupal Slack.

The Migrate Drupal Module was approved for removal in #3371229: [Policy] Migrate Drupal and Migrate Drupal UI after Drupal 7 EOL.

This is Postponed. The status is set according to two policies. The Remove a core extension and move it to a contributed project and the Extensions approved for removal policies.

The deprecation work is in #3522602: [meta] Tasks to remove Migrate Drupal module and the removal work in #3522602: [meta] Tasks to remove Migrate Drupal module.

Migrate Drupal will not be moved to a contributed project. It will be removed from core after the Drupal 12.x branch is open.

marc.bau’s picture

How should a removal of migration stuff from core fix the defects and allow people to migrate? *shakes head*

quietone’s picture

Status: Postponed » Closed (won't fix)

@marc.bau, sites can continue to use the Migrate Drupal functionality in Drupal 11, which will be supported until 2028. The Migrate API itself will remain Drupal 12. Fixes can still be made to Migrate Drupal but that will not include feature requests.

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

quietone’s picture

Oops, forgot this comment.

The Migrate Drupal and Migrate Drupal UI modules are deprecated and will be removed from Drupal 12. Since these are deprecated feature requests and some tasks will not be fixed.

Thanks to all for working on this!