Views integration (among other things) broken when voting on non-node entities

Created on 22 November 2017, about 7 years ago
Updated 27 January 2024, 11 months ago

Because the entity_id field is declared as an Entity Reference field, when displaying votes in a view, Views will attempt to use the entity reference formatters to display the entity that was voted on. Those formatters rely on the target type of the reference to do their business. Since no explicit target type, is (or can be) declared in the base field definitions, however, the target type defaults to node (see EntityReferenceItem::defaultStorageSettings()). As soon as you start voting on entities that are not nodes, this will break.

To solve this, I think the entity_id field should be declared as a simple string field (because entity IDs may be strings) and then we need a custom views field to handle the rendering. This is similar to Comment module which ships a CommentedEntity views field.

Alternatively we could introduce a dependency on Dynamic Entity Reference which will give us that for free.

🐛 Bug report
Status

Active

Version

3.0

Component

Code

Created by

🇩🇪Germany tstoeckler Essen, Germany

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • I'm posting my workaround here in case it might helps, though I don't consider it a true solution. I am also partial to using Dynamic Entity Reference to solve the issue like @tstoeckler mentioned , but haven't looked too deep into that.

    Context First

    I have multiple custom entities which have votes. Similar to the issue description, the "voted entity" field does not render in views because the "BaseFieldDefinition" in Vote.php is for an entity reference that does not have the appropriate "target_type" setting applied (i.e. content type).

    You could manually define the target_type settings, but if you have multiple entity types, then you're out of luck. Unless I'm mistaken adjusting the "target_type" setting in "bundleFieldDefinitions" also doesn't solve the issue because the entity type passed there is limited to a singular VoteType definition, and my VoteTypes are tied to multiple custom entities.

    I looked into creating a computed field like @asherry mentioned , but ultimately went with the following:

    My solution

    1. Use "hook_entity_base_field_info" to add new base fields to "vote"
    2. Use "hook_views_pre_render" to update the target_id for those new entity reference fields
    3. Update view to use newly available fields. Combine columns, or use hidden fields and rewrites to style view output (or twig theming)

    First I used "hook_entity_base_field_info" to add new base fields to "vote"

    function HOOK_entity_base_field_info(EntityTypeInterface $entity_type) {
      if ($entity_type->id() == 'vote') {
        $fields = [];
    
        $fields['entity_id_custom'] = BaseFieldDefinition::create('entity_reference')
          ->setLabel(t('Entity ID: custom'))
          ->setDescription(t('Entity ID: custom'))
          ->setSetting('target_type', 'custom');
    
    
        $fields['entity_id_custom_2'] = BaseFieldDefinition::create('entity_reference')
          ->setLabel(t('Entity ID: custom 2'))
          ->setDescription(t('Entity ID: custom'))
          ->setSetting('target_type', 'custom_2');
    
        return $fields;
      }
    }
    

    Then I used "hook_views_pre_render" to update the target_id for those new entity reference fields before rendering the votes table view.

    // Update the entity references based on their entity_type
    function HOOK_views_pre_render(ViewExecutable $view) {
      // Check if view is votingapi_votes
      if ($view->id() === 'votingapi_votes') {
        
        // Loop through results, update target_id for each vote
        foreach ($view->result as $key => $value) {
          $entity_type = $value->_entity->entity_type->value; // Get vote entity_type
          $eid = $value->_entity->entity_id->target_id; // Get vote entity_id
    
          // Here, I use the "entity_type" to construct the field name. 
          // The important thing is that it matches the field names used in "hook_entity_base_field_info"
          $field_name = 'entity_id_'.$entity_type; // Get field name
          $custom = $value->_entity->$field_name; // Get reference to update by field_name
          $custom->target_id = $eid; // Change the target ID
        }
      }
    }
    

    Lastly, I updated the relevant view(s) to use the newly defined fields. For the "votingapi_votes" view, I added each custom entity field as a column, then combined them in the view table settings. Side note: they did have to be "arranged" next to each other to combine without bugging out.

    I might consider using a Dynamic Entity Reference field later, but for now I hope this helps anyone stuck like I was!

Production build 0.71.5 2024