4

I have several custom content entity types that have relationships to each 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, which is a feature request.

I need to define the relationship in the module with EntityViewData::getViewsData() or hook_views_data().

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 when the cardinality is higher than 1, or unlimited (where the field is created in a separate table).

None of the examples on the documentation pages (or that I have been able to find googling) defines a relationship between two entities that requires an intermediary table.

Views defines an EntityReverse class 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 the view 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 I could pass to getViewsData().

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

Does anyone know a solution?

3 Answers 3

8

After some trial and error, I have been able to find a solution.

In the example code for hook_views_data(), there was a relationship example that provided this snippet.

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

Having already discovered the EntityReverse class, I was pretty sure that I would need to use '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 getViewsData() in the ViewsData class I created for the related entity.

Then it dawned on me that the documentation for hook_views_data() says:

The return value of the getViewsData() method on the interface is the same as this hook

It means I can describe views data for any table and not just my entity's tables. Instead of trying to attach this relationship to the related entity, I tried attaching it to the main entity and this worked.

For example,

  • {MAIN ENTITY} is the entity being related

  • {RELATED ENTITY} is the entity with a relationship to {MAIN ENTITY} where {RELATED ENTITY} class has the following fields

       $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 {RELATED ENTITY} is the following.

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;
}
3
  • 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
    Commented Oct 23, 2018 at 22:34
  • Yes, that is correct
    – rjl
    Commented Nov 28, 2018 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
    Commented Feb 26, 2019 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.

0

After trying couple of hours, could able to connect custom entity "dashboards" with users table.

First write below code so yours custom entity would be available for view operations.

"dashboards" is the table name of the custom entity.

$data['dashboards']['table'][] = [];
$data['dashboards']['table']['group'] = t('Dashboards');
$data['dashboards']['table']['provider'] = 'custom_dashboards';
$data['dashboards']['table']['base'] = array(
  // Identifier (primary) field in this table for Views.
  'field' => 'id',
  // Label in the UI.
  'title' => t('Dashboards'),
  // Longer description in the UI. Required.
  'help' => t('Dashboards table.'),
  'weight' => -10,
);

Dashboard table have a user reference column called "uid". So now, make a relation with users table and dashboards table using below code, so a relationship can be added in view to show dashboard data associated with users.

  $data['users']['dashboard_data'] = [
    'title' => t('Dashboards content'),
    'help' => t('Relations with dashboards table'),
    'relationship' => [
      // Views name of the table being joined to from foo.
      'base' => 'dashboards_field_data',
      // Database field name in example_table for the join.
      'base field' => 'uid',
      // Real database field name in foo for the join, to override
      // 'unique_dummy_name'.
      'field' => 'uid',
      // ID of relationship handler plugin to use.
      'id' => 'standard',
      'label' => t('Dashboards content'),
    ],
  ];

full code given below

    public function getViewsData() {
      $data = parent::getViewsData();
      // Additional information for Views integration, such as table joins, can be
     // put here.
     $data['dashboards']['table'][] = [];
     $data['dashboards']['table']['group'] = t('Dashboards');
     $data['dashboards']['table']['provider'] = 'custom_dashboards';
     $data['dashboards']['table']['base'] = array(
     // Identifier (primary) field in this table for Views.
    'field' => 'id',
    // Label in the UI.
    'title' => t('Dashboards'),
    // Longer description in the UI. Required.
    'help' => t('Dashboards table .'),
    'weight' => -10,
);

  $data['users']['dashboard_data'] = [
    'title' => t('Dashboards content'),
    'help' => t('Relations with dashboards table'),
    'relationship' => [
      // Views name of the table being joined to from foo.
      'base' => 'dashboards_field_data',
      // Database field name in example_table for the join.
      'base field' => 'uid',
      // Real database field name in foo for the join, to override
      // 'unique_dummy_name'.
      'field' => 'uid',
      // ID of relationship handler plugin to use.
      'id' => 'standard',
      'label' => t('Dashboards content'),
    ],
  ];

    return $data;
    }

Now create a view of user content and add relationship with dashboards content.

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.