Migrating Your Data from D7 to D10: Migrating field formatter settings Migrations How-To: #20

Remember, if you have been following our series, you have already migrated view modes—a prerequisite for field formatters. The Migrate Skip Fields module will also be used to handle content model changes in the new site.

In this article, we are completing field-related migrations by importing formatter settings. This step builds on our previous work with view modes and field groups, bringing us closer to a functional Drupal 10 site. As we go through the process we will also identify other configuration elements used by field formatters that don’t have an automatic migration.Below is a screenshot of a Venue node rendered in Drupal 10.

Before we begin

Familiarity with site building concepts related to fields is assumed. Refer to article 16 for a high level overview. Today’s article also touches on view modes and date formats.

Field formatter migrations

Series Overview & ToC | Previous Article | Next Article – coming soon!
Notice that for the Topics field, the label is rendered in bold in Drupal 10, but not in Drupal 7. As for the terms themselves, in Drupal 10 they appear stacked on top of each other and, in Drupal 7, they are listed next to one another. For the Slides field, Drupal 10 shows the file size in parenthesis and not the file icon. In Drupal 7, you see an icon that varies per file type and there is no reference to the file size. These differences are a combination of changes to how formatters are implemented in different versions of Drupal and how themes decide to render some user interface elements.
Notice that the Date field in Drupal 10 shows the time even though the field type is supposed to store a date value only. This is because the Default long date format used to render the field is configured to render a time component. In Drupal 7, the Date field is configured to use Long date format, which can also render a time component; however, it is hidden when the date field is configured to store a date value only. This highlights how formatters can behave differently between Drupal versions and the importance of carefully reviewing the migrated configuration.
We are going to make a small addition here. The last process plugin in this pipeline is skip_on_empty. We will add a message configuration to log when a field is skipped because its storage setting is missing. The logs are added to the migration tables and can be viewed from the command line or the migration messages admin interface.

  • Remove the following keys: uuid, langcode, status, dependencies, cck_plugin_method, and migration_group. Notice that for field migrations, we preserve the field_plugin_method key.
  • Add two migration tags: component_entity_display and tag1_configuration.
  • Add key: migrate under the source section.
  • Update the required migration_dependencies to include upgrade_d7_field, upgrade_d7_field_instance, upgrade_d7_view_modes, and the migrations that create configuration entities.

Below is a screenshot of an Session node comparing how some of the fields attached to the content type are rendered.
In addition to the updates above, remember that the Migrate Skip Fields module is installed on the site. Via hook_migration_plugins_alter, it modifies the field formatter migration to prevent some fields being imported. In particular, those excluded by the migrate_skip_fields_by_name setting as explained in article 17.
The Venue content type has a Phone field that is rendered as plain text. This matches the configuration from Drupal 7, but that does not mean that we cannot make improvements as part of the migration. In this case, we can update the field to use the Telephone link formatter to render a link using the tel URL scheme. Users visiting the site with a mobile phone will be able to place the call by clicking on the link. Drupal is producing semantic HTML markup and it is up to the visitor’s browser to determine what action to take when the link is clicked. Not obvious from the screenshot above, but the Address field shows the country in Drupal 10. The field formatter in Drupal 7 hides it when the field only allows a single country to be selected. No action to take as part of the example project.
This is where having familiarity with site building concepts and past experience will help. Below is a screenshot of an Event node comparing how some of the fields attached to the content type are rendered.
A formatter determines how field data is rendered by Drupal. Each field type can be supported by one or more field formatters. For example, an entity reference field can print only the entity ID, its label as plain text or as a link, or the representation of the entity as specified by a view mode. Some field configuration might affect which formatters are available. For example, entity reference fields that point to taxonomy terms can use the RSS category formatter.
While reviewing the field formatter migration, we identified that date formats are not migrated automatically. Migrations can be complex, and not every Drupal 7 setting has an automated upgrade path. Make sure to thoroughly review the migrated configuration and perform manual updates as needed. To wrap up the migration of fields, remember that storage, instance, widget, and formatter settings are closely interconnected.
Image by Mohamed Hassan from Pixabay

  1. The manage display page where you can make adjustments to the migrated field formatter configuration.
  2. The view page for a session node where you can see the current state of the field formatter configuration. At the moment, we have not migrated nodes nor other content entities. Feel free to create some content to see how it is rendered.

We use upgrade_d7_field_formatter_settings to migrate field formatter settings. Copy it from the reference folder into our custom module and rebuild caches for the migration to be detected.

Manage Display page
Similar to other field-related migrations, we should confirm that the result of migrating field formatter settings is correct. There are two places you should look at. Using the Event content type as an example, go to:
Technical note: Drupal 10 does not provide an automated upgrade path for custom date types and formats from Drupal 7. They are stored in the date_format_type and date_formats tables respectively. You can write a custom migration to read from those Drupal 7 tables and create DateFormat configuration entities in Drupal 10. Or you can recreate them manually on the new site. In either case, you will have to identify where the custom date formats were used in Drupal 7 and manually update the corresponding configuration in Drupal 10. At the time of this writing, this issue contains a patch for migrating custom date formats, but no date types.
ddev drush cache:rebuild
ddev drush migrate:status upgrade_d7_field_formatter_settings
ddev drush migrate:import upgrade_d7_field_formatter_settings

cd drupal10
cp ref_migrations/migrate_plus.migration.upgrade_d7_field_formatter_settings.yml web/modules/custom/tag1_migration/tag1_migration_config/migrations/upgrade_d7_field_formatter_settings.yml
ddev drush cache:rebuild

For the example above, we could hide the time component for the Date field in multiple ways. One option is to create a new date format which does not print the time. Another option is the Custom date formatter for the date field and use a PHP datetime pattern that does not print the time like l, F j, Y. The first option is recommended if you plan to reuse the same date format in multiple places. The second option can be used if you are certain that there is no need to reuse the pattern.

Session Node
For the purpose of this example, we are going to go with a third option: use a different date format. Because we are using the Olivero theme, we can use the Olivero Medium date format which will hide the time and the day of the week. An acceptable compromise for the example.
Technical note: The FormatterInterface interface includes a isApplicable method that determines if a formatter can be used for a field taking into account its configuration.
For field formatters, the module does not act on the migrate_skip_fields_by_type setting. The original migration already accounts for not importing formatter data for a field whose storage setting was not migrated. This happens in the field_type_exists pipeline of the process section.

Venue Node
After the modifications, the upgrade_d7_field_formatter_settings.yml file should look like this:
For our example project, we are not going to take any action here. In real life projects, you are likely going to use a custom theme that will control how interface elements are rendered. Contributed modules like Fences can also manipulate the output of fields. As a rule of thumb, before adding any module to your project, evaluate if the functionality provided outweighs the extra configuration and maintenance required to use it.
id: upgrade_d7_field_formatter_settings
class: Drupalmigrate_drupalPluginmigrateFieldMigration
field_plugin_method: alterFieldFormatterMigration
migration_tags:
- 'Drupal 7'
- Configuration
- component_entity_display
- tag1_configuration
label: 'Field formatter configuration'
source:
key: migrate
plugin: d7_field_instance_per_view_mode
constants:
third_party_settings: { }
process:
# @modified
field_type_exists:
-
plugin: migration_lookup
migration: upgrade_d7_field
source:
- field_name
- entity_type
-
plugin: extract
index:
- 0
-
plugin: skip_on_empty
method: row
message: "Field storage configuration does not exist."
entity_type:
-
plugin: get
source: entity_type
-
plugin: static_map
map:
field_collection_item: paragraph
paragraphs_item: paragraph
bypass: true
bundle:
-
plugin: migration_lookup
migration: upgrade_d7_field_instance
source:
- entity_type
- bundle
- field_name
-
plugin: extract
index:
- 1
view_mode:
-
plugin: migration_lookup
migration: upgrade_d7_view_modes
source:
- entity_type
- view_mode
-
plugin: extract
index:
- 1
-
plugin: static_map
bypass: true
map:
full: default
field_name:
-
plugin: get
source: field_name
options/label:
-
plugin: get
source: formatter/label
options/weight:
-
plugin: get
source: formatter/weight
plugin_id:
-
plugin: process_field
source: type
method: getPluginId
formatter_type:
-
plugin: process_field
source: type
method: getFieldFormatterType
options/type:
-
plugin: static_map
bypass: true
source:
- '@plugin_id'
- '@formatter_type'
map:
taxonomy_term_reference:
taxonomy_term_reference_link: entity_reference_label
taxonomy_term_reference_plain: entity_reference_label
taxonomy_term_reference_rss_category: entity_reference_label
i18n_taxonomy_term_reference_link: entity_reference_label
i18n_taxonomy_term_reference_plain: entity_reference_label
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
field_url:
url_default: link
url_plain: link
field_collection:
field_collection_view: entity_reference_revisions_entity_view
addressfield:
addressfield_default: address_default
telephone:
text_plain: string
telephone_link: telephone_link
entityreference:
entityreference_label: entity_reference_label
entityreference_entity_id: entity_reference_entity_id
entityreference_entity_view: entity_reference_entity_view
file:
default: file_default
url_plain: file_url_plain
path_plain: file_url_plain
image_plain: image
image_nodelink: image
image_imagelink: image
datetime:
date_default: datetime_default
format_interval: datetime_time_ago
date_plain: datetime_plain
-
plugin: d7_field_type_defaults
-
plugin: skip_on_empty
method: row
hidden:
-
plugin: static_map
source: '@options/type'
map:
hidden: true
default_value: false
options/settings:
-
plugin: default_value
source: formatter/settings
default_value: { }
options/third_party_settings:
-
plugin: get
source: constants/third_party_settings
options/settings/view_mode:
field_collection:
plugin: paragraphs_process_on_value
source_value: type
expected_value: field_collection
process:
plugin: get
source: formatter/settings/view_mode
destination:
plugin: component_entity_display
migration_dependencies:
required:
- upgrade_d7_field
- upgrade_d7_field_collection_type
- upgrade_d7_field_instance
- upgrade_d7_node_type
- upgrade_d7_taxonomy_vocabulary
- upgrade_d7_view_modes
optional: { }

Coming up in our step-by-step migration series, we will complete configuration migrations by walking through: media types and image styles, roles and permissions, and text and input formats. Once the remaining configuration is in place, we will be ready to work with content.

Similar Posts