1

I have several custom content entity types that have relationships to one another. The fields used are defined as base fields and I am using $fields['FIELD_NAME'] = BaseFieldDefinition::create('entity_reference')

Views doesn't currently implement reverse relationships for base fields, it is a feature request https://www.drupal.org/node/2706431

So you need to define the relationship in your module with the getViewsData() method of the EntityViewData class https://api.drupal.org/api/drupal/core%21modules%21views%21src%21EntityViewsData.php/class/EntityViewsData/8.2.x or hook_views_data() https://api.drupal.org/api/drupal/core%21modules%21views%21views.api.php/function/hook_views_data/8.2.x

Using the examples provided, I was able to define a reverse relationship for an entity_reference field with cardinality of 1 (where the field is created in the entity's base_table). But I can't figure out how to do it with a cardinality > 1 or unlimited (where the field is created in a separate table).

None of the examples on those pages above (or that I have been able to find with the google) defines a relationship between 2 entities that requires an intermediary table.

Views defines an EntityReverse class https://api.drupal.org/api/drupal/core%21modules%21views%21src%21Plugin%21views%21relationship%21EntityReverse.php/8.2.x that I have looked at for ideas. It has a $first_join and a $second_join. I can confirm that if the field is added to the entity using the normal UI, then views provides both relationships: to the related entity and the reverse relationship from the related entity.

I am not sure at all how to translate the EntityReverse class into a definition that views would expect getViewsData().

And How to do a Reverse Entity Reference? is not the answer.

Anyone know a solution? or a place to point me for more research?

6

After some trial and error... Success! (sorry to post my own answer - hopefully someone else finds it useful)

In the example code of hook_views_data there was a relationship example that provided this snippet

// ID of relationship handler plugin to use.
'id' => 'standard',

Having already discovered views EntityReverse class, I was pretty sure that I would need

'id' => 'entity_reverse',

The part that tripped me up the most was that I was trying to define the relationship as part of the related entity inside the getViewsData() method of the ViewsData class I had created for the related entity.

Then it dawned on me, the documentation for hook_views_data says "The return value of the getViewsData() method on the interface is the same as this hook" which means I can describe views data for any table and not just my entity's tables. So instead of trying to attach this relationship to the related entity, I tried attaching it to the main entity and Voila, Success...

Example...

{MAIN ENTITY} = the entity being related to

{RELATED ENTITY} = the entity with a relationship to the {MAIN ENTITY} where the {RELATED ENTITY} class has

$fields['{FIELD NAME}'] = BaseFieldDefinition::create('entity_reference')
  ->setSetting('target_type', '{MAIN ENTITY}')
  ->setCardinality(FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) // or a specific number higher than 1
  ->...

The getViewsData() method of the ViewsData class for the {RELATED ENTITY}

public function getViewsData() {

  $data = parent::getViewsData();

  $data['{MAIN ENTITY BASE TABLE}']['{useful non-conflicting name - I used the {RELATED ENTITY BASE TABLE}}'] = [
    'title' => t('{some useful title that displays in Add Relationships popup}'),
    'help' => t('{some useful description that displays in Add Relationships popup}'),
    'relationship' => [
      'field_name' => '{name of the field - same as {FIELD_NAME}}',
      'field table' => '{name of intermediary table - it is probably {RELATED ENTITY}__{FIELD NAME}',
      'field field' => '{name of intermediary field - it is probably {FIELD NAME}_target_id',
      'base' => '{RELATED ENTITY BASE TABLE}',
      'base field' => '{RELATED ENTITY ID FIELD}',
      'id' => 'entity_reverse',
      'label' => '{some useful label that displays in Relationship select in various views field settings popups}',
    ],
  ];

  return $data;
}
  • This looks very useful. Just to confirm the terminology, "{MAIN ENTITY} " refers to "target_entity", while "{RELATED ENTITY}" refers to the entity with the entity reference field. – imclean Oct 23 '18 at 22:34
  • Yes, that is correct – rjl Nov 28 '18 at 23:26
  • I'm trying out this idea, but I find that the {useful non-conflicting name - I used the {RELATED ENTITY BASE TABLE}} is used by Views as the field name from the base table, so there is a failure in the sql unless I use the real field name...and then I lose the relationships available for the real field. – greggles Feb 26 at 15:50
0

There is a patch to provide reverse relationships by default at https://www.drupal.org/project/drupal/issues/2706431

I've tried it and it works, though before it gets into core it requires a bit more work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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