Series Overview & ToC | Previous Article | Next Article
In the previous article, we migrated field storage and instance settings. Along the way, we used the Migrate Skip Fields module to account for content model changes in the new site. Today, we continue with the next field-related migration: widgets. While doing so, we will find out that new migrations might uncover issues or misconfigurations with already executed migrations.
Before we begin
Familiarity with site building concepts related to fields is assumed. Refer to article 16 for a high level overview.
Drupal themes control the appearance of the website and how user interface elements are presented. In a standard Drupal 10 installation, Olivero is the default theme and Claro is the administration theme. Additionally, the administration theme is used when editing or creating content. Our example Drupal 10 project follows this setup. All of this can be configured from the Apperance
page at https://migration-drupal10.ddev.site/admin/appearance
Alternatively, you could run the following commands:
ddev drush theme:enable olivero && ddev drush --yes config:set system.theme default olivero
ddev drush theme:enable claro && ddev drush --yes config:set system.theme admin claro
ddev drush --yes config:set --input-format=yaml node.settings use_admin_theme TRUE
Field widgets migrations
A widget determines the form elements used to capture field-related data. Each field type can be supported by one or more field widgets. For example, an entity reference field can use an HTML input with autocomplete functionality or a select list. Configuration for the field can affect which form elements are used. Following the same example, an entity reference field can be configured to use the Check boxes/radio buttons
widget. In this case, a cardinality of one renders radio buttons. Check boxes are presented when the cardinality is greater than one or when set to unlimited.
When multiple widgets are available for the same field type, Drupal takes care of standardizing how the data is stored in the database. No matter which field type is used for the entity reference field, they are saved in the database using the same data structure.
We use upgrade_d7_field_instance_widget_settings
to migrate field widget settings. Copy it from the reference folder into our custom module and rebuild caches for the migration to be detected.
cd drupal10
cp ref_migrations/migrate_plus.migration.upgrade_d7_field_instance_widget_settings.yml web/modules/custom/tag1_migration/tag1_migration_config/migrations/upgrade_d7_field_instance_widget_settings.yml
ddev drush cache:rebuild
Note that while copying the file, we also changed its name and placed it in a migrations
folder inside our custom module. After copying the file, make the following changes:
- Remove the following keys:
uuid
,langcode
,status
,dependencies
,cck_plugin_method
, andmigration_group
. Notice that for field migrations, we preserve thefield_plugin_method
key. - Add two migration tags:
component_entity_form_display
andtag1_configuration
. - Add
key: migrate
under the source section. - Update the required
migration_dependencies
to includeupgrade_d7_field
,upgrade_d7_field_instance
, and the migrations that create configuration entities.
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 widget migration to prevent some fields being imported. In particular, those excluded by the migrate_skip_fields_by_name
setting as explained in the previous article. For field widgets, the module does not act on the migrate_skip_fields_by_type
setting. The original migration already accounts for not importing widget data for a field whose storage setting was not migrated. This happens in the field_type_exists
pipeline of the process
section.
We are going to make a small addition here. The last process plugin in this pipeline is skip_on_empty
. We are going to 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.
After the modifications, the upgrade_d7_field_instance_widget_settings.yml
file should look like this:
id: upgrade_d7_field_instance_widget_settings
class: Drupal\migrate_drupal\Plugin\migrate\FieldMigration
field_plugin_method: alterFieldWidgetMigration
migration_tags:
- 'Drupal 7'
- Configuration
- component_entity_form_display
- tag1_configuration
label: 'Field instance widget configuration'
source:
key: migrate
plugin: d7_field_instance_per_form_display
constants:
form_mode: default
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."
bundle:
-
plugin: migration_lookup
migration: upgrade_d7_field_instance
source:
- entity_type
- bundle
- field_name
-
plugin: extract
index:
- 1
form_mode:
-
plugin: get
source: constants/form_mode
field_name:
-
plugin: get
source: field_name
entity_type:
-
plugin: get
source: entity_type
-
plugin: static_map
map:
field_collection_item: paragraph
paragraphs_item: paragraph
bypass: true
options/weight:
-
plugin: get
source: widget/weight
widget_type:
-
plugin: process_field
source: type
method: getFieldWidgetType
options/type:
type:
plugin: static_map
bypass: true
source: '@widget_type'
map:
link_field: link_default
email_textfield: email_default
date_select: datetime_default
date_text: datetime_default
date_popup: datetime_default
media_generic: file_generic
phone_textfield: telephone_default
options_onoff: boolean_checkbox
entityreference_autocomplete: entity_reference_autocomplete
entityreference_autocomplete_tags: entity_reference_autocomplete_tags
taxonomy_autocomplete: entity_reference_autocomplete
d7_text: d7_text_default
taxonomy_term_reference: taxonomy_term_reference_default
image: image_default
image_miw: image_image
url_external: link_default
field_collection_embed: entity_reference_paragraphs
field_collection: field_collection_default
addressfield_standard: address_default
entityreference: entityreference_default
file_mfw: file_generic
filefield_widget: file_generic
list: list_default
date: datetime_default
datetime: datetime_default
datestamp: datetime_timestamp
number_default: number_default_default
options/settings:
-
plugin: field_instance_widget_settings
source:
- widget/type
- widget/settings
options/third_party_settings:
-
plugin: get
source: constants/third_party_settings
destination:
plugin: component_entity_form_display
migration_dependencies:
required:
- upgrade_d7_field
- upgrade_d7_field_collection_type
- upgrade_d7_field_instance
- upgrade_d7_node_type
- upgrade_d7_taxonomy_vocabulary
optional: { }
Now, rebuild caches for our changes to be detected and execute the migration. Run migrate:status
to make sure we can connect to Drupal 7. Then, run migrate:import
to perform the import operations.
ddev drush cache:rebuild
ddev drush migrate:status upgrade_d7_field_instance_widget_settings
ddev drush migrate:import upgrade_d7_field_instance_widget_settings
No errors after running the migration. Great! Feel free to have a look at the logs using the drush migrate:messages
command.
Similar to the migrations of storage and instance settings, we should review that the result of migrating field widget settings is correct. There are two places you should look at. Using the Article
content type as an example, go to:
- The manage form display page where you can make adjustments to the migrated field widget configuration.
- The add article page where you can see the current state of the field widget configuration.
This is where having familiarity with site building concepts and past experience will help. Below is a screenshot of the Add content
page for the Article content
type.
Compare it with the equivalent page in Drupal 7: https://migration-drupal7.ddev.site/node/add/article
With regards to fields, there are three things to note:
-
The
Image
(field_image
) field is missing. This is to be expected because we will be using media reference fields instead. All uses of thefield_image
field were skipped while working on the storage and instance migrations. We are going to add media reference fields in a future article. -
The
Tags
(field_tags
) field does not behave the same compared to the source site. In Drupal 7, there is a single HTML input element that permits entering a comma-separated list of taxonomy terms. Additionally, if a term does not exist, it will be created on the fly in theArticle Tags
taxonomy vocabulary. In Drupal 10, the field is currently configured to use a separate HTML input element for each term we want to reference. Moreover, creating terms on the fly is not allowed. -
The
Body
(body
) field does not offer the option to choose a text format. At the moment only thePlain text
format exists on the site. When there are no other formats available, rich text fields do not show the select list for choosing among the available options. We are going to cover migrating text formats in a future article.
Understanding how field widget settings are mapped
The entity reference system received a major overhaul in Drupal 8 and that has carried over ever since. For brevity, we are only going to highlight what is relevant to the example at hand.
In Drupal 7, there were at least two ways to reference taxonomy terms. One is using Term reference
fields provided by the core Taxonomy module and the other is using Entity Reference
fields provided by the contributed Entity Reference module. The former provides one widget called Autocomplete term widget (tagging)
. The latter provides two widgets: Autocomplete
and Autocomplete (Tags style)
.
Drupal 10 follows the pattern of the contributed module by providing two widgets for referencing entities and even reuses the same labels. Below are the machine names and labels for all five widgets:
-
taxonomy_autocomplete
: Drupal 7's coretaxonomy
moduleAutocomplete term widget (tagging)
widget. -
entityreference_autocomplete
: Drupal 7's contributedentityreference
moduleAutocomplete
widget. -
entityreference_autocomplete_tags
: Drupal 7's contributedentityreference
moduleAutocomplete (Tags style)
widget. -
entity_reference_autocomplete
: Drupal 10 coreAutocomplete
widget. -
entity_reference_autocomplete_tags
: Drupal 10 coreAutocomplete (Tags style)
widget.
Take a look at the following snippet extracted from the upgrade_d7_field_instance_widget_settings
migration:
process:
options/type:
type:
plugin: static_map
bypass: true
source: '@widget_type'
map:
entityreference_autocomplete: entity_reference_autocomplete
entityreference_autocomplete_tags: entity_reference_autocomplete_tags
taxonomy_autocomplete: entity_reference_autocomplete
This is where the mapping of widgets settings from source to destination happens. Notice that taxonomy_autocomplete
from Drupal 7 is mapped to entity_reference_autocomplete
in Drupal 10. This is why the Tags
(field_tags
) field in Drupal 10 does not offer the tags style behavior. For that to work, we need to update its instance settings to leverage the entity_reference_autocomplete_tags
.
As for creating terms on the fly, Drupal 7 Term reference
fields could only include terms from a single vocabulary. In Drupal 10, entity reference fields allow including entities from multiple bundles of the same entity type. When multiple bundles can be referenced and creating entities that do not exist is allowed, you need to pick one bundle where the new entities will be added. In the case of the Tags
(field_tags
) field in Drupal 10, we need to update field instance settings to allow creating taxonomy terms on the fly.
That was quite a technical detour from reviewing the result of the field widget migration. In most cases, you do not need to know the technicalities of how the Migrate API maps configuration from Drupal 7 to Drupal 10. That being said, we have leveled up by gaining a deeper understanding of how the transformation process works.
Manual updates to field widget settings
In your own projects, take the time to review the migrated widget settings for all fields across all entity types. For our example project, below is the list of adjustments that need to be made manually:
-
field_tags
field inarticle
content type: change field storage to create referenced entities if they do not already exist and update the widget to use theAutocomplete (Tags style)
one. -
field_topics
field insession
content type: change field storage to create referenced entities if they do not already exist and update the widget to use theAutocomplete (Tags style)
one. -
field_address
field invenue
content type: update the field instance settings to make theUnited States
as the only available country and hide some address components. Namely, add an override to hide:First name
,Middle name
,Last name
,Organization
,Address line 3
.
To replicate the tag style behavior for the field_tags
field, go to Manage form display
page for the Article
content type: https://migration-drupal10.ddev.site/admin/structure/types/manage/article/form-display
and select the Autocomplete (Tags style)
widgets for the Tags
field.
To allow creating terms on the fly in field_tags
field go to the Manage fields
page for the Article
content type, go to https://migration-drupal10.ddev.site/admin/structure/types/manage/article/fields
Then, edit the Tags
field and in the Reference type
section check the box for Create referenced entities if they don't already exist
.
If we were to allow referencing terms from multiple vocabularies, you will get a prompt labeled Store new items in
to indicate which vocabulary should contain the newly created terms. The image below shows this interface for reference, but in our final example, the Tags
field only allows referencing terms from one vocabulary: Article Tags
.
The changes to field_topics
and field_address
will be left as an exercise to the reader. Refer to the code repository for the final results of the migration of configuration.
While reviewing the field widget migration, we identified instance settings issues that required manual updates. This is common in the migration process, where adjustments to previous migrations are necessary when working on new ones. Remember that field migrations are complex, with storage, instance, widget, and formatter settings being closely interconnected.
We’ve covered significant ground in our comprehensive Drupal 7 to 10 migration series to date, but there is more to explore. Our next article will focus on migrating view modes and field groups. We encourage you to apply these insights to your own migration projects and look forward to guiding you through the next steps.