Problem/Motivation

The current relationship definitions are:
- incomplete
- restrictive, and
- do not play well with Views (due to their bundled, multi-table join nature)

For example, consider the relation_handler_join class.
The build_join() method finds the other endpoint on a relation in three steps:
- join entity to endpoints
- self-join the endpoints table but grab a different endpoint (r_index)
- join endpoints to other entity

This join is intended for use when the base table to the view is an entity like a node. In practice, I have found it more useful to have the base table be Relation. But there is no relationship definition that provides the other endpoint in this case. If this class is used, then it produces a cross product of rows. In other words, you get N x N rows returned instead of N rows.

So I modified this class to eliminate the first join from entity to endpoints. This solved the cross product problem. However, if you then want to relate to the other endpoint entity, it is a bit of wishful thinking to have the join handler get the correct alias for the desired instance of the endpoints table. Using the "%alias" token works for the current relationship but fails if you need multiple relationships for the View.

This is because of the "not playing well with Views" part. Since Relation joins multiple tables in a single relationship definition, but Views only knows about two of them, then Views is not able to manage the aliases. Because of this, you can not specify the "table instance" through the Views UI and are not able to accurately determine an alias in code.

The unknown alias problem is an issue if you need to add multiple relationship definitions to the view. For me, this seems to be a very likely possibility. The handler code can not reliably determine the aliases it needs.

Proposed resolution

Replace the three existing multi-table relationships (plus the fourth mentioned above) with three dual-table relationships:
- relation to endpoints
- endpoints to entity
- endpoints to endpoints

With these three:
- we provide the existing functionality and more
- you will be able to specify the "table instance" through the Views UI, and
- Relation will fit into the Views API ("play well")

The new relationships may imply you can not use one of the endpoint entities as the base table to Views. However, in my experience this seems not a hindrance, but a good practice. You will still be able to access each endpoint (or all endpoints) using the new relationships.

Remaining tasks

Review, write tests, change documentation (API documentation does not exist).

User interface changes

The relationship options form will now include the "table instance" select element as Views will know about it.

API changes

None.

Comments

solotandem’s picture

Status: Active » Needs review
StatusFileSize
new7.25 KB

Attached patch implements the proposed resolution.

chx’s picture

StatusFileSize
new13.2 KB

Terrific idea.

I must presume you meant this patch. I can't seem to be able to pick r_index though.

dawehner’s picture

StatusFileSize
new13.29 KB

There

+++ b/views/relation.views.incundefined
@@ -189,60 +193,68 @@ function relation_views_data_alter(&$data) {
-            'handler' => 'views_handler_relationship',

You can't have a relationship without a handler. Maybe an issue for views should be filled...

Here is a new patch

chx’s picture

StatusFileSize
new12.84 KB

Right now, the biggest problem is that I do not readily see how to add the relation table itself. Otherwise, we are good.

solotandem’s picture

#2: Yes, I would replace the definitions and handlers, not add to them. But I did not know how you would take to the idea and did not want to be presumptuous. (I did use the word "replace" in the issue.)

#4: Maybe I am missing a use case, but as I wrote above, I have found a good practice to use relation as the base table for the view. Then you have it and may not ever need to add it. If we do, or if we need a relationship from entity to endpoint, we can certainly add them.

Thanks for looking at this, both of you.

chx’s picture

StatusFileSize
new17.23 KB

We are getting closer.

chx’s picture

Status: Needs review » Needs work

Thanks for the work, what we have in contrib works. To some extent. There are a few problems: you can't get to the relation table and you need to make that chainable , as a base table isn't enough because you can have E1-R1-E2-R2-E3. Also if you have node --> user then you can't get from user to anywhere.

solotandem’s picture

@chx, regarding today's commits: line 74 in relation_handler_relationship.inc, the relation_handler_endpoint_endpoint_join class

    if ($this->options['r_index'] > -1) {
      // Grab a specific index.
      $conditions .= " AND $l.$r_index_field_name = " . $this->options['r_index'];
    }

I had the table alias as $r not $l and that was intended. The other endpoint table ($l in this case) has already been joined and on that earlier join you would be able to specify an r_index value for it. The r_index restriction on this join is intended for the new instance of the endpoint table being joined. I envisioned this being useful with n-ary relations where you might want endpoints 1 and 2 out of 0-3. This is not fleshed out. I have a case where we would want to extend the r_index != r_index while restricting the search to index values in a range.

For example, a 4-way relation from A->B<-B<-A. The r_index != r_index would be restricted to r_index IN (1,2). That is where I was going with this.

mustanggb’s picture

Was doing a "1->many" of "users->nodes" using a view of users and a "Provides a relationship from @left(users) to @right(nodes) via the relation @relation_type_label" relationship

Since the 06/12/2011 4:24:44 pm commit this has been removed and it seems the replacement is meant to be "@left(users) to the other endpoints(nodes) using the "@relation_type_label" relation"
However this option is not appearing as a Relationship option in my view of users

solotandem’s picture

@akamustang: The relationship definitions are being redone as indicated above. They are mostly there, although chx has remarked in IRC that a relationship from user to relation is not available. Can you redo the view starting from the relation and add relationships as follows?
- relation to endpoint
- endpoint to user
- endpoint to other endpoint
- other endpoint to node

The first relationship is in the "relation" group in the Views UI, while the others are in "entity" group.

If not, stay tuned for further commits.

mustanggb’s picture

After creating a relation view the only available relationships group was "Relation", there was not the option of "Entity"

chx’s picture

Yeah there is no entity group (and solotandem thought of endpoints, anwyays). Everything is in the relation group. Read the title/description carefully, they tell you quite a lot. I also worked on them a lot to be so. Also, it's being heavily worked on, so within a short term it might be a lot better.

solotandem’s picture

Status: Needs work » Needs review
StatusFileSize
new12.42 KB

Attached patch adds missing relationships:
- from endpoints to "left" entity.
- from entity to endpoints

Patch also adds documentation and (imho) clarifies the titles and labels on the relationships.

With these definitions, I was able to use the entity (node, user, etc.) as the base table to the view, and relate to the sibling endpoint entity.

dawansv’s picture

Seems to work for me now with this patch.

I have a directional relation called Participants between nodes (representing courses) and users. I wanted to have a view to pull the users attached a particular course; after some messing around, I was able to to it by creating the 3 following relationships:

Relation: Participants (endpoints -> user)
(Endpoints::Participants) Relation: Participants (endpoints -> endpoints)
(Endpoints::Participants) Relation: Endpoints: Participants (endpoints -> node)

EDIT:

In addition, I am now also able to do it directly from a view using the user table as its basis (instead of relation), so that's even better:

User: Endpoints: Participants (user -> endpoints)
(Endpoints::Participants) Relation: Participants (endpoints -> endpoints)
(Endpoints::Participants2) Relation: Endpoints: Participants (endpoints -> node)

Thanks

MeDAN’s picture

I'm using current version of the Relation module(15 dec). Are this patches are applied in my version ? Earlier I could get RID field, after update I can't get this field...

// Update

I can add only "endpoints" field. Fields like RID,VID, Relation Type, UID, Created etc. can't be added. I think it is caused table group name change but Im now an expert in Relation module.

$data['field_data_endpoints']['table']['group'] = t('Relation');
$data['field_data_endpoints']['table']['join'] = array();
solotandem’s picture

The patch in #6 is in the latest code, but not the patch in #13.

Neither patch should prevent you from adding the RID field. Describe the structure of your view: base table, relationships, etc. You might move this to a new support request issue.

MeDAN’s picture

Ok I created new issue -> http://drupal.org/node/1372956.

Any advice/patch/whatever is very important to me. I'm fighting with views in Relation for a long time. After each update I have new problems...

chx’s picture

There's a whole + // Define join from entity to endpoints. section commented out. Also, there are no tests :(

Thanks for providing the missing definitions!

solotandem’s picture

StatusFileSize
new13.31 KB

Attached patch adds missing relationships:
- from endpoints to relation (not included in #13)
- from endpoints to "left" entity
- from entity to endpoints

I removed the commented lines, but still do not have any tests. :(

pdcarto’s picture

Re #14, I think I undertand the concept, but I don't see how to make the middle views relationship: the one going from endpoints -> endpoints.

In my case, I have students and organizations as node entities, and internships as relations. Internships have some fields like year (taxonomy) and final report (file field). I need to display a list of students and information about their internships (organization name, year, and final report). Not all students have internships, so I can't base the view off the internship relation.

Here are the three views relationship options I am currently seeing (I have applied patch #19):

Relation: endpoints[internship] -> node) Endpoints table to node using "internship" relation
Relation: interns with -> endpoints[internship] Relation table to endpoints table using "internship" relation
Relation: node -> endpoints[internship]) node to the sibling endpoints using the "internship" relation

Nothing obvious there about relating endpoints to endpoints. I'm not sure what the second one ("Relation table to endpoints table") does. Is that the "end points -> end points" views relation? I don't see an option to base it on the previously created node->endpoints.

Thanks

pdcarto’s picture

I figured #20 out - failed to clear views cache after applying the patch! It seems to be working - so far so good, anyway. Thanks!

howto’s picture

The last patch at #19 work for me. But we need add "relation type" condition (column bundle of table "field_data_endpoints") on the first JOIN clause from endpoints to "left" entity.

// Define join from endpoints to "left" entity.
 $data['field_data_endpoints']['table']['join'] += array(
// Directly links to the $entity_type_right table.
$entity_type_left => array(
   'left_field' => $entity_infos[$entity_type_left]['entity keys']['id'],
   'field' => 'endpoints_entity_id',
   ),
 );

Because between 2 entity type (or entity type:bundle) have many relation types so we must specific relation type for the select query

I try to create relationship from "endpoints ---> relation" and then filter by "Relation type" and "r_index" but the query omit entities (left side) if it do not have relation with "right side" entities instead of result in NULL
So, add "Relation type" condition at first join from endpoints to "left" entity will retain the entities (left side) that do not have relation with "right side"
---> JOIN clause in #19 comment <-----
LEFT JOIN {field_data_endpoints} field_data_endpoints ON A.id = field_data_endpoints.endpoints_entity_id

---> My JOIN clause <-----
LEFT JOIN {field_data_endpoints} field_data_endpoints ON A.id = field_data_endpoints.endpoints_entity_id AND field_data_endpoints.bundle = 'relation_type1'

Change in this patch:
1. Add join handler in relation.views.inc (relation_handler_endpoint_entity_join)
2. Declare file in relation.info
3. Define join handler class in file "relation_handler_endpoint_entity_join"

howto’s picture

Ignore #22 patch

Post lasted patch

tomogden’s picture

It works perfectly, great job!

While it's true working with relations requires a rather complicated string of Views relationships (inner joins), it's really the best way to accomplish this. Maybe a training video should be in order.

mikran’s picture

Status: Needs review » Needs work

I'm not quite sure if I'm on the map with the scope of this issue but my observations are the following(with a patch from #23): I have simple view listing users and I add new relationship user -> endpoints of relation type X. SQL as a result:

SELECT users.name AS users_name
FROM 
{users} users
INNER JOIN {field_data_endpoints} field_data_endpoints_users ON users.uid = field_data_endpoints_users.endpoints_entity_id

The relationships list shows these joins per relation type but the resulting query matches endpoints of all relations and all entity types. When you proceed from endpoints to relation or whatever the query can't make difference between user/1 and node/1 for example.

Only way to get the bundle added to query is by picking directional relation and then choosing position of the relationship base for it.

mustanggb’s picture

howto’s picture

StatusFileSize
new163.57 KB

@mikran

To get the bundle added to query is by picking directional relation and then choosing position of the relationship base for it

. I think this is not a appropriate way to get the correct result because the query will add bundle condition on the "second" field_data_endpoints table (regular alias "field_data_endpoints_field_data_endpoints")

Eg:

  • I have a directional relation between node:article & node:page name "article_to_page".
  • I have an article (node/1) as my input and i want to get all node:page that have relation with article (node/1) at relation type "article_to_page".

My view config:

  1. Add relationship "Article to page (endpoints -> endpoints)" and select "Position of the relationship base" = "target"

The sql query is:

SELECT node.title AS node_title, node.nid AS nid
FROM 
{node} node
LEFT JOIN {field_data_endpoints} field_data_endpoints ON node.nid = field_data_endpoints.endpoints_entity_id
LEFT JOIN {field_data_endpoints} field_data_endpoints_field_data_endpoints ON field_data_endpoints_field_data_endpoints.entity_id = field_data_endpoints.entity_id AND field_data_endpoints_field_data_endpoints.endpoints_r_index != field_data_endpoints.endpoints_r_index AND field_data_endpoints_field_data_endpoints.endpoints_r_index = 1 AND field_data_endpoints_field_data_endpoints.endpoints_entity_type = 'node' AND field_data_endpoints_field_data_endpoints.bundle = 'article_to_page'

This query does not result the exact node:page that have relation with article (node/1) at relation type "article_to_page".
This is detail result run in phpmyadmin
Query result that run in phpmyadmin

mikran’s picture

Status: Needs work » Needs review
StatusFileSize
new4.75 KB
new13.46 KB

Okay, here is a patch that fixes the scenario I had. These conditions probably have to be extended to endpoints -> endpoints joins as well.

The added file 'views/relation_handler_endpoint_entity_join.inc' is in separate patch file as I didn't figure out how to put it to same file.

mikran’s picture

StatusFileSize
new18.25 KB

Whitespace error fixed and now I figured out how to put everything into single file :).

mikran’s picture

Status: Needs review » Needs work

Some more test results:

Chain:
1. entity -> endpoint (works)
2. endpoint -> endpoint (works)
-2.1 endpoint -> relation (does not work, uses entity_id whereas endpoints_entity_id is what we are looking for)
-2.2 endpoint -> entity (does not work, uses correct field 'endpoints_entity_id' but 'bundle' and 'endpoints_entity_type' are not included)

mustanggb’s picture

How about this one then
Using #19 I have a directional relationship from All users bundles > Nodes of type test
Starting with a view showing Relations, relationships can be build and work fine
However starting with a view of Users no relationships (pertaining to this module) are available

chx’s picture

Component: Code » API
Status: Needs work » Closed (won't fix)

To play nice with Views, it is enough to stick to the original implementation but move the logic into the query method of the relationship handler and use the Views API properly instead of DBTNG.

mustanggb’s picture

So just to clarify
Does this mean that views integration has been rewritten and is now "playing nicely" again?

tomogden’s picture

I'm lost as well. Is there a follow up issue somewhere? What happens with the patches we've been running with that we need to make Relation work?

naught101’s picture

I think this is chx's way of saying "fixed, but it a slightly different way". See commit e8e00ad

tomogden’s picture

Confirmed. I was able to make the view function using the latest version of the project, and it only required TWO relationships.

tomogden’s picture

Issue summary: View changes

Add clarification.