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.
hook_views_data
instead ofhook_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 ahook_views_data_alter
implementation you have to specify thefield
(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 isnid
. Otherwise the reference structure is not clear to me.hook_views_data
but no change to the behaviour unfortunately. The relationship between mypackage_date
entity and thepackage
content type is represented by an integer field inpackage_date
and a multiple value integer field inpackage
- as stated in my question, it's not an entity relationship.