Problem/Motivation

The relation module declares some tokens in relation_token_info(). The entity module declares additional tokens based on the entity properties defined in relation_entity_property_info_alter(). The latter are not implemented in relation_tokens(). They include:

  • endpoints-source-[entity_type]
  • endpoints-target-[entity_type]

The missing implementations became evident when trying to pass this token as an argument to a view on an entityreference field attached to a relation bundle.

Proposed resolution

Implement the tokens.

Remaining tasks

Review, add tests for relation tokens (a separate issue as none exist).

User interface changes

None.

API changes

None.

Data model changes

None.

Comments

solotandem created an issue. See original summary.

solotandem’s picture

Status: Active » Needs review
StatusFileSize
new2.74 KB

Attached patch implements proposed resolution.

mikran’s picture

Status: Needs review » Needs work
+++ b/relation.module
@@ -780,6 +780,41 @@ function relation_get_endpoints($relation, $entity_type = NULL) {
+function relation_get_endpoint_entityid($relation, $endpoint_type = 'source', $entity_type = NULL) {

We already have a function to get relation endpoints (relation_get_endpoints) and this can be used here as well. It needs new parameters so that endpoints can be filtered by direction and probably also a parameter to control whether entities should be loaded or if entity_id is enough.

+++ b/relation.module
@@ -780,6 +780,41 @@ function relation_get_endpoints($relation, $entity_type = NULL) {
+  $r_index = $endpoint_type == 'source' ? 0 : 1;

What about non-binary relation types?

+++ b/relation.tokens.inc
@@ -135,6 +135,14 @@ function relation_tokens($type, $tokens, array $data = array(), array $options =
+            list(, $endpoint_type, $entity_type) = explode('-', $name);

$endpoint_type usually refers to entity type. $direction or something like that could be a less confusing name for the variable.

solotandem’s picture

@mikran, thanks for taking a look at this request. Let me address each of your comments in turn starting with the terminology one as this affects the other two.

Regarding the claim that "$endpoint_type usually refers to entity type," I would refer you to the module code where this term is used for precisely the purpose of identifying the 'source' and 'target' endpoints in each of these files:

  • relation_feeds/RelationFeedsProcessor.inc
  • relation.module (see relation_entity_property_info_alter())
  • relation.rules.inc (see relation_rules_get_specific_endpoints() and others)

For this reason, this term was used in the patch at hand.

Yes, there is a function to return the endpoints, plural, i.e. all of them. There is not a function to return a single endpoint or, as is needed here (for token use), the entity_id from a single endpoint. Adding a function appears quite reasonable.

Yes, what about non-binary relation types? As the module comment reads regarding arity, "In nearly all cases you will want to leave this set to 2." I have not used a relation with arity > 2 for production use. Not recalling the specifics of how they are handled, I tried these today in order to respond to this comment. Based on this trial, I would question whether the module supports a directional relation with arity > 2. In other words, can it distinguish between the source and target entities on a directional relation with arity > 2? Are the database columns sufficient to indicate the 'endpoint type'? Similarly, are the parameters to relation_create() or relation_save() sufficient for same? And relation_endpoint_field_presave() simply sets $item['r_index'] = $delta;. So are not the r_index columns being asked to serve the dual purpose of being:

  • an indicator of the endpoint type in the relation_bundles table
  • delta in the endpoints table?

Does not the latter imply that the endpoint type is not being stored? (There are no other columns.)

For example, I created both directional and non-directional relations with arity = 4. Both had the same 4 node bundles for source (and target for the directional). For the non-directional relation, the database entries are fine. However, looking in the endpoints table, how am I to distinguish between those that are the source and the target endpoints for the directional relation? Is my code supposed to maintain this information?

Given that relation itself does not appear to handle a directional relation with arity > 2, is not the statement
$r_index = $endpoint_type == 'source' ? 0 : 1;
sufficient for the use cases it can handle, i.e. a binary relation that is directional? And for a non-directional relation there is really only a 'source' endpoint type whose r_index should be zero (for purposes of this new function).

As the 'todo' comment on this function indicates, it is left as an assignment for the reader to implement returning a specific endpoint among the source or target endpoints. All it is trying to do at this point is return the first one, which it does if I am not mistaken.

Another example, see relation_rules_get_specific_endpoints()

  if ($info['endpoint_type'] == 'source') {
    if ($info['relation_directional']) {
      $endpoints = array($relation->endpoints[LANGUAGE_NONE][0]);
    }
    else {
      $endpoints = $relation->endpoints[LANGUAGE_NONE];
    }
  }
  else {
    // @todo This does not respect number of source endpoints.
    $endpoints = array_slice($relation->endpoints[LANGUAGE_NONE], 1);
  }

The above code returns all but the first endpoint if the endpoint_type is 'target'. It returns only the first endpoint if endpoint_type is 'source' and the relation is directional. How does that respect arity > 2 and/or directional relations?

In summary, does this 'need work' or is it ready for review and testing?

mikran’s picture

Regarding the claim that "$endpoint_type usually refers to entity type," I would refer you to the module code where this term is used for precisely the purpose of identifying the 'source' and 'target' endpoints in each of these files:

You're right! My bad, I didn't verify my claim there.

Yes, what about non-binary relation types?

As the module comment reads regarding arity, "In nearly all cases you will want to leave this set to 2." I have not used a relation with arity > 2 for production use. Not recalling the specifics of how they are handled, I tried these today in order to respond to this comment. Based on this trial, I would question whether the module supports a directional relation with arity > 2. In other words, can it distinguish between the source and target entities on a directional relation with arity > 2?

Relation does not support many-to-many directional relations but one-to-many directional relations are supported. And really, implementation specifics are almost same as binary relations, r_index 0 is the source and r_index >0s are the targets in order. So you can do things like relation type: "album" (artist -> song a, song b, song c, ...). (this is the only bug issue I know of this kinds of relations #2110529: admin/content/relation is wrong for non binary directional relations, so nothing major)

// @todo This does not respect number of source endpoints.

I think it might have been the idea that one day many-to-many relations would be supported too but I doubt that will ever happen in D7 version.

In summary, does this 'need work' or is it ready for review and testing?

Yes I think this is still 'needs work' on that point that target should work for any r_index >0 endpoint.

and

Yes, there is a function to return the endpoints, plural, i.e. all of them. There is not a function to return a single endpoint or, as is needed here (for token use), the entity_id from a single endpoint. Adding a function appears quite reasonable.

with relation_get_endpoints you could get all endpoint ids of given entity type from relation and use the first of the returned ids. That way it would work on one-to-many relations and it would improve the developer experience as the relation_get_endpoints could then be used to retrieve just source or target endpoints and optionally just entity ids instead of loaded entities. If 'target' endpoint is requested then we actually mean all r_index > 0 endpoints. New footprint (to keep it backwards compatible): relation_get_endpoints($relation, $entity_type = NULL, $endpoint_type = NULL, $load_entities = TRUE), right?

This added relation_get_endpoint_entityid function is basically escaping the endpoints-foreach as soon as matching endpoint is found but the relation is fully loaded already so I don't think the new function is really needed for performance reasons either.

solotandem’s picture

Status: Needs work » Needs review
StatusFileSize
new5.05 KB

@mikran, I revised the patch as follows:

  • based on what I understand your suggestions to be regarding additional parameters to relation_get_endpoints(,,$endpoint_type,$load_entities).
  • added a helper function relation_get_endpoints_by_endpoint_type() that is called by relation_get_endpoints() and relation_tokens(), and could be called by relation_rules_get_specific_endpoints().

The reason for adding relation_get_endpoints_by_endpoint_type() is twofold:

  • relation_get_endpoints() produces an array keyed by entity_type (which is useful with the call to entity_load() but is not useful for the token use case)
  • this function is a refactor of code in relation_rules_get_specific_endpoints() and could be called there.

I would remove the following from relation_get_endpoints() and simply return an empty array; if compatability with existing code is a concern, this should be reasonably safe as it would evaluate to FALSE. It is retained simply to respect the style of the existing code.

  if (empty($items)) {
    // @todo Why not return the empty array below?
    return FALSE;
  }
mikran’s picture

I really like the direction this is going now. And I agree it's better to return an empty array there. So this is RTBC from me when those @todos are fixed the way you recommended.

solotandem’s picture

StatusFileSize
new5.86 KB

The attached patch removes the @todos:

  • remove the if (empty($items)) code block in relation_get_endpoints()
  • calls relation_get_endpoints_by_endpoint_type() from relation_rules_get_specific_endpoints().
mikran’s picture

Status: Needs review » Fixed

  • mikran committed cd5f19f on 7.x-1.x authored by solotandem
    Issue #2600076 by solotandem: Implement tokens declared by entity module
    

Status: Fixed » Closed (fixed)

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