Problem/Motivation
Discovered via #2814953: Migrate Drupal 7 node/user reference fields, but is a long pre-existing bug.
#2814953-96: Migrate Drupal 7 node/user reference fields contains:
+++ b/core/modules/field/src/Plugin/migrate/process/d7/FieldInstanceSettings.php
@@ -65,6 +65,43 @@ public function transform($value, MigrateExecutableInterface $migrate_executable
+ 'target_bundles' => array_filter($field_data['settings']['referenceable_types'] ?? []),
Yet … field_field_config_presave() does this:
// In case we removed all the target bundles allowed by the field in
// EntityReferenceItem::onDependencyRemoval() or field_entity_bundle_delete()
// we have to log a critical message because the field will not function
// correctly anymore.
$handler_settings = $field->getSetting('handler_settings');
if (isset($handler_settings['target_bundles']) && $handler_settings['target_bundles'] === []) {
\Drupal::logger('entity_reference')->critical('The %field_name entity reference field (entity_type: %entity_type, bundle: %bundle) no longer has any valid bundle it can reference. The field is not working correctly anymore and has to be adjusted.', [
'%field_name' => $field->getName(),
'%entity_type' => $field->getTargetEntityTypeId(),
'%bundle' => $field->getTargetBundle(),
]);
}
IOW, target_bundles must either be NULL or if it's an array, it must be non-empty. Otherwise all references will be refused!
Steps to reproduce
node_reference field without referenceable_types setting triggers something like this during the migration:

Proposed resolution
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
Comments
Comment #2
wim leersThis happens for two content types on
training.acquia.com, both of which do not specifyreferenceable_typesand hence get the[]fallback, which is wrong as explained in the issue summary.Comment #3
wim leersComment #4
quietone commentedAre we sure it should be NULL? On D9.2.x when configuring a content reference field, the 'Content types' is a required field. Maybe it should be an array of all the referenceable_types available on the source site. I mean NULL may work but what is shown in the UI would not be.
Comment #5
matroskeenIf missing
referenceable_typesproperty means "allow all bundles", thenNULLis fine. Otherwise, it might end up with some unexpected behavior.Let's say on the source site the reference field allowed bundle "A" to be referenced.
However, on the destination, bundle "A" is not available at the moment.
Given that, the reference field might be empty, because we can't just "allow all", can we?
Comment #6
wim leersAll of this, plus the fact that bundles may be renamed on the destination, leads me to one obvious suggestion: allow all bundles on the destination site. That's the intent of the setting on the source, and it's the equivalent correct configuration on the destination.
IOW: I proposed
NULLbecause it's the simplest possible solution, and the migration process plugin already does that forentityreferencesource field type migrations. But you're right that it could (should) be even better. That just requires more work.Comment #7
quietone commentedI tested on D7 and when no content types are selected it
is notpossible to enter a value in the node reference field. On D8 when no content types are selected, when target_bundles = NULL, it is possible to enter a value in a node reference field. If I haven't made a mistake there then we can't replicate the behaviour.I had made a patch that uses NULL, as suggested, and the process plugin now does a migrate lookup to get the destination values for the node_types.
Comment #9
wim leersFYI @huzooka has had to tackle similar problems in https://www.drupal.org/project/media_migration — I think the relevant code is at
\Drupal\media_migration\Plugin\migrate\process\MediaImageFieldInstanceSettings::transform(), but I'll let him confirm and elaborate 🤓Comment #10
quietone commentedUse the process plugin test instead of a migration test and change it to use a data provider. No changes to the fix.
Comment #12
wim leersAmazing progress here! 🥳
Comment #13
huzookaWith patch #10,
d7_field_instancemigration need a hard dependency ond7_node_type.Comment #14
wim leers#13: good point. In fact, it will need a required dependency on every migration that targets a config entity type that represents the bundles for a particular destination entity type. Not just
node_type. So alsod7_taxonomy_vocabularyin core, but more still in contrib.Ideally, we can automatically compute this. Otherwise many contrib migrations (for contrib entity types) are likely to get this wrong.
Comment #15
wim leersNULLshould be replaced with an array listing all node type IDs, otherwise it's still invalid.Comment #16
wim leersTo follow up on #15: I wanted to be 100% certain about this.
So I went ahead and created a new
entity_referencefields withnodeas the target entity type. Upon doing so, I end, the followingfield.field.node.course.field_test.ymlwas generated:… which does match the current migration system behavior.
However, if you try to save this at
/admin/structure/types/manage/course/fields/node.course.field_test, then you'll get this:The form validation logic does the right thing: it matches the
field_field_config_presave()logic (which was introduced in #2925913: EntityReferenceItem logs critical errors in onDependencyRemoval()).The problem is that first
field.storage.node.field_test.ymlgets generated, and a placeholderfield.field.node.course.field_test.ymlgets generated. You're supposed to click on the above screenshot. But nothing is forcing you to. But that is a bug/weakness in Field UI (for having such a brittle UX) and in Field API (for not warning about this, anywhere, not even on the Status Report, where it happily reports 😔).Overall: a pretty messy situation. But at least for the migration system, the solution is pretty simple (will work on that now). For Field UI/Field API the solution is less clear. And definitely out of scope.
Comment #17
wim leersThis fixes #15 + #16.
Comment #18
wim leersInterdiff for #17.
Comment #19
wim leersSimilar (but different) problem for
user_referencefields:The problems here are that the roles that are being set are labels instead of the config ID — we need to use the processed role ID (i.e. it must undergo the same processing as
d7_user_role.ymlspecifies).Furthermore, in D7 it's possible to configure it to only allow the
authenticated user. But … that is expressly forbidden in D8: you have to either pick a specific user role. Besides, the equivalent of "only authenticated" is "not anonymous". And for that, D8|9 gained an explicit setting:include_anonymous(since #2434697: Remove UserAutocompleteController).So, incorporating all newfound processing knowledge that in this interdiff.
Comment #22
wim leersTo get a
[article ⇒ article, blog ⇒ blog]-style result here, we should usearray_combine().Running these tests fails locally for me 😬
Between this and #3197324: Exception trace cannot be serialized because of closure, running core migration tests is … mildly maddening 😅 Pointers welcome!
user_reference, but the fact thatinclude_anonymousremainsTRUEsuggests that we really need to fix #13 too.Comment #24
srishtiiee commentedComment #25
wim leersThanks, @srishti.bankar, for addressing those first 2 failures in #19, and oddly enough now there's a different set of failures! 😳
Will re-investigate
Comment #26
quietone commented@srishti.bankar, thanks for the patch. There are changes to the source Drupal 7 fixture in the patch, what are those changes doing? Why are they needed?
The issue summary needs an update. @Wim Leers, can you summarize your analysis in the IS? Particularly the differences between D7 and beyond?
Comment #28
narendrarPatch #19 rerolled to 9.3.x
However while creating interdiff, I get below error and interdiff file was empty.
Hence attaching rerolled patch only.
Comment #29
wim leersYou omitted the changes to
MigrateDrupal7TestBase.phpand the fixture, @narendraR. Why?Comment #30
narendrarThe test changes in #24 are not included in #28. #28 is rerolled over #19.
Comment #33
wim leers#3319582: Fix calls to methods with too many parameters passed in conflicted with this, but also allows slightly simplifying this 👍
Comment #36
ovilla commentedThis is a reroll of #33 for Drupal 10.3
Comment #37
acbramley commentedThis came up in BSI. It needs a reroll on to an MR against 11.x
Comment #41
jofitzMy commit fixes an issue in the unit test, but highlights another issue; what is the expected value of
target_bundlesin the'node_reference no content types'test? My understanding is that it should be NULL, therefore we need to reinstate$target_bundles = empty($target_bundles) ? NULL : $target_bundles;while retaining the precedingelsecondition.@wim leers if you can dredge this from your memory then I would appreciate your opinion, thanks.
(no longer needs reroll)