Forked from #391330-41: File Field for Core:
The image_link_content formatter raises an interesting issue: we currently have no generic, reliable way to generate the url of an 'object' (node, user, comment, taxo term, contrib fieldable entity...). The patch assumes "{$object_type}/{$object_id}", which happens to work for nodes, users and comments, but is obviously not future proof.

Not sure how best to address that : a (pseudo-)token string in hook_fieldable_info() ? a new (fieldable type) hook that builds a url ?

Marking this 'critical' (as in: we need to solve this before we ship)

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

drewish’s picture

oh totally, i hope this doesn't end up totally tied to the fields API because we really need similar functionality for the files table. over on #353458: hook_file_references() was not designed for a highly flexible field storage i'm trying to kill hook_file_references() and move it to a table so that user, nodes, comments, what ever can hold a reference to a file and this would be a great way to build a link to the them for administrative purposes ala #322287: Add file.module to provide UI for managing files.

yched’s picture

Well, currently hook_fieldable_info() is the only cross-entity hook we have in core...
If/when #460320: Standarized, pluggable entity loading (nodes, users, taxonomy, files, comments) lands, then hook_fieldable_info() becomes hook_entity_info() (with *optional* properties for the 'fieldable' behavior) and it will be a 'not fieldable-only' home for this.

All the entity types you mention (user, nodes, comments) are or will soon be fieldable, BTW. Once File field lands and replaces upload.module, what's the other way for an object to reference a file than through a File Field ?

mr.baileys’s picture

Subscribing as #16161: Move Read More link to end of node content would benefit from this too.

I'm new to the whole Fields API story, but as I understand it, one of the main benefits of fields is their flexibility. In that light I think a hook or callback solution (with default fallback {type}/{id}) is preferred over the pseudo token string.

yched’s picture

Title: 'as link' formatters need a generic way to build the url of an 'object' » 'as link' formatters need a generic way to build the url of an 'entity'

Renaming to match the now officially official term of 'entity'.

Frando’s picture

Suggestion:

Let's add a "view path" to entity declarations in hook_entity_info. This could then contain an arbitrary URL, with a wildcard for the ID (or, even better, for an arbitrary field of the entity).

So e.g in node_entity_info() you'd had

$return['node']['view path'] = 'node/%nid';

and to create the link to an entity, you'd match the wildcard and replace it with the identically named property of the actual entity object.

This would provide a very flexible solution also for not-so-standard entity types, and could also be altered by contrib through hook_entity_info_alter().

Frando’s picture

Status: Active » Needs work
FileSize
3.42 KB

The attached patch adds a function to create the URL to an arbitrary entity, following the suggestion I outlined above.

It's working well. I think this solution should be flexible enough.

Note that this is not yet integrated in field API. I didn't find a spot so I'm leaving this to the field api guys.

yched’s picture

Looks good to me, thanks for tackling this Frando :-).

+++ includes/common.inc
@@ -5165,6 +5165,37 @@ function entity_get_controller($entity_type) {
+ * @param $entity
+ *   An actual entity instance for which the path shall be created.
+ *
+ * @return

I think we don't add empty lines before @return
'an actual entity instance' ? couldn't this simply be 'the entity' ?

+++ includes/common.inc
@@ -5165,6 +5165,37 @@ function entity_get_controller($entity_type) {
+    preg_match('!%([^/]+)(/|$)!', $info['view path'], $matches);
+    // Get the property of the entity that has the name of the wildcard.
+    $property = $entity->{$matches[1]};

We'd possibly need to support several replacements ?

+++ modules/system/system.api.php
@@ -61,6 +61,10 @@
  *   - cacheable: A boolean indicating whether Field API should cache
  *     loaded fields for each object, reducing the cost of
  *     field_attach_load().
+ *   - view path: A pattern for the menu router path to view entities. The
+ *       pattern can contain a wildcard that will be replaced by the entity's
+ *       property of the same name (if the view path pattern is 'node/%nid',
+ *       %nid will be replaced by the entity's nid property).

indentation is off

+ We'd also need comment/%cid for comments

First use cases will come with Filefield / imagefield, we can commit something without waiting for them.

This review is powered by Dreditor.

bcn’s picture

FileSize
4.43 KB

re-roll to chase head, but didn't include all of yched's comments from #7, thus leaving as 'needs work'.
In particular, still need to address

+++ includes/common.inc
@@ -5165,6 +5165,37 @@ function entity_get_controller($entity_type) {
+    preg_match('!%([^/]+)(/|$)!', $info['view path'], $matches);
+    // Get the property of the entity that has the name of the wildcard.
+    $property = $entity->{$matches[1]};

We'd possibly need to support several replacements ?

and

+ We'd also need comment/%cid for comments

.

jeffschuler’s picture

Status: Needs work » Needs review
FileSize
5.3 KB

re-roll to incorporate yched's:

  • supporting multiple replacements
    • I'm not sure how one might go about handling recursive regex substitutions, so I explod()ed and foreach'ed it. There's probably a more elegant solution.
  • comment/%cid for comments

Status: Needs review » Needs work

The last submitted patch failed testing.

jeffschuler’s picture

Status: Needs work » Needs review
FileSize
5.3 KB

Re-rolled with better tools, and fixed an error I had made.

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community

Looks proper to me .. .since file module is in core now, do we have a use case yet? regardless, this is ready.

yched’s picture

It's actually imagefield that will have use cases, so this can go in as is IMO.

c960657’s picture

Status: Reviewed & tested by the community » Needs work

Very elegant.

+    $pieces = explode('/', $path);
+    // Replace wildcards in the view path.
+    foreach ($pieces as $piece) {
+      // Match the wildcard
+      if (preg_match('!(%[^/]+)(/|$)!', $piece, $matches)) {

The regexp looks too complex. $pieces is exploded on /, so $piece will never contain a slash. And (/|$) is redundant AFAICT, because the + quantifier is greedy, so it will always match until the end of the of the string (or to a slash if there was one anyway).

+        $path = str_replace($matches[1], $property, $info['view path']);

If there are more %foo placeholders in $info['view path'], only the latter is replaced, because $path is overwritten inside the loop.

I suggest something like this instead (not tested):

foreach ($pieces as &$piece) {
  if (substr($piece, 0, 1) == '%') {
    $piece = $entity->{substr($piece, 1)};
  }
}
return implode('/', $pieces);

Also, some tests would be great.

catch’s picture

Version: 7.x-dev » 8.x-dev

Imagefield just went in without this. While this looks good, it's probably Drupal 8 material at this point.

yched’s picture

Version: 8.x-dev » 7.x-dev

imagefield's way of linking to the object is a temporary hack / workaround that works for nodes but not necessarily for other entities ($element['#object_type'] . '/' . $id). I don't think this can fly for D7.

bjaspan’s picture

I agree this seems important, and I mostly agree with the comments in #14. But since code freeze is passed, we could actually do this in a much simpler way, by assuming the link is [view path]/[id] (untested):

function field_create_path($entity_type, $entity) {
  $info = entity_get_info($entity_type);
  list($id) = field_extract_ids($entity_type, $entity);
  return $info['view path'] . '/' . $id;
}

We keep the part of the patch that adds 'view path' to entity info.

If we actually do #606994: Move entity handling out of Field API, which is just renaming (or copying to preserve the API) field_extract_id() to entity_extract_id(), then this can be used system-wide.

yched’s picture

re #17: I do like the approach in #11, but if we feel pressed by time, we could also decide we don't support multiple replacements in the path pattern ?

sun’s picture

The only other thing this should support is an optional $vid

yched’s picture

Question for catch / bangpound:
In D6 we had taxonomy_term_path() and hook_term_path() : http://api.drupal.org/api/function/taxonomy_term_path
This does not fit with the "'URL to $object' as fixed pattern in hook_entity_info()" approach here, but I can't seem to find the equivalent in D7 - although we still have $vocabulary->module (which doesn't seem to be used anywhere now).

Was this removed on purpose in D7 ? Is this supposed to be doable from contrib ?
Can we think of other reasons why we should move to a CALLBACK_entity_url($object) approach instead ?

yched’s picture

Status: Needs work » Needs review
FileSize
6.16 KB

Let's finally get this fixed.

Spending some more time going back and forth on this, I think a callback-based solution would be more flexible and future proof.

For instance, Barry worked on a 'combofield as fieldable field' approach some time ago. I'm not sure he went on with it, but this is a case where determining the path to generate for an imagefield 'as link' formatter needs some computation.

Attached patch does that. Should even be a tad faster.

catch’s picture

Status: Needs review » Reviewed & tested by the community

Can't find anything to complain about, we removed hook_term_path() in favour of hook_url_alter(), however this would be a more performant solution for modules wanting to recreate what we had in D6, since hook_url_alter() runs on everything. It's a small API change for modules which expose their own entity types, but these are very few, if at all, at the moment.

webchick’s picture

Correct me if I'm wrong, but don't we lose functionality here? How does Taxonomy Rewrite module change the path returned by taxonomy entities with this approach?

sun’s picture

1) These are internal paths, not URLs - URL rewriting happens later.

2) A hypothetical taxonomy_entity_rewrite.module would implement hook_entity_info_alter() to replace the path callback function with its own.

webchick’s picture

Status: Reviewed & tested by the community » Fixed

Ok, committed to HEAD. Thanks!

webchick’s picture

Status: Fixed » Needs work
Issue tags: +Needs documentation

Oops. Needs documentation.

catch’s picture

Priority: Critical » Normal
yched’s picture

Thks webchick.

I'm not sure I see what needs to be documented here. This doesn't affect D6->D7 module upgrade, AFAICT ?

Dave Reid’s picture

Holy crap, I love you all. This is a great addition to entity API. And a huuuuuge helper for Pathauto D7.

webchick’s picture

Status: Needs work » Fixed

Oh, I guess I was assuming somewhere we had some nice documentation about how to create entities, which would need to be adjusted to take this change into account.

HAHAHA I KILL ME. :(

Ok, setting back to fixed then.

Dave Reid’s picture

Status: Fixed » Needs review
FileSize
614 bytes

Oops, we're missing the path callback in user_entity_info().

sun’s picture

Status: Needs review » Reviewed & tested by the community
webchick’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs tests

Committed. But you know what that means, right? :P

scor’s picture

As discussed with catch in IRC, this approach does not fully support the case of the comment entity. The comment path callback value cannot be reused in the theme layer because it's a path internal to Drupal and it cannot include a fragment, as a result, in template_preprocess_comment() we end replicating (twice!) the logic of what comment_path() is supposed to do:

  $variables['title']     = l($comment->subject, 'comment/' . $comment->cid, array('fragment' => "comment-$comment->cid"));
  $variables['permalink'] = l('#', 'comment/' . $comment->cid, array('fragment' => "comment-$comment->cid"));

Also, the code above costs us an extra call to url() via l(): this is to be multiplied by 50 comments / page or more. We should allow the entity path callback functions to return a structure containing the path but also optional options compatible with url():

/**
 * Entity path callback.
 */
function comment_path($comment) {
  return array(
    'path' => 'comment/' . $comment->cid,
    'options' => array('fragment' => 'comment-' . $comment->cid),
  );
}

EDIT: ignore the extra call to l(), this will be dealt in a different issue, which will leverage the full support or url friendly entity path callback and make it cacheable by entitycache.

scor’s picture

Status: Needs work » Needs review
FileSize
2.14 KB

and here is the patch

catch’s picture

Status: Needs review » Needs work

Just in case 'path' isn't the first item in the array, current(entity_path($obj_type, $object)) take the array key directly instead of using current.

scor’s picture

Reading #699440: Add bundle support to entity_uri callback to remove performance overhead of forum_url_outbound_alter() (regression from D6) might help to understand better what's the reason behind the patch #35.

@catch: I was trying to do that in one line but afaik PHP doesn't allow you to do that :(

yched’s picture

Agreed on the approach, I got "comment/$cid" wrong.

Just wondering: if the function returns an array with keys 'path' and 'options', is entity_path(), 'path callback', [entity_type]_path() terminology still appropriate ?

catch’s picture

@scor, yeah that's a two-line job :(

On terminology, hmm. Doesn't seem ideal, but can't think of anything better.

scor’s picture

the terminology needs to be changed, but could not find something appropriate. we need something broader than path. how about url_structure or url_elements?

or simply url? entity_url(), 'url callback', [entity_type]_url()

catch’s picture

Would url give the impression that it's a fully formed url, rather than a url() friendly array though?

yched’s picture

Yes, 'url' doesn't apply here.

Well, I don't think we have any name in core for the association of 'a drupal path + querystring and fragment options', so _path() might just be the best option.

scor’s picture

Status: Needs work » Needs review
FileSize
2.19 KB

solving #36:

Just in case 'path' isn't the first item in the array, current(entity_path($obj_type, $object)) take the array key directly instead of using current.

yched’s picture

Status: Needs review » Needs work
+++ modules/image/image.field.inc
@@ -461,7 +461,8 @@ function image_field_formatter_view($obj_type, $object, $field, $instance, $lang
-    $path = entity_path($obj_type, $object);
+    $path_elements = entity_path($obj_type, $object);
+    $path = $path_elements['path'];

If entity_path() can have a 'path' and 'options', then the image formatter should take both into account, right ?
Else it won't work for linking to comments.

Powered by Dreditor.

catch’s picture

Also do we need to update the docs for hook_entity_info()? It currently says "path callback: A function taking an entity as argument and returning the path to the entity. "

scor’s picture

Status: Needs work » Needs review
FileSize
3.55 KB

patch addressing #44 and #45.

scor’s picture

ignore patch above, this is the right one. I still think we need to have a better name than 'path' for the path and options elements, lines such as this are confusing:

    $path = $variables['path']['path'];
yched’s picture

+++ modules/image/image.field.inc
@@ -502,7 +502,12 @@ function theme_image_formatter($variables) {
+    $path = $variables['path']['path'];
+    $options = empty($path['path']['options']) ? array() : $path['path']['options'];

Hm, If I'm not mistaken, this should be
$path = $variables['path'];
Then you have $path['path'] and $path['options'] ?

Optionally, the formatter could do
$path = entity_path($entity);
'#path' = $path['path'];
'#path_options' = $path['options'];
and let the theme function be a little simpler.

I also think entity_path() should always add an empty 'options' array(), if [entity]_path() callback provides none, for easier DX.

+++ modules/image/image.field.inc
@@ -502,7 +502,12 @@ function theme_image_formatter($variables) {
+    $options = array_merge($options, array('html' => TRUE));

why not simply $options['html'] = TRUE; ?

Powered by Dreditor.

scor’s picture

The key '#path_options' in image_field_formatter_view() does not get passed to theme_image_formatter().

I've made the path 'options' key default to array().

scor’s picture

Title: 'as link' formatters need a generic way to build the url of an 'entity' » Entity path callback does not work for comment and should be more generic

Discussed this with catch on IRC. The 'path' term is too specific here. I'd like to suggest the broader term 'uri' which includes the 'path' and the various 'options' like fragment and query which can be used to build the uri of an entity (comment entities have a fragment in their uri for example).

uri callback: A function taking an entity as argument and returning the uri elements of the entity, e.g. 'path' and 'options'. The actual entity uri can be constructed by passing these elements to url().

The '#path' key in the formatter would need to be aligned with the fact that we can have a uri instead of a path only.

scor’s picture

just realizing I forgot to attach the patch in the previous comment... :/

Status: Needs review » Needs work
Issue tags: -Needs tests, -Needs documentation

The last submitted patch, 525622_comment_path_50.patch, failed testing.

catch’s picture

Status: Needs work » Needs review
Issue tags: +Needs tests, +Needs documentation

#51: 525622_comment_path_50.patch queued for re-testing.

catch’s picture

Status: Needs review » Reviewed & tested by the community

This looks great. I can't think of anything other than 'uri' which describes this somewhat accurately, and the url() friendly array is something we've done in a lot of functions. It's a teensy API change, but the path callback only went in a couple of weeks ago, isn't documented yet, there's barely any entity modules floating around, and it qualifies as a proper bug since it's actually the wrong thing for comments in HEAD - so we should get this in quick and /then/ document it.

jherencia’s picture

Subcribing.

webchick’s picture

Status: Reviewed & tested by the community » Fixed

Committed to HEAD.

catch’s picture

Status: Fixed » Needs work

Oooh, thank you. Back to cnw for docs.

effulgentsia’s picture

effulgentsia’s picture

For people who worked on this issue, I would really appreciate your input on #823428: Impossible to alter node URLs efficiently. Dries and chx are questioning that patch, and therefore, I'm asking the community for how to make sense of the entity_uri() function. Thanks!

yched’s picture

reverting to the original issue title, for later reference

yched’s picture

Title: Entity path callback does not work for comment and should be more generic » 'as link' formatters need a generic way to build the url of an 'entity'
jhodgdon’s picture

As per #57 - can you clarify what docs need to be written?

If it's an entry in the module/theme update guide that's needed, please change the tag to "Needs Update Documentation". If it's something else, you can leave the tag at "Needs Documentation", but please explain what is needed.

  • webchick committed 83c9734 on 8.3.x
    #525622 by Frando, yched, et al: Add entity hook callbacks so that 'as...
  • webchick committed b3d9f5f on 8.3.x
    #525622 follow-up by Dave Reid: Added missing path callback property.
    
    
  • webchick committed 52195a6 on 8.3.x
    #525622 by scor, catch, and yched: Allow Entity path callback to deal...

  • webchick committed 83c9734 on 8.3.x
    #525622 by Frando, yched, et al: Add entity hook callbacks so that 'as...
  • webchick committed b3d9f5f on 8.3.x
    #525622 follow-up by Dave Reid: Added missing path callback property.
    
    
  • webchick committed 52195a6 on 8.3.x
    #525622 by scor, catch, and yched: Allow Entity path callback to deal...

  • webchick committed 83c9734 on 8.4.x
    #525622 by Frando, yched, et al: Add entity hook callbacks so that 'as...
  • webchick committed b3d9f5f on 8.4.x
    #525622 follow-up by Dave Reid: Added missing path callback property.
    
    
  • webchick committed 52195a6 on 8.4.x
    #525622 by scor, catch, and yched: Allow Entity path callback to deal...

  • webchick committed 83c9734 on 8.4.x
    #525622 by Frando, yched, et al: Add entity hook callbacks so that 'as...
  • webchick committed b3d9f5f on 8.4.x
    #525622 follow-up by Dave Reid: Added missing path callback property.
    
    
  • webchick committed 52195a6 on 8.4.x
    #525622 by scor, catch, and yched: Allow Entity path callback to deal...

  • webchick committed 83c9734 on 9.1.x
    #525622 by Frando, yched, et al: Add entity hook callbacks so that 'as...
  • webchick committed b3d9f5f on 9.1.x
    #525622 follow-up by Dave Reid: Added missing path callback property.
    
    
  • webchick committed 52195a6 on 9.1.x
    #525622 by scor, catch, and yched: Allow Entity path callback to deal...