Sometimes in long living projects (modules, websites, ...) you have to make changes to historical fields. In our case, that change included one of the most essential properties: Changing the field type of an existing Drupal field with a lot of existing values.
For example, you have to merge / switch field types, if older modules were deprecated. Or you acknowledge that you need to use decimal instead of integer for a field (or vice-versa) or link text instead of string. There might be many cases, where you need to "keep the field" and it's data from outer perspective, but change its technical type and its many properties (storage, settings, widget formatter, display formatter, ...).
These are all typical cases which may occur over time, and you can't simply erase the field with all values, but have to migrate values using the old field name (id / uuid).
But even if the field schema is exactly the same (as in our case described below), this is still a horrible (DX) task to implement in Drupal (as of 9.4). This is why I'm writing a blog post about it to let you save a bit of your valuable time solving it.
Use-case: Switching a fontawesome_iconpicker (string) field to micon (string_micon) field
Our concrete task was to migrate existing fields, using fontawesome_iconpicker formatter (on core "string" field type) to Micon formatter (on micon "string_micon" field type).
This originated from discussion here: https://www.drupal.org/project/fontawesome_iconpicker/issues/3271956
as the fontawesome_iconpicker module isn't well maintained anymore and was lacking a Drupal 9 compatible release. We already used Micon successfully on many newer sites. So that became our favorite icon handling module.
Writing the code for this conversion lead me into this complicated topic. The implementation can be found in our contrib module: https://www.drupal.org/project/fontawesome_iconpicker_to_micon
Hard task: You can't simply change an existing field type
Drupal fields bring their own schema and data in fields is of course written into that schema. That explains, why field schema shouldn't be simply changeable without writing a migration.
As Drupal doesn't compare the field schema, simply changing the field type in config, for example a field.storage.*.*.yml is not allowed. When trying to do such a change and importing configuration, Drupal complains:
The import failed due to the following reasons:
Unexpected error during import with operation update for field.storage.node.field_icon: Cannot change the field type for an existing field storage. The field storage node.field_icon has the type string.
But also, it's not simply possible to change the field type in field classes. For example, FieldStorageConfig only allows to set the field type in the constructor.
And as Drupal serializes these objects in the entity.definitions.installed
key-value store, you can't even "hack it" the quick and dirty way (for god’s sake).
You can see this for example using this script:
dsm(\Drupal::keyValue('entity.definitions.installed')->get('node.field_storage_definitions'));
So this documentation page https://www.drupal.org/docs/drupal-apis/update-api/updating-entities-an… was a very important resource to finally figure out, how to implement these schema changes.
I won't go into detail here, as the documentation page already explains the parts very well. See the code in our module, how we solved it, as we only needed parts of it.
Other field schema changes are also hard to implement (DX)
Not only changing the field type is a hard task. Writing the drowl_paragraphs module, which adds a complex Drowl Paragraphs Settings field type, showed how hard it is to add / remove / modify field properties as Drupal core totally misses helpers for that.
If you need to implement something similar, see the core issue for code proposals: https://www.drupal.org/project/drupal/issues/937442
Other helpful resources for migrating field types
To solve this issue, I read and compared several blog posts and Stack Overflow questions, here are some helpful ones:
- https://www.liquidcms.ca/post/change-field-type-field-existing-data
- https://www.drupal.org/project/drupal/issues/2843108
- https://www.drupal.org/forum/support/module-development-and-code-questi…
- https://www.nikunj.dev/drupal-8-updating-field-configuration-update-hoo…
Others are only partially helpful or even outdated, but perhaps they may help in your specific case. Use with care:
- http://lobsterr.me/post/how-update-entity-field-drupal-8
- https://blog.42mate.com/change-field-type-with-existing-data-on-drupal-…
(Hopefully) upcoming solution: Core field schema helpers
This core issue: https://www.drupal.org/project/drupal/issues/937442
discusses to add field schema helpers to Drupal Core. That would help a lot to safe us from duplicate code and allows implementing short and comprehensive update hooks for such tasks. Please help yourself or sponsor developers to get this implemented! You may invest some of the time you saved here for that task ;)