Problem/Motivation
Deprecate the migrate destination plugins used for migrating legacy Drupal sites.
There are 29 Migrate destination plugins, 12 are in the migrate module. That leaves 17 to check.
Commands to find the destination plugins that are not in core/modules/migrate
List
git grep -l "#\[MigrateDestination" | grep -v core/modules/migrate | nl
List all deprecated destination plugins
git grep -l "#\[MigrateDestination" | grep -v core/modules/migrate | xargs grep -H -c https://www.drupal.org/node/3533565 | grep :2 | awk -F: '{print $1}'
List all non deprecated destination plugins
git grep -l "#\[MigrateDestination" | grep -v core/modules/migrate | xargs grep -H -c https://www.drupal.org/node/3533565 | grep :0 | awk -F: '{print $1}'
Here are all the destination plugins, each of which needs a decision to keep or to deprecate.
- core/modules/ban/src/Plugin/migrate/destination/BlockedIp.php
- core/modules/block/src/Plugin/migrate/destination/EntityBlock.php
- core/modules/comment/src/Plugin/migrate/destination/EntityComment.php
- core/modules/comment/src/Plugin/migrate/destination/EntityCommentType.php
- core/modules/file/src/Plugin/migrate/destination/EntityFile.php
- core/modules/image/src/Plugin/migrate/destination/EntityImageStyle.php
- core/modules/language/src/Plugin/migrate/destination/DefaultLangcode.php
- core/modules/node/src/Plugin/migrate/destination/EntityNodeType.php
- core/modules/search/src/Plugin/migrate/destination/EntitySearchPage.php
- core/modules/shortcut/src/Plugin/migrate/destination/EntityShortcutSet.php
- core/modules/shortcut/src/Plugin/migrate/destination/ShortcutSetUsers.php
- core/modules/system/src/Plugin/migrate/destination/EntityDateFormat.php
- core/modules/system/src/Plugin/migrate/destination/d7/ThemeSettings.php
- core/modules/taxonomy/src/Plugin/migrate/destination/EntityTaxonomyVocabulary.php
- core/modules/user/src/Plugin/migrate/destination/EntityUser.php
- core/modules/user/src/Plugin/migrate/destination/EntityUserRole.php
- core/modules/user/src/Plugin/migrate/destination/UserData.php
Steps to reproduce
Proposed resolution
Keep
- core/modules/comment/src/Plugin/migrate/destination/EntityCommentType.php
- core/modules/file/src/Plugin/migrate/destination/EntityFile.php
- core/modules/image/src/Plugin/migrate/destination/EntityImageStyle.php
- core/modules/language/src/Plugin/migrate/destination/DefaultLangcode.php
- core/modules/shortcut/src/Plugin/migrate/destination/EntityShortcutSet.php
- core/modules/system/src/Plugin/migrate/destination/EntityDateFormat.php
- core/modules/taxonomy/src/Plugin/migrate/destination/EntityTaxonomyVocabulary.php
- core/modules/user/src/Plugin/migrate/destination/EntityUser.php
- core/modules/user/src/Plugin/migrate/destination/UserData.php
Deprecate the import() method
- core/modules/block/src/Plugin/migrate/destination/EntityBlock.php
- core/modules/comment/src/Plugin/migrate/destination/EntityComment.php
- core/modules/node/src/Plugin/migrate/destination/EntityNodeType.php
- core/modules/search/src/Plugin/migrate/destination/EntitySearchPage.php
Deprecate
- Drupal\migrate\Plugin\Derivative\MigrateEntityComplete
- Drupal\migrate\Plugin\migrate\destination\EntityContentComplete
- core/modules/shortcut/src/Plugin/migrate/destination/ShortcutSetUsers.php
- core/modules/system/src/Plugin/migrate/destination/d7/ThemeSettings.php
- core/modules/user/src/Plugin/migrate/destination/EntityUserRole.php
Other
Ban is deprecated, so no action needed.
- core/modules/ban/src/Plugin/migrate/destination/BlockedIp.php
Remaining tasks
Decide if more destination plugins should be deprecated.
User interface changes
Introduced terminology
API changes
Data model changes
Release notes snippet
Issue fork drupal-3502752
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 3502752-deprecate-migrate-destination
changes, plain diff MR !12581
Comments
Comment #2
quietone commentedComment #3
quietone commentedComment #4
benjifisherUpdated from discussion on the Migrate video call today. benjifisher, heddn, mikelutz and quietone were present. #3518542: [meeting] Migrate Meeting 2025-04-24 2100Z
Two of the blocking issues have been Fixed, and the third is Closed (won't fix).
Comment #5
quietone commentedComment #7
quietone commentedComment #8
quietone commentedComment #9
heddnI don't think we want to blindly deprecate all of the destinations. In some cases, we just want to remove the Drupal upgrade logic. Although that said, we might be able to completely deprecate because in theory the more generic entity destination should cover most cases. Let's see what this turns into with time.
Comment #10
quietone commentedComment #11
quietone commentedComment #12
smustgrave commentedCould we document in the summary why those not deprecated should remain?
Comment #13
quietone commentedComment #14
quietone commentedComment #15
quietone commentedComment #16
quietone commentedComment #17
xurizaemonUpdating ID so that list of retained destinations is clearer. Seems reasonable to deprecate
shortcut_set_users,d7_theme_settings,user_data.I think the entity destinations should be retained, Migrate is such a good general purpose toolkit I expect there are integrations using many of those.
Unsure if anyone might be destinating data to all of them, eg
entity:image_styleorentity:date_formatare harder to picture for non-upgrade migrations, but there's a scenario for almost everything!Comment #18
benjifisherI started looking at the individual destination plugins. I will have to finish my review some other time, but here are a couple of observations for now.
I think that
EntityContentComplete.phpwas added to support the "complete" node migrations. Even though it is in themigratemodule, I think we should consider deprecating it.Several of the destination plugins use
Row::getDestinationProperty(). That suggests that these plugins are coupled to the core migrations (intended for site upgrades). For example, inEntityImageStyle,I may be missing some, but at least these destination plugins call that method:
Ouch, that is a majority of them!
Comment #19
longwaveSome of those are false positives. There is no way we can remove
core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php- I imagine that is the most used destination plugin?!Comment #20
longwaveAs with the process plugins issue, I think we should err on the side of caution here and only deprecate ones that we know are no use. Almost all migrations that I write are for content only, and config is set up first outside of migrate. Therefore I suspect that the config entity destinations are largely not much use, but the content entity ones are probably all useful to someone - but perhaps we can decide on that in a followup or two.
I personally think UserData is useful in niche edge cases and believe I might have used it once on a project. I agree that the custom ShortcutSetUsers and ThemeSettings ones are unlikely to be useful in any non-Drupal migration.
Comment #21
benjifisherSame here. But I think we still have to consider each one separately. For example,
entity:taxonomy_vocabularyis a config migration, but I can see importing vocabularies (and then terms) from a CSV file (or other source) and the code looks like it is doing what a destination plugin is supposed to do: avoiding potential problems before saving the entity, in a way that does not depend on the source nor migration plugins:On the other hand,
entity:commentis a content migration, but itsimport()method is there to reset, then restore,$this->state->get('comment.maintain_entity_statistics', 0). I think entity statistics are maintained only when migrating from D6 or D7. OTOH, this looks appropriate:Of course. I only said that using
Row::getDestinationProperty()"suggests that these plugins are coupled to the core migrations" (emphasis added). If we look more closely, it does not hard-code any destination properties. For example,Maybe it is OK for a more specific destination plugin to hard-code a destination property, if that is also one of the entity keys.
Comment #22
quietone commentedI restored UserData and updated the issue summary
Comment #23
quietone commentedLike process plugin, let's deprecate the two here and open an new issue for more discussion.
For EntityContentComplete I'd like to hear from mikelutz as he was the one who designed that, but that would be for the possible followup.
Comment #24
benjifisherI am fixing some copy/paste errors in the issue summary.
Comment #25
benjifisherI am reviewing the 17 destination plugins, and I decided to start with the 5 that do not extend
Drupal\migrate\Plugin\migrate\destination\Entity;\Drupal\ban\Plugin\migrate\destination\BlockedIp\Drupal\language\Plugin\migrate\destination\DefaultLangcode\Drupal\shortcut\Plugin\migrate\destination\ShortcutSetUsers\Drupal\system\Plugin\migrate\destination\d7\ThemeSettings\Drupal\user\Plugin\migrate\destination\UserDataMost of these use some module-defined service to import the data. (The exception is
DefaultLangcode, which creates and saves a config object—not a config entity.) For example,BlockedIpincludesGenerally, a destination plugin following this pattern should be kept, not deprecated. Only 2 of the 5 deserve special attention:
ShortcutSetUsers@quietone, you added this one to the Deprecate list without comment. I think we should keep it. It follows the same pattern as
BlockedIp:We may decide to deprecate and remove the Shortcut module, but that decision is still pending: #3476880: [Policy] Move Shortcut module to contrib is still NR. Did you have another reason for deprecating it?
d7\ThemeSettingsTL;DR: I agree that we should deprecate this one, but the decision is not simple.
Despite the
d7in the namespace, this plugin is not closely tied to upgrades from D7. Theimportmethod doesunset()a few "pseudo field" destination properties, but mostly it is a wrapper fortheme_settings_convert_to_config(). At first, that seems fairly generic and possibly useful.Looking more closely at
theme_settings_convert_to_config(), its main job is to convert some legacy array keys to current config keys. For example:It looks as though, when the theme settings form was converted from D7 to D8, someone decided that it would be easier to use this helper function than to rewrite the form builder to match the desired config object. A little research (
git log -S theme_settings_convert_to_config) confirms this:hook_update_N). Later, when we realized that database update functions were not practical for the D7-to-D8 transition, the update function was replaced with a migration using this destination plugin.These two uses are reflected in the parameter comment for the current version of the helper function:
IMO, we should have rewritten the form builder long ago so that it does not need this helper function. @sun expressed the same opinion in #1712250-39: Convert theme settings to configuration system:
The
@todocomment was added, but then it was removed. It never got to-done.In the interest of eventually deprecating and removing
theme_settings_convert_to_config(), let’s not preserve it in this destination plugin. If it is used in only one place (the form submit handler) then it will be simpler to remove it.If anyone wants to use the migration system to import theme settings from a non-D7 source, then it is simpler to use the keys appropriate for the current config object. If they are already using the
d7_theme_settingsdestination plugin, then they could copy the code to their custom module once we remove it.Comment #26
benjifisherIn Comment #18, I suggested that using
Row::getDestinationProperty()was a hint that a destination plugin was too closely coupled to a particular migration. Then, in Comment #21, I suggested,Looking at the base classes, I think that is correct. Ideally, a destination plugin lists the supported properties in its
fields()method. For an entity plugin, those should be the entity properties. Then it is perfectly OK to usegetDestinationProperty()with a supported property.Comment #27
benjifisherOnly 3 of the 17 destination plugins are for content entities:
Drupal\comment\Plugin\migrate\destination\EntityCommentDrupal\file\Plugin\migrate\destination\EntityFileDrupal\user\Plugin\migrate\destination\EntityUserEntityCommentI already looked at this one in Comment #21. I think we should keep it for
processStubRow(), but I think we should have a followup issue to remove theimport()method.EntityFileThis one does two useful things. In
import(), it loads the entity byuri(one of the entity properties) instead offid. InprocessStubRow(), it creates an empty file for each stub file entity.Keep it.
EntityUserThis one is mostly about handling passwords, and there are a few lines giving special treatment to
user/1.Keep it.
Comment #28
benjifisherI finished my review with the 9 destination plugins for config entities:
Drupal\block\Plugin\migrate\destination\EntityBlockDrupal\comment\Plugin\migrate\destination\EntityCommentTypeDrupal\image\Plugin\migrate\destination\EntityImageStyleDrupal\node\Plugin\migrate\destination\EntityNodeTypeDrupal\search\Plugin\migrate\destination\EntitySearchPageDrupal\shortcut\Plugin\migrate\destination\EntityShortcutSetDrupal\system\Plugin\migrate\destination\EntityDateFormatDrupal\taxonomy\Plugin\migrate\destination\EntityTaxonomyVocabularyDrupal\user\Plugin\migrate\destination\EntityUserRoleThe base class implements
fields()to return an empty array, and I do not think any of these plugins override that. Now that we have config validation, I think we should use it: the top-level keys undermappingshould be the fields, and the corresponding labels can be the values. This idea is so far out of scope for this issue that I do not think it counts as a followup issue. It is just something that we should do to improve the migration system.Drupal\block\Plugin\migrate\destination\EntityBlockThis plugin overrides
getEntity()to load the block by plugin ID and theme instead of entity ID. I think we should keep it.It also overrides
import()to re-throwSchemaIncompleteException. This was added in #3265483: Handle block migration for modules moved to contrib to deal with blocks provided by modules that used to be in core but have been removed. I think we should remove this in a follow-up issue. It should be the responsibility of the source plugin and the migration to send only valid rows to the destination plugin.Drupal\comment\Plugin\migrate\destination\EntityCommentTypeThis plugin overrides
import()to add a Body field to the comment type:As long as there is a
commentmodule in core, and it provides a service to do that, I think we should keep this plugin.Drupal\image\Plugin\migrate\destination\EntityImageStyleThis plugin adds image effects using
$style->addImageEffect($effect)so that the effect plugin can be initialized. Keep it.Drupal\node\Plugin\migrate\destination\EntityNodeTypeThis plugin overrides
getEntity()because the config schema does not allow description or help text to be empty strings. Keep it.It also overrides
import()to add a Body field, if thecreate_bodydestination property is set. I think we should remove that in a followup issue.Drupal\search\Plugin\migrate\destination\EntitySearchPageThis plugin overrides
updateEntity()to invoke$entity->setPlugin()and$entity->getPlugin()->setConfiguration(). I think we should keep it.It also overrides
import()to check that the module providing the search page is enabled. Again, I think the source plugin and the migration should be responsible for checking that. Let’s remove this method in a followup issue.Drupal\shortcut\Plugin\migrate\destination\EntityShortcutSetThis plugin overrides
getEntity()in order to do this:I am not sure what problem this is supposed to solve, nor how it works. It sort of looks like something that comes up only when upgrading from D6/D7, so maybe we should deprecate this one.
Drupal\system\Plugin\migrate\destination\EntityDateFormatThis plugin overrides
updateEntityProperty(). In the parent class, that method handles nested properties usingNestedArray::setValue(). In the override, it uses the dedicatedDateFormatInterface::setPattern()method when appropriate. I think we should keep this one.Drupal\taxonomy\Plugin\migrate\destination\EntityTaxonomyVocabularyI described this plugin in Comment #21. I think we should keep it.
Drupal\user\Plugin\migrate\destination\EntityUserRoleThis plugin overrides
import()to filter out invalid permissions. If we do not do that in the destination plugin, then they will be filtered out when we save the entity (the role). The only difference is that theusermodule will log an error instead of the migration system adding a warning to the migration message table.I think this is another case where the source plugin and the migration should be responsible for sending valid data to the destination plugin. Let’s deprecate this one.
Comment #30
samitk commentedRebased with 11.x branch.
Comment #31
quietone commented@samit.310@gmail.com, thanks for keeping this MR up to date. I am not sure why this is at needs review. Were there any problems with the rebase that we should be aware of?
This still requires work for the comments from @benjifisher, starting from #25. So I am returning this to NW.
Comment #32
benjifisherLet me summarize the recommendations from my previous comments:
Deprecate
EntityContentComplete(in themigratemodule)EntityShortcutSetd7\ThemeSettingsEntityUserRoleDo not deprecate
ShortcutSetUsersFollowup
In an issue targeting Drupal 12 (perhaps at the same time that the deprecated code is removed),
EntityComment::import().EntityBlock::import().EntityNodeType::import().EntitySearchPage::import().Comment #33
quietone commented@benjifisher, thanks for the summary!
A few things. The summary doesn't mention EntityShortcutSet, which is on the list for deprecation. Is that an oversight?
Instead of simply removing the ones in the 'Followup' section I think they should be deprecated. And, this issue seems like a good time to do that.
Oh, and EntityContentComplete has a deriver which is used by /EntityContentComplete, so deprecated that.
Comment #34
benjifisher@quietone:
Yes, I put
EntityShortcutSeton the wrong list. I just updated Comment #32, moving it to the Deprecate list. Thanks for catching that.Good idea: let's deprecate the
import()methods on the Followup list.+1 for deprecating the deriver along with the
EntityContentCompleteplugin.I will have a look at the code later today.
Comment #35
benjifisherThe deprecated classes look good to me.
This issue still NW to deprecate the four
import()methods. (See Comments #32-#34.)Comment #36
quietone commentedMade those changes and also updated the change record,.
Comment #37
benjifisher@quietone:
Thanks for the updates, including the change record. I am updating the issue summary, now that we have agreed on the deprecations.
In Comment #35, I missed something: when adding a constructor to a class that did not have one, we should include a doc block. I added suggestions to the MR for three files. NW for that.
I also reviewed the deprecations for the
import()methods. They all look good.Comment #38
quietone commentedThanks for the issue summary update and review.
I didn't add docs because it is not required for constructors, so lets skip it.
From, https://project.pages.drupalcode.org/coding_standards/php/documentation/...
Comment #39
benjifisherIn that case, I think the MR is ready.
Comment #41
catchThis looks good, balance of which ones to keep vs. remove seems OK and we can always deprecate more individual plugins later.
Committed/pushed to 11.x and cherry-picked to 11.3.x, thanks!
Comment #46
quietone commentedReviewed and published the change record