Problem/Motivation
When Drupal migrates field formatter and field widget configurations into entity_view_display and entity_form_display entities, it often happens that the migrated formatter plugin (or field widget) has no replacement on the destination site.
In this cases, plugin discovery throws (and logs) a PluginNotFoundException (with an another log created by MigrateUpgradeImportBatch::onIdMapMessage), and the affected field gets hidden in its target entity_view_display (or entity_form_display) entity.
PluginManager's message:
The "file_rendered" plugin does not exist. Valid plugin IDs for Drupal\Core\Field\FormatterPluginManager are: address_country_default, address_default, address_plain, address_zone_default, comment_default, comment_username, comment_permalink, datetime_plain, datetime_custom, datetime_time_ago, datetime_default, file_default, file_rss_enclosure, file_extension, file_filemime, file_uri, file_url_plain, file_video, file_audio, file_table, file_size, file_link, image, image_url, link, link_separate, media_thumbnail, oembed, list_key, list_default, entity_reference_rss_category, text_default, text_summary_or_trimmed, text_trimmed, user_name, author, timestamp, entity_reference_entity_id, entity_reference_label, boolean, number_decimal, language, basic_string, string, timestamp_ago, uri_link, number_integer, number_unformatted, email_mailto (/Users/zoli/projects/drupal/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryTrait.php:53)
Migrate Drupal UI's log message:
Source ID node,article,featured,field_image: The "file_rendered" plugin does not exist. Valid plugin IDs for Drupal\Core\Field\FormatterPluginManager are: address_country_default, address_default, address_plain, address_zone_default, comment_default, comment_username, comment_permalink, datetime_plain, datetime_custom, datetime_time_ago, datetime_default, file_default, file_rss_enclosure, file_extension, file_filemime, file_uri, file_url_plain, file_video, file_audio, file_table, file_size, file_link, image, image_url, link, link_separate, media_thumbnail, oembed, list_key, list_default, entity_reference_rss_category, text_default, text_summary_or_trimmed, text_trimmed, user_name, author, timestamp, entity_reference_entity_id, entity_reference_label, boolean, number_decimal, language, basic_string, string, timestamp_ago, uri_link, number_integer, number_unformatted, email_mailto (/Users/zoli/projects/drupal/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryTrait.php:53)
The messages above always follow each other.
Proposed resolution
Instead of logging the same message with MigrationInterface::MESSAGE_ERROR twice, we prevent PluginNotFoundExceptions by automatically selecting the default field formatter plugin ID (or widget ID). In these cases (so when the fall-back logic was needed for the right field formatter plugin ID), we add the related message to the MigrateExecutable in those migrate process plugins that are doing the fallback. These are caught in MigrateUpgradeImportBatch and logged into the logger.
This still informs the user, but does allow the migration to succeed, allows checking the rendered migrated entity, and generally results in fewer pointless messages.
We can do this for both d7_field_formatter_settings and d7_field_instance_widget_settings — the same problem can occur for both formatters & widgets.
Remaining tasks
User interface changes
API changes
Data model changes
Default formatters and widgets instead of hidden fields in entity forms and rendered entities.
Less resources are needed to restore the original structure.
Comments
Comment #2
huzookaComment #3
huzookaSet to NR only for testbot.
Comment #4
huzookaComment #5
huzookaComment #6
huzookaComment #7
huzookaComment #8
huzookaI added a really weird workaround for the
Drupal\migrate_drupal\Plugin\migrate\field\FieldPluginBaseclass (I have to refactor the preexisting\Drupal\Tests\migrate_drupal\Kernel\*\FieldDiscoveryTesttests later).Explanation will be posted tomorrow (after a successful patch testing hopefully).
With the attached patch, we already get the expected Migrate Drupal UI log messages (without the PluginNotFoundExceptions mentioned in the IS):
Source ID node,article,featured,field_image: The field formatter plugin id file_rendered (used on field type image) could not be mapped to an existing formatter plugin; defaulting to image and dropping all formatter settings. Either redo the migration with the module installed that provides an equivalent formatter plugin, or modify the entity view display after the migration and manually choose the right field formatter.Comment #9
huzookaComment #10
wim leersNice progress here 🙂
field_formatter_map_alterandfield_widget_map_alteradditions for example.🤓 s/id/ID/
🤓
s/plugin_id/plugin ID/
s/target/destination/
🤓 Mismatch.
You can omit the parenthetical here. It's fine that there is no interface and that you're hinting the concrete class :)
👍 Glad to see you're handling this edge case :)
🤔 What about Drupal 6 though? I don't think this process plugin is D7-specific?
🤓 Mismatch.
🐛 Widget, not formatter.
🤓 s/variable/variables/
Comment #11
huzookaComment #12
huzookaCleaned up
Drupal\migrate_drupal\Plugin\migrate\field\FieldPluginBaseand slightly updated the relatedFieldDiscoverytests.We'll definitely need additional test coverage for the three new process plugins, and of course we also have to add
some moretest source data in the related database fixtures.Re #10:
I'll explain the most-recent patch in the following comment.
Comment #13
wim leers#12:
Comment #14
wim leers(This is relatively minor, let's focus on everything else first.)
But that means they're logged on behalf of
migrate_drupal_uiinstead of on behalf of the specific migration plugin where it occurred.Plus, it means that it'd be logged differently for each migration runner, for example https://www.drupal.org/project/migrate_tools or https://www.drupal.org/project/migrate_run would yield different log entries.
Comment #15
huzookaComment #16
huzookaThe essential part of this patch is that it adds two migrate plugins to Drupal:
Drupal\field\Plugin\migrate\process\FieldFormatterFallback(field_formatter_fallback) andDrupal\field\Plugin\migrate\process\FieldWidgetFallback(field_widget_fallback).These are the process plugins that are checking whether the required formatter / widget plugin IDs are available on the destination site, or not.
Let me focus on the widget plugins (especially how
d7_field_instance_widget_settingsworks)! My idea was:widget_type_mapped.'options/type'), re-use the previously processedwidget_type_mappedvalue, and chain it with the newfield_widget_fallbackplugin.widget_type_mappedvalue with the (final)'options/type'value, and if they are not equal, we have to return an empty array. This is what the thirdDrupal\migrate\Plugin\migrate\process\NullIfNotEqual(null_if_not_equal) migrate plugin does for us.Unfortunately, I was stuck on the first step. Right now, in the field migrations, even the name of the destination properties (this is
'options/type'), and even the key of their process plugin (this is0in the field formatter migrations andtypein the field widget migrations) are hardcoded (seed7_field_instance_widget_settings.yml,FieldPluginBaseor evenFieldDiscoverTest).For the
d7_field_instance_widget_migrationthis also means that it is impossible to add an another process plugin to the'options/type':typeprocess pipeline.I decided to make these a bit more flexible; that's why I added the
field_widget_map_alterkey to thosestatic_mapmigrate process plugins that's mapping has to be extended (and has to be extensible) by the field migrations that extendFieldPluginBase.The biggest part of the patch tries to solve this hard-coded logic and make the field [ config[ related]] migrations a bit more flexible.
PS
I had the same issues with the formatter plugin migration.
Comment #18
huzookaForgot to apply the required changes for D6 field discover test.
Comment #19
wim leers🤓 Übernit: removing empty lines in this patch is an unnecessary distraction in an already complex patch. Let's not change these.
Looks like this still needs to happen.
🤔 Why do we skip the message only sometimes?
👏 This looks elegant to me :) But definitely needs migration system maintainer sign-off.
🤓 Übernit: s/id/ID/
Comment #20
huzookaRe #19.2, #19.3:
After diving into the field instance and field instance widget settings process plugins, I have to say that this issue still needs some more work.
These are the problematic migrations (and plugins):
Field instance migration:
FieldInstanceSettings(d7_field_instance_settings)FieldInstanceDefaults(d7_field_instance_defaults)Field widget instance migration:
FieldInstanceWidgetSettings(field_instance_widget_settings) plugin, (in a wrong namespace, because this is used for d6 and d7 migrations – however, the plugin ID suggests this)All of the above plugins are using the source widget plugin IDs to do their transformations (see
FieldInstanceWidgetSettings: the values in the switch statements are d6/d7 widget IDs).I just was lucky with the field widget (and the field config) migrations.
The attached patch addresses #19.1, #19.4, #19.5:
Comment #21
huzookaWell, I was wrong in #20:
If the equivalent widget plugin ID is not available on the destination, we definitely don't want to migrate the field settings.
So processing the settings based on the source plugin ID is the right way!
So I removed #19.2 because it makes no sense.
Re #19.3:
We don't really fall back to a default widget in field instance migrations: the widget is only used for processing the properties of a
FieldConfigentity. (And I don't have a better idea).Comment #22
huzookaBefore adding the required test coverage for the new migrate process plugins and new test cases (db-fixture updates), I would like to get a maintainer sign off on the proposed solution.
After that, the next actionable tasks are (imho):
field_formatter_fallback,field_widget_fallbackandnull_if_not_equalimageandemailtypes are good candidates for this... Or should we cover all of the field types?Comment #23
wim leersManually tested this patch on two test cases:
wimleers.com(simple D7 site) and Demo Framework (complex D7 site).wimleers.com
Demo Framework
Conclusion
I think that in this case we should detect that it's for an unknown field type and provide an error message attuned to that. That would prevent users from wasting time on this if the real problem to solve is installing the appropriate field type — because if no fallback can be computed, then the corresponding
FieldConfigandFieldStorageConfigsurely also failed!Both of these need to be improved, but keeping at because @huzooka's question in #22 still stands: this needs sign-off from a migration system maintainer for the general direction.
Comment #24
wim leersCame up in yesterday's Migrate meeting, no response yet. We'll keep waiting patiently :)
Comment #25
huzookaComment #26
huzookaRe #23.2:
I think this patch fixes the issue of the field formatter plugin fallback: prior this pacth, I tried to find a formatter plugin for the d7 'taxonomy_term_reference' plugin... Now I check the destination field type :).
Let's see what happens with the core tests.
Comment #27
wim leersManually tested with
wimleers.comagain.And this positive impression is confirmed if we dig a little deeper:

i.e. all of the missing formatters now have a message saying we automatically fell back to the default! 🥳
Comment #28
alisonFirst, four questions, "just checking":
These two diffs, near line 82 (
alterFieldWidgetMigration) -- and they're repeated for the "formatter" function, of course:1:
2:
- $migration->mergeProcessOfProperty('options/type', $process);Second, I have a draft change record -- but I'm going to post it in a follow-up comment, b/c it got pretty long and I don't want things to get lost. I'm sure it can be trimmed down, this is just a first draft.
Comment #29
alison"Draft change record", such as it is... coming at this issue fresh, doing my best to understand what's being done! (FWIW, I'm least sure of my explanation of null_if_not_equal.)
Summary
Use default field formatter plugins and field widget plugins when source formatters/widgets don't have a replacement on the destination site, thereby preventing affected fields from being hidden in its target entity_*_display (view or form), and making these fields usable and visible.
Also, the new null_if_not_equal process plugin can be used outside this widget/formatter fallback use case.
Before
When Drupal migrates field formatter and field widget configurations into entity_view_display and entity_form_display entities, it often happens that the migrated formatter plugin (or field widget) has no replacement on the destination site. In these cases, plugin discovery throws (and logs) a PluginNotFoundException (with an another log created by
MigrateUpgradeImportBatch::onIdMapMessage) message (MESSAGE_ERRORseverity).Also, the affected field gets hidden in its target entity_view_display or entity_form_display entity.
After
Automatically select the default field formatter plugin ID or widget plugin ID. (A migrate message is still saved to logs, saying that the "fallback" was used, but just one message, and with
MESSAGE_NOTICEseverity.)More details
Three new migrate process plugins have been added:
FieldFormatterFallback: Transform a non-mapped field formatter plugin ID to the D8 default formatter when the destination plugin ID is not available.FieldWidgetFallback: Transforms field widget plugin ID to the D8 default widget if the destination widget plugin id is unavailable.NullIfNotEqual: Changes the source value to null if the two given variables are not equal.(The first two, the "fallback" plugins, save a migrate message with
MigrationInterface::MESSAGE_NOTICEseverity on fallback.)The third plugin,
null_if_not_equal, was added in order to compare the original (mapped-only) "widget/formatter_type_mapped" value with the final/"real" value ('options/type'), and return an empty array. The empty array prevents the widget/formatter plugin from being migrated -- instead, the fallback/default plugin values are used. In other words, if the compared values don't match, the plugin settings are set to "null", which triggers the fallbacks.The null_if_not_equal process plugin can be used beyond this widget/formatter fallback use case.
Lastly, two new
static_mapprocess plugin options were added to FieldPluginBase -- both boolean -- to allow merging the widget/formatter mapping for use by the new fallbacks:field_widget_map_alterfield_formatter_map_alterComment #30
heddnJust a drive-by review here.
NullIfNotEqual is probably not needed. I haven't worked out how, but this type of thing is usually possible without the addition of new plugins.
Instead of the existing somewhat complicated method to merge things, couldn't the existing static_map class be extended by FieldFormatterFallback and FieldWidgetFallback and build all that logic internally?
For the CR, usually we use the name of the plugin_id, as that is part of the API and the class name isn't so important.
Comment #31
alison(Thanks for the CR feedback! I'll integrate that into the actual draft CR if this whole thing moves forward.) (Meanwhile...)
...........
I realize #3061571: If no Drupal 8 equivalent filter is found during migration, drop transformation-only filters, so that body fields show up is different, but it just got bumped to RTBC -- since we're getting maintainer concerns on this issue (#3108302), I just wondered, even though they're different situations, are there any "principles" from the implementation on #3061571 that could/should be applied to this issue (#3108302)...?
Comment #32
heddnIgnore #30 now. I've spent a little more time looking into things.
So, I did some poking around in what we currently have in core for comparison process plugins. There doesn't seem to be any. And using callback, it only supports a single value, not multiple arguments. Skip on value in migrate_plus also doesn't fit the bill either.
So I got to thinking, what if instead of making a single purpose plugin, what if we created a "comparison" process plugin? One that had as config the traditional compare operators: https://www.php.net/manual/en/language.operators.comparison.php and returned true/false. From there, we could easily do a static_map mapping of true/false to empty array.
Remember that transform plugins can take more then one value into the transform method. See file_copy as an example.
And for FieldFormatterFallback/FieldWidgetFallback, could the logic in there be
I'm not sure why we have to add the boolean
field_widget_map_alterflag. Could we leverage the existing call togetFieldWidgetMapand add the mappings into it?Comment #35
huzookaThis patch is actually a rewrite on top of #3202462: [PP-1] Provide option for contrib modules to map their D6 / D7 field formatter and widget plugin IDs to the equivalent D9 plugin ID.
Comment #36
huzookaThe plugin MUST declare itself with handle_multiple set to TRUE.
Comment #37
kapilv commentedComment #38
huzooka@KapilV Sorry.
Comment #39
huzookaComment #40
wim leers#35: wow, this patch now looks way simpler! Well done :)
👍 This is the generic successor to both
FieldFormatterFallbackandFieldWidgetFallbackin #26.🐛 Needs doc update: this works not just for field formatter plugin IDs now, but also field widget plugin IDs.
👉Fixed in attached patch.
🐛 This message is wrong: this is for widgets, not formatters.
👉Fixed in attached patch.
👍
Comment #41
wim leersI did not mean to change the IS.
While I'm here, I might as well clarify the issue title: it is blocked on #3202462: [PP-1] Provide option for contrib modules to map their D6 / D7 field formatter and widget plugin IDs to the equivalent D9 plugin ID, which is in turn blocked on #3204212: Convert remaining widget and formatter type migrations to MigrateField plugins .
Comment #42
kapilv commentedComment #44
anybodyConfirming #40 fixed my migration CCK file (image) field problems from Drupal 6 to Drupal 9.1.7 where I used "Lightbox 2" as image formatter, which wasn't existing in Drupal 9. Without the patch the Gallery images were missing, now they are imported with the fallback as expected.
For others who need the whole composer patches combo of this issue with current state:
BTW shouldn't this be "Needs review", not "Needs work" as of #40? (Or alternatively "Postponed"?)
EDIT: Updated to work with 9.3.x, replaced https://www.drupal.org/files/issues/2021-03-22/3204212-field-migration-w...
Comment #45
wim leersRebased #40 to apply to
9.2.0-rc1.Was trivial thanks to git :)
Comment #46
wim leers#44: yay!!! 🥳
Hopefully we can get this committed soon!
Comment #47
alisonYay!
We still need a CR, right? (It's not just an outdated tag?)
If yes, if no one else has already started, I can work on updating the one I started in #29, with the feedback in #30 -- but could someone offer some guidance on "where" I should "put" a draft CR? -- just in a comment, or in the issue summary, or an actual CR node, or?
Comment #48
quietone commented@alisonjo315, The actual CR. I use the link at the top of the Issue just above 'Related issues', 'add change notice', the change notice will open in draft mode.
Comment #49
grevil commentedAnybody knows why the patch from #45 fails to apply on 9.3.x? Just wanted to use the patch for my local d6 Migration, but can't seem to be able to apply it. Is it dependent on some other patch? If so which one?
I tried the patch-order from @Anybody's comment in #44, but then it doesn't want to apply the patch from #3204212: Convert remaining widget and formatter type migrations to MigrateField plugins . Maybe somebody can help? I'm currently using Drupal 9.3.0-alpha1.
Sorry if this is trivial stuff :)
Comment #50
anybody@Grevil: #44 contained the first patch against 9.1.x - updated and replaced by
https://www.drupal.org/files/issues/2021-06-14/3204212-field-migration-w...
Please try if all three apply now or which one doesn't
Comment #51
grevil commented@Anybody still can't apply patch "https://www.drupal.org/files/issues/2021-06-14/3204212-field-migration-w..." from #3204212: Convert remaining widget and formatter type migrations to MigrateField plugins , even with rerolled patch.
Could not apply patch! Skipping. The error was: Cannot apply patch https://www.drupal.org/files/issues/2021-06-14/3204212-field-migration-widget-formatter-mapping-27.patchEDIT: Apparently patch 27 from #3204212: Convert remaining widget and formatter type migrations to MigrateField plugins doesn't work for 9.3.x anymore, see comment #27. It still works for 9.2.x though!
Comment #53
jeroentComment #55
ajaypratapsingh commentedRerolled patch #26 against drupal core 9.5.x
Comment #56
ranjith_kumar_k_u commentedComment #58
hemant bansal commentedComment #59
hemant bansal commentedRerolled patch #26 with Drupal 9.5.x
Comment #60
andypostIt could be useful for #3213491-20: Add fallback format support to responsive images
Comment #61
danflanagan8This one definitely needs a status change. All the recent patches have failures, so at the very least this should be set to Needs Work. But this also appears to be blocked by two other issues per #41. If this isn't in fact postponed, it needs a title update. I'm going to be bold and set this to Postponed.
Comment #64
wim leersThis patch (I'm still using #45, and that works fine even on Drupal 10.2!) triggers
on PHP >=8.2.
Fixed that.
Comment #65
quietone commentedThe 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.
Comment #66
quietone commentedThe Migrate Drupal and Migrate Drupal UI modules are deprecated and will be removed from Drupal 12. For these modules, effort is now focused on bug fixes and necessary tasks. Therefore, this feature request is closed as won't fix.
Thanks to all for working on this!