3

I want to create a two-way relationship between a field in a custom entity (package_date) and the field in a content type (package).

I have been able to add a relationship from a view for package_date, but it only allows me to add the linked field to the view, not any other package_date field. When the view is saved, I get this error.

No entity type for field field_package_id on view package_dates in Drupal\views\Plugin\views\HandlerBase->getEntityType() (line 697 of core\modules\views\src\Plugin\views\HandlerBase.php)

The package_date entity definition is the following.

namespace Drupal\my_module\Entity;

use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\EntityChangedTrait;

/**
 * Defines the PackageDate entity.
 *
 * @ingroup package_date
 *
 * @ContentEntityType(
 *   id = "package_date",
 *   label = @Translation("Package Date"),
 *   handlers = {
 *     "view_builder" = "Drupal\Core\Entity\EntityViewBuilder",
 *     "views_data" = "Drupal\views\EntityViewsData",
 *   },
 *   base_table = "package_date",
 *   entity_keys = {
 *     "id" = "id",
 *     "uuid" = "uuid",
 *   },
 *   admin_permission = "administer package_date entity",
 *   fieldable = true,
 * )
 */

class PackageDate extends ContentEntityBase {

    use EntityChangedTrait;

  /**
   * {@inheritdoc}
   *
   * Define the field properties here.
   *
   */
  public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {

        // Standard field, used as unique if primary index.
        $fields['id'] = BaseFieldDefinition::create('integer')
            ->setLabel(t('ID'))
            ->setDescription(t('The ID of the Term entity.'))
            ->setReadOnly(TRUE);

        // Standard field, unique outside of the scope of the current project.
        $fields['uuid'] = BaseFieldDefinition::create('uuid')
            ->setLabel(t('UUID'))
            ->setDescription(t('The UUID of the Contact entity.'))
            ->setReadOnly(TRUE);

    $fields['package_id'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Package ID'))
      ->setDisplayOptions('view', array(
        'label' => 'above',
        'type' => 'integer',
        'weight' => 0,
      ))
      ->setDisplayConfigurable('view', TRUE);

        $fields['start_datetime'] = BaseFieldDefinition::create('datetime')
        ->setLabel('Package Start Time')
        ->setDisplayOptions('view', array(
            'label' => 'above',
                'type' => 'datetime',
                'weight' => 1,
        ))
        ->setSetting('datetime_type', 'datetime')
        ->setSetting('timezone_override', '');

        $fields['end_datetime'] = BaseFieldDefinition::create('datetime')
        ->setLabel('Package End Time')
        ->setDisplayOptions('view', array(
            'label' => 'above',
                'type' => 'datetime',
                'weight' => 2,
        ))
        ->setSetting('datetime_type', 'datetime')
        ->setSetting('timezone_override', '');

    $fields['price'] = BaseFieldDefinition::create('decimal')
      ->setLabel(t('Package Price'))
      ->setSettings(array(
          'precision' => 10,
          'scale' => 4,
          'decimal_separator' => '.',
      ))
      ->setDisplayOptions('view', array(
        'label' => 'above',
        'type' => 'decimal',
        'weight' => 0,
      ))
      ->setDisplayConfigurable('view', TRUE);

    $fields['capacity'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Capacity'))
      ->setDisplayOptions('view', array(
        'label' => 'above',
        'type' => 'integer',
        'weight' => 0,
      ))
      ->setDisplayConfigurable('view', TRUE);

    $fields['availability'] = BaseFieldDefinition::create('integer')
      ->setLabel(t('Availability'))
      ->setDisplayOptions('view', array(
        'label' => 'above',
        'type' => 'integer',
        'weight' => 0,
      ))
      ->setDisplayConfigurable('view', TRUE);

    $fields['created'] = BaseFieldDefinition::create('created')
      ->setLabel(t('Created'))
      ->setDescription(t('The time that the entity was created.'));

    $fields['changed'] = BaseFieldDefinition::create('changed')
      ->setLabel(t('Changed'))
      ->setDescription(t('The time that the entity was last edited.'));

    return $fields;
  }
}

The hook_view_data() implementation uses the following code.

function my_module_views_data() {
  $data['package_date']['table']['group'] = t('Package Dates');

  $data['package_date']['table']['join'] = array(
    // Index this array by the table name to which this table refers.
    // 'left_field' is the primary key in the referenced table.
    // 'field' is the foreign key in this table.
    'node__field_package_id' => array(
      'left_field' => 'field_package_id_value',
      'field' => 'package_id',
    ),
  );

  $data['package_date']['package_id'] = array(
    'title' => t('Package ID'),
    'relationship' => array(
      'base' => 'node__field_package_id', // The name of the table to join with.
      'base field' => 'field_package_id_value', // The name of the field on the joined table.
      // 'field' => 'package_id' -- see hook_views_data_alter(); not needed here.
      'handler' => 'views_handler_relationship',
      'label' => t('Relationship between Package and Package Dates'),
      'title' => t('Package to Package Dates Relationship'),
      'id' => 'standard',
    ),
  );

  return $data;
}

The exported view is the following one.

langcode: en
status: true
dependencies:
  config:
    - system.menu.admin
  module:
    - datetime
    - pve
    - user
id: _package_dates
label: 'Package Dates'
module: views
description: 'Display current cache of  Package Dates'
tag: ''
base_table: _package_date
base_field: id
core: 8.x
display:
  default:
    display_plugin: default
    id: default
    display_title: Master
    position: 0
    display_options:
      access:
        type: perm
        options:
          perm: 'access site reports'
      cache:
        type: tag
        options: {  }
      query:
        type: views_query
        options:
          disable_sql_rewrite: false
          distinct: false
          replica: false
          query_comment: ''
          query_tags: {  }
      exposed_form:
        type: basic
        options:
          submit_button: Apply
          reset_button: false
          reset_button_label: Reset
          exposed_sorts_label: 'Sort by'
          expose_sort_order: true
          sort_asc_label: Asc
          sort_desc_label: Desc
      pager:
        type: full
        options:
          items_per_page: 50
          offset: 0
          id: 0
          total_pages: null
          tags:
            previous: ‹‹
            next: ››
            first: '« First'
            last: 'Last »'
          expose:
            items_per_page: false
            items_per_page_label: 'Items per page'
            items_per_page_options: '5, 10, 25, 50'
            items_per_page_options_all: false
            items_per_page_options_all_label: '- All -'
            offset: false
            offset_label: Offset
          quantity: 9
      style:
        type: table
      row:
        type: fields
      fields:
        _package_id:
          table: _package_date
          field: _package_id
          id: _package_id
          entity_type: null
          entity_field: _package_id
          plugin_id: field
          relationship: none
          group_type: group
          admin_label: ''
          label: 'Package ID'
          exclude: false
          alter:
            alter_text: false
            text: ''
            make_link: false
            path: ''
            absolute: false
            external: false
            replace_spaces: false
            path_case: none
            trim_whitespace: false
            alt: ''
            rel: ''
            link_class: ''
            prefix: ''
            suffix: ''
            target: ''
            nl2br: false
            max_length: 0
            word_boundary: true
            ellipsis: true
            more_link: false
            more_link_text: ''
            more_link_path: ''
            strip_tags: false
            trim: false
            preserve_tags: ''
            html: false
          element_type: ''
          element_class: ''
          element_label_type: ''
          element_label_class: ''
          element_label_colon: true
          element_wrapper_type: ''
          element_wrapper_class: ''
          element_default_classes: true
          empty: ''
          hide_empty: false
          empty_zero: false
          hide_alter_empty: true
          click_sort_column: value
          type: number_integer
          settings: {  }
          group_column: value
          group_columns: {  }
          group_rows: true
          delta_limit: 0
          delta_offset: 0
          delta_reversed: false
          delta_first_last: false
          multi_type: separator
          separator: ', '
          field_api_classes: false
        start_datetime:
          id: start_datetime
          table: _package_date
          field: start_datetime
          relationship: none
          group_type: group
          admin_label: ''
          label: 'Package Start Time'
          exclude: false
          alter:
            alter_text: false
            text: ''
            make_link: false
            path: ''
            absolute: false
            external: false
            replace_spaces: false
            path_case: none
            trim_whitespace: false
            alt: ''
            rel: ''
            link_class: ''
            prefix: ''
            suffix: ''
            target: ''
            nl2br: false
            max_length: 0
            word_boundary: true
            ellipsis: true
            more_link: false
            more_link_text: ''
            more_link_path: ''
            strip_tags: false
            trim: false
            preserve_tags: ''
            html: false
          element_type: ''
          element_class: ''
          element_label_type: ''
          element_label_class: ''
          element_label_colon: true
          element_wrapper_type: ''
          element_wrapper_class: ''
          element_default_classes: true
          empty: ''
          hide_empty: false
          empty_zero: false
          hide_alter_empty: true
          click_sort_column: value
          type: datetime_default
          settings:
            timezone_override: ''
            format_type: medium
          group_column: value
          group_columns: {  }
          group_rows: true
          delta_limit: 0
          delta_offset: 0
          delta_reversed: false
          delta_first_last: false
          multi_type: separator
          separator: ', '
          field_api_classes: false
          entity_type: _package_date
          entity_field: start_datetime
          plugin_id: field
        end_datetime:
          id: end_datetime
          table: _package_date
          field: end_datetime
          relationship: none
          group_type: group
          admin_label: ''
          label: 'Package End Time'
          exclude: false
          alter:
            alter_text: false
            text: ''
            make_link: false
            path: ''
            absolute: false
            external: false
            replace_spaces: false
            path_case: none
            trim_whitespace: false
            alt: ''
            rel: ''
            link_class: ''
            prefix: ''
            suffix: ''
            target: ''
            nl2br: false
            max_length: 0
            word_boundary: true
            ellipsis: true
            more_link: false
            more_link_text: ''
            more_link_path: ''
            strip_tags: false
            trim: false
            preserve_tags: ''
            html: false
          element_type: ''
          element_class: ''
          element_label_type: ''
          element_label_class: ''
          element_label_colon: true
          element_wrapper_type: ''
          element_wrapper_class: ''
          element_default_classes: true
          empty: ''
          hide_empty: false
          empty_zero: false
          hide_alter_empty: true
          click_sort_column: value
          type: datetime_default
          settings:
            timezone_override: ''
            format_type: medium
          group_column: value
          group_columns: {  }
          group_rows: true
          delta_limit: 0
          delta_offset: 0
          delta_reversed: false
          delta_first_last: false
          multi_type: separator
          separator: ', '
          field_api_classes: false
          entity_type: _package_date
          entity_field: end_datetime
          plugin_id: field
        availability:
          id: availability
          table: _package_date
          field: availability
          relationship: none
          group_type: group
          admin_label: ''
          label: Availability
          exclude: false
          alter:
            alter_text: false
            text: ''
            make_link: false
            path: ''
            absolute: false
            external: false
            replace_spaces: false
            path_case: none
            trim_whitespace: false
            alt: ''
            rel: ''
            link_class: ''
            prefix: ''
            suffix: ''
            target: ''
            nl2br: false
            max_length: 0
            word_boundary: true
            ellipsis: true
            more_link: false
            more_link_text: ''
            more_link_path: ''
            strip_tags: false
            trim: false
            preserve_tags: ''
            html: false
          element_type: ''
          element_class: ''
          element_label_type: ''
          element_label_class: ''
          element_label_colon: true
          element_wrapper_type: ''
          element_wrapper_class: ''
          element_default_classes: true
          empty: ''
          hide_empty: false
          empty_zero: false
          hide_alter_empty: true
          click_sort_column: value
          type: number_integer
          settings:
            thousand_separator: ''
            prefix_suffix: true
          group_column: value
          group_columns: {  }
          group_rows: true
          delta_limit: 0
          delta_offset: 0
          delta_reversed: false
          delta_first_last: false
          multi_type: separator
          separator: ', '
          field_api_classes: false
          entity_type: _package_date
          entity_field: availability
          plugin_id: field
        capacity:
          id: capacity
          table: _package_date
          field: capacity
          relationship: none
          group_type: group
          admin_label: ''
          label: Capacity
          exclude: false
          alter:
            alter_text: false
            text: ''
            make_link: false
            path: ''
            absolute: false
            external: false
            replace_spaces: false
            path_case: none
            trim_whitespace: false
            alt: ''
            rel: ''
            link_class: ''
            prefix: ''
            suffix: ''
            target: ''
            nl2br: false
            max_length: 0
            word_boundary: true
            ellipsis: true
            more_link: false
            more_link_text: ''
            more_link_path: ''
            strip_tags: false
            trim: false
            preserve_tags: ''
            html: false
          element_type: ''
          element_class: ''
          element_label_type: ''
          element_label_class: ''
          element_label_colon: true
          element_wrapper_type: ''
          element_wrapper_class: ''
          element_default_classes: true
          empty: ''
          hide_empty: false
          empty_zero: false
          hide_alter_empty: true
          click_sort_column: value
          type: number_integer
          settings:
            thousand_separator: ''
            prefix_suffix: true
          group_column: value
          group_columns: {  }
          group_rows: true
          delta_limit: 0
          delta_offset: 0
          delta_reversed: false
          delta_first_last: false
          multi_type: separator
          separator: ', '
          field_api_classes: false
          entity_type: _package_date
          entity_field: capacity
          plugin_id: field
        created:
          id: created
          table: _package_date
          field: created
          relationship: none
          group_type: group
          admin_label: ''
          label: Created
          exclude: false
          alter:
            alter_text: false
            text: ''
            make_link: false
            path: ''
            absolute: false
            external: false
            replace_spaces: false
            path_case: none
            trim_whitespace: false
            alt: ''
            rel: ''
            link_class: ''
            prefix: ''
            suffix: ''
            target: ''
            nl2br: false
            max_length: 0
            word_boundary: true
            ellipsis: true
            more_link: false
            more_link_text: ''
            more_link_path: ''
            strip_tags: false
            trim: false
            preserve_tags: ''
            html: false
          element_type: ''
          element_class: ''
          element_label_type: ''
          element_label_class: ''
          element_label_colon: true
          element_wrapper_type: ''
          element_wrapper_class: ''
          element_default_classes: true
          empty: ''
          hide_empty: false
          empty_zero: false
          hide_alter_empty: true
          click_sort_column: value
          type: timestamp
          settings:
            date_format: medium
            custom_date_format: ''
            timezone: ''
          group_column: value
          group_columns: {  }
          group_rows: true
          delta_limit: 0
          delta_offset: 0
          delta_reversed: false
          delta_first_last: false
          multi_type: separator
          separator: ', '
          field_api_classes: false
          entity_type: _package_date
          entity_field: created
          plugin_id: field
        changed:
          id: changed
          table: _package_date
          field: changed
          relationship: none
          group_type: group
          admin_label: ''
          label: Changed
          exclude: false
          alter:
            alter_text: false
            text: ''
            make_link: false
            path: ''
            absolute: false
            external: false
            replace_spaces: false
            path_case: none
            trim_whitespace: false
            alt: ''
            rel: ''
            link_class: ''
            prefix: ''
            suffix: ''
            target: ''
            nl2br: false
            max_length: 0
            word_boundary: true
            ellipsis: true
            more_link: false
            more_link_text: ''
            more_link_path: ''
            strip_tags: false
            trim: false
            preserve_tags: ''
            html: false
          element_type: ''
          element_class: ''
          element_label_type: ''
          element_label_class: ''
          element_label_colon: true
          element_wrapper_type: ''
          element_wrapper_class: ''
          element_default_classes: true
          empty: ''
          hide_empty: false
          empty_zero: false
          hide_alter_empty: true
          click_sort_column: value
          type: timestamp
          settings:
            date_format: medium
            custom_date_format: ''
            timezone: ''
          group_column: value
          group_columns: {  }
          group_rows: true
          delta_limit: 0
          delta_offset: 0
          delta_reversed: false
          delta_first_last: false
          multi_type: separator
          separator: ', '
          field_api_classes: false
          entity_type: _package_date
          entity_field: changed
          plugin_id: field
      filters: {  }
      sorts: {  }
      title: 'Package Dates'
      header: {  }
      footer: {  }
      empty:
        area_text_custom:
          id: area_text_custom
          table: views
          field: area_text_custom
          relationship: none
          group_type: group
          admin_label: ''
          empty: true
          tokenize: false
          content: '<p>There is currently no data to display.</p>'
          plugin_id: text_custom
      relationships:
        _package_id:
          id: _package_id
          table: _package_date
          field: _package_id
          relationship: none
          group_type: group
          admin_label: 'Relationship between Package and Package Dates'
          required: false
          entity_type: _package_date
          entity_field: _package_id
          plugin_id: standard
      arguments: {  }
      display_extenders: {  }
    cache_metadata:
      max-age: 0
      contexts:
        - 'languages:language_content'
        - 'languages:language_interface'
        - url.query_args
        - user.permissions
      tags: {  }
  _package_dates_report:
    display_plugin: page
    id: _package_dates_report
    display_title: 'Package Dates'
    position: 1
    display_options:
      display_extenders: {  }
      path: admin/-package-dates
      menu:
        type: normal
        title: 'Package Dates'
        description: ''
        expanded: false
        parent: system.admin_reports
        weight: 0
        context: '0'
        menu_name: admin
      display_description: ''
    cache_metadata:
      max-age: 0
      contexts:
        - 'languages:language_content'
        - 'languages:language_interface'
        - url.query_args
        - user.permissions
      tags: {  }

It looks like the relationships property is the wrong way around, but I'm not sure.

I'd also like to be able to create a view for the package content type, and link this to the package_date entity. When trying to do so, I don't have a relationship I can add.

I expect I'm missing one or more things to get this working, but I'm not sure what. At the very least, I think I need the inverse of the defined relationship, but I'm sure that's not the only issue.

4
  • 2
    Keen to know how this would work, I have posted similar questions, but no answers on those yet.
    – amit
    Commented Apr 18, 2017 at 22:27
  • 1
    Since optimo_package_date is your own custom entity, you can use hook_views_data instead of hook_views_data_alter. hook_views_data_alter would only be required to change node's implementation of hook_views_data (eg. to allow a node to reference your custom entity). In a hook_views_data_alter implementation you have to specify the field (which is currently missing). If I get it right the date entity contains a reference to the node. In that case the 'base field' should be the identifier of the node, which is nid. Otherwise the reference structure is not clear to me. Commented Apr 25, 2017 at 14:17
  • 1
    I changed back to using hook_views_data but no change to the behaviour unfortunately. The relationship between my package_date entity and the package content type is represented by an integer field in package_date and a multiple value integer field in package - as stated in my question, it's not an entity relationship.
    – user34185
    Commented Apr 25, 2017 at 14:33
  • 1
    @BrynJ, apologies I missed that. I assumed you had one integer field referring to a node id. I have no idea how to approach this with two fields unfortunately... Commented Apr 25, 2017 at 17:51

1 Answer 1

2
+25

You have actually provided both relationships correctly;

  • $data['package_date']['table']['join'] provides an automatic reference from the external entity to this one; when the other entity is loaded, this one is always available.

  • $data['package_date']['package_id']['relationship'] provides a relationship that needs to be added manually via the views UI.

(If you want them to behave similar, you would have to use hook_views_data_alter()) to add one of the above definitions (automatic or manual) to the node entity.)

What I think your problem here (mostly based on Drupal 7 logic), is that you are referring the wrong table. When looking at the complete list of invocations of hook_views_data() and for instance look at book_views_data(), you will see that all those implementations look like this:

$data['book']['table']['join'] = [
  'node_field_data' => [
    'left_field' => 'nid',
    'field' => 'nid',
  ],
];

All those joins use the node_field_data table, not the field specific table node__field_package_id you are using:

$data['package_date']['table']['join'] = array(
  // Index this array by the table name to which this table refers.
  // 'left_field' is the primary key in the referenced table.
  // 'field' is the foreign key in this table.
  'node__field_package_id' => array(
    'left_field' => 'field_package_id_value',
    'field' => 'package_id',
  ),
);

By providing the value for left_field, you are already telling Views to which field you want to use, you you should not access that field directly. I suppose it should work once you change node__field_package_id to node_field_data.

0

Your Answer

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