Creating a backreference when the referenced field is used in two different bundle types yields "extra" fields not referenced by the originating node.

I've got the following:
Content types: Store and Lead.
Node Reference field: field_store *shared* by Lead and User.
View with base table store and a Relationship "Content: Stores(s) (field_store) - reverse (to Content)

Views Creates This:

 SELECT node.title AS node_title, node.nid AS nid, field_store_node.title AS field_store_node_title, field_store_node.nid AS field_store_node_nid, 'node' AS field_data_field_store_node_entity_type
FROM 
{node} node
LEFT JOIN {field_data_field_store} field_data_field_store ON node.nid = field_data_field_store.field_store_nid
LEFT JOIN {node} field_store_node ON field_data_field_store.entity_id = field_store_node.nid
WHERE (( (node.status = '1') AND (node.type IN  ('store'))  ))  

A quick query of `field_data_field_store` with field_data_field_store.field_store_nid set to a valid store NID shows that records with a bundle values of both "store" and "user" are returned. The USER entity_id's end up getting joined as NIDs in the second Left Join. If there happens to be a NID with the corresponding UID it gets returned with the view.

I've added:

 AND (field_store_node.type IN  ('lead'))

via the views UI. It will get some of the bad results, but not all of them. What is needed is a way to add:

AND (field_data_field_hotel.bundle = 'lead')

This needs to be added programatically by the module where 'lead' is replaced by the bundle type of the content type on the "originating" side of the reversed relationship.

Can anybody point me to the chunk of code where this bit of magic needs to occur ?

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Dave Kopecek’s picture

>> two different bundle types yields "extra" fields not referenced by the originating node.

That was "extra rows" not "extra fields"

DigitalFrontiersMedia’s picture

I think there's something else wrong with your query that is the cause of the fits.

Excluding the details I think are extraneous, the query generated by Views is basically doing this:

SELECT something FROM
node
LEFT JOIN node ON some_field_in_table_X_not_in_node_table = some_other_field_in_same_table_X_not_in_node_table

I'm not sure how MySQL would react to a statement requesting a join back to the same table being selected from based on two fields in some other table just happening to match each other (which in the scenario described would seem like a circular reference itself: the field_store_nid happening to reference the nid/entity_id--a 'lead' in this case--that actually contains the field_store. I don't even think the latter would be possible unless the reference field was placed on a content type and was allowed to reference the same content type the field was added to.

I'm pretty sure the Relationship "Content: Stores(s) (field_store) - reverse (to Content) is not necessary and is the cause of this problem since it is likely adding the circular node JOIN back to node. Any information that you seem to be seeking is coming from either the node table or the field_data_field_store table--both of which are already JOINed and a part of the query before your Relationship is added. Thus, you should be able to get to any field data that you might need from either without the added Relationship.

This also means that you should be able to replace your field_store_node.type IN ('lead') filter with a simple node.type filter after making this change without any need for the field_data_field_hotel.bundle = 'lead' stuff.

DigitalFrontiersMedia’s picture

Category: bug » support
Status: Active » Closed (works as designed)

The more I think about this, the more I think this isn't a bug but simply a problem with the Relationship that was added. Changing to Support Request and closing. Please re-open if this ends up not being the answer and if it, indeed, turns out that Views is somehow the culprit.

DigitalFrontiersMedia’s picture

Category: support » bug
Status: Closed (works as designed) » Active

I stand corrected. I had missed the new node alias "field_store_node" on the join and was interpreting

LEFT JOIN {node} field_store_node ON field_data_field_store.entity_id = field_store_node.nid

as

LEFT JOIN {node} field_store_node ON field_data_field_store.entity_id = field_data_field_store.nid

As you say, there appears to be a problem here. Setting back to Active, Bug Report

Dave Kopecek’s picture

I"m opening this back up. Pretty sure this is a bug and not how a backreferences should work. Here's the gist of it:

When the base of a view is an entity of type "node" creating a Reverse reference in Views, i.e. something that reads like "Relationship "Content: Stores(s) (field_store) - reverse (to Content)" should return "content", i.e. nodes. It should not return "Users".

As currently constructed query blindly assumes that all entity_ids are NIDs. If the entity_type is type "user" entity id is a UID not a NID. If there is a Node where NID = uid-returned-from-view you'll get "extra" mis-related rows returned.

I'm now thinking that it's not the BUNDLE type, that's the issue, but the ENTITY type. The entity type should be specified as a condition in the join:

LEFT JOIN field_data_field_hotel field_data_field_store ON node.nid = field_data_field_store.field_store_nid
AND field_data_field_store.entity_type = 'node'

While I'm by no means an expert on the dark inner workings of these entity tables it would seem to me that one way to fix this programatically would be to make this be field_data_field_store.entity_type = entity-type-of- views-base-table. This would make the backreference work in the great majority of cases. It seems that a "multiple entity back reference" would require some tricky joins and might best be handled separately.

And another thing... This is probably another issue, but it looks like the view as constructed will also pick up deleted field references. After removing the field_store field from my user table in the above example, there are still rows in field_data_field_store with entity_type "user" left in field_data_field_store with the deleted column set to 1. While we're at it we should probably filter them out in the join as well:

LEFT JOIN field_data_field_hotel field_data_field_storeON node.nid = field_data_field_store.field_store_nid
AND field_data_field_store.entity_type = 'node'  AND field_data_field_store.deleted <> 1

Regarding #2 above:
I think you're mis-reading the query. It's created by Views and performs the joins correctly, it just isn't limiting the joins for multiple entity use cases.

Dave Kopecek’s picture

Status: Active » Needs work

Based on implementations of hook_field_views_data_views_data_alter() found in the views module ( see views/modules/taxonomy.views.inc, etc) I think the solution here is pretty straightforward: add a 'join_extra' array to $data['node'][$pseudo_field_name]['relationship'] in node_reference_field_views_data_views_data_alter() as below.

This seems to work:

    $data['node'][$pseudo_field_name]['relationship'] = array(
      'title' => $title,
      'help' => t('Relate each @entity referencing the node through @field.', array('@entity' => $entity, '@field' => $label)),
      'handler' => 'views_handler_relationship_entity_reverse',
      'field_name' => $field['field_name'],
      'field table' => _field_sql_storage_tablename($field),
      'field field' => $field['field_name'] . '_nid',
      'base' => $entity_info['base table'],
      'base field' => $entity_info['entity keys']['id'],
      'label' => t('!field_name', array('!field_name' => $field['field_name'])),
      'join_extra' => array(
        0 => array(
          'field' => 'entity_type',
          'value' => $entity_type,
        ),
        1 => array(
          'field' => 'deleted',
          'value' => 0,
          'numeric' => TRUE,
        ),
      ),      
    );

Will post a patch shortly.

Dave Kopecek’s picture

Status: Needs work » Needs review
FileSize
991 bytes

Patch for #6

Status: Needs review » Needs work

The last submitted patch, 0001-Fix.patch, failed testing.

Dave Kopecek’s picture

FileSize
740 bytes

Hmm, looks like patches created by TortoiseGit aren't liked. Let's try this again.

DigitalFrontiersMedia’s picture

Status: Needs work » Needs review

Patch failed to be re-tested due to incorrect status. Changing status to try to kickstart it again. Or does the patch need to be re-submitted?

Status: Needs review » Needs work

The last submitted patch, node_reference.patch, failed testing.

Dave Kopecek’s picture

Status: Needs work » Needs review

#9: node_reference.patch queued for re-testing.

Status: Needs review » Needs work

The last submitted patch, node_reference.patch, failed testing.

Dave Kopecek’s picture

Status: Needs work » Needs review
FileSize
779 bytes

3rd time's a charm, I hope.

czigor’s picture

Status: Needs review » Reviewed & tested by the community

This works. Views itself has the same solution in #1321210: Join condition missing for Field API table(s) so this must be the right way to go.

Chris Matthews’s picture

Version: 7.x-2.0 » 7.x-2.x-dev
renatog’s picture

Really makes sense

  • RenatoG committed 7c0b4da on 7.x-2.x authored by Dave Kopecek
    Issue #1578768 by Dave Kopecek, DigitalFrontiersMedia, czigor, Chris...
renatog’s picture

Status: Reviewed & tested by the community » Fixed

committed to the dev branch.

Thank you so much everyone

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.