6

I am doing a migration right now and have a need to maintain how entities are related in a way that the core entity reference field does not seem to support.

Lets say I have two content types, Foo and Bar. I am migrating Foo first, and Bar gets stubbed.

I need to relate Foo to Bar, but the relationship is not one way. Bar is also related to Foo, but there is no way (when editing Bar) to see this relationship. If Foo or Bar are deleted, it needs to unlink that.

Does Drupal 8 do that yet? How would you effectively do this? Do you need to create a custom entity to hold links?

I looked at the Dynamic Entity Reference module and it does not seem to do this. I also looked at the Corresponding Entity Reference module and it looks dead with a lot of logged bugs (last release 2017).

2
  • Sounds like a thing the cer module could do but as you said it's not working.. If you don't mind a custom solution, some code to update the referenced node when the other was updated/created/deleted should do it. So after you migrate, a custom submit handler on the linked content types to manage creations/updates and then on hook_entity_delete to handle deletion of the referenced content relation.
    – seroton
    Commented Mar 21, 2019 at 17:44
  • There are answers, that suggest to use 2 entity reference fields and sync them. But I do not find this solution robust. In my case I have 13 content types, that can be related to each other. I think a better solution would be to develop a new field type. There is Drupal 7 project Relation for this issue, but no Drupal 8 solution yet.
    – milkovsky
    Commented Oct 16, 2020 at 7:10

2 Answers 2

2

I would use 2 entity reference field, one in each content type referencing the other. With some custom logic, you could add the desired behavior, eg remove reference Foo -> Bar when the user removes Bar -> Foo.

Regarding the migation, you can run the first migration twice with the --update flag the 2nd time:

  • Step 1. Migrate Foo. The migration_lookup plugin will not find any values for the entity reference so it will be empty.
  • Step 2. Migrate Bar. Foo already exists, you should have no problem with the entity reference.
  • Step 3. Migrate Foo again, with the --update flag. Bar already exists, so the entity reference field will filled.
2
  • 1
    We're talking over 100,000+ imports though. That is a lot to do multiple times to fill in all the relation gaps.
    – Kevin
    Commented Mar 21, 2019 at 19:27
  • There should be no problem with migration in one run. Drupal will save the IDs in the entity relationship field table, even if the entities do not exist yet. You will see the related entities in CMS once you migrate them.
    – milkovsky
    Commented Oct 16, 2020 at 7:05
0

Does Drupal 8 do that yet?

Not that I know of.

How would you effectively do this?

I would use hook_ENTITY_TYPE_presave and sprinkle some custom code, like the following simple example:

function MODULE_node_presave(Drupal\node\NodeInterface $node) {
    if ($node->bundle() == 'article') {
      $node2 = Node::load($node->field_reference->value); // load referenced node.
      $node2->field_reference->value = $node->id(); // reference back original node.
      $node2->save(); // save referenced node.
    }
  }
}
  • If they are different content types the above is fine.

  • If they are the same content type, then watch out for infinite loop! Add a condition to check if the reference already exists, or save directly to DB to prevent another presave hook from firing.

I believe when you delete node, Drupal automatically deletes references, but double check on that by looking at the DB.

You will also want to cover the scenario when a reference gets removed via node update, to do that use:

function MODULE_node_presave(Drupal\node\NodeInterface $node) {
    if ($node->bundle() == 'article') {
      if (!empty($node->original) && 
         $node->original->field_reference->value != $node->field_reference->value) { // check if it changed
            $node2 = Node::load($node->original->field_reference->value); // load removed referenced node.
            $node2->field_reference->value = ''; // remove reference back to original node.
            $node2->save(); // save removed referenced node.
      }
   }
}

Obviously all the above is the most basic scenario, if you allow for multiple values, you'll have to work with loops and arrays and perhaps with even with the Batch API if there are lots of nodes that reference the same node.


That said, the above is only necessary in special use cases, where the owner wants to be able to link or unlink from either end. Most of the time one way is enough, as you can easily get the referencing node info via views relationship, twig, or DB query.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.