Problem/Motivation
Currently, there is no central definition of the path to an entity. Sure there's its route/menu item, but we can't reverse associate from that.
Sure entities now have a uri() method, but you need an entity object for that. Also, the default implementation still uses a uri callback function, for no reason I can fathom.
In rest.module, we need to be able to dynamically generate paths to not individual entities, but entity types. That is, we need to be able to dynamically generate the route path pattern for an entity.
Field API still uses hook_menu-style paths for wiring up the admin pages. That's... going to break as soon as hook_menu-based routes go away.
A number of other issues are circling the same problem space of needing to deal with links to entities in the abstract. Among them:
#1783964: Allow entity types to provide menu items
#1839516: Various subsystems require an Entity Operations API. Introduce one.
Proposed resolution
Bite the bullet. What we really need here is to define a canonical path TEMPLATE for an entity type. When I say template, I mean the uri-template specification, RFC 6570:
http://tools.ietf.org/html/rfc6570
A subset of that specification is what Symfony/Drupal 8 use for route patterns, and is also rapidly becoming the standard for REST services anyway. (I'm using it for a non-Drupal HAL-based project now.)
In particular, we should define multiple such patterns. One for each operation, as identified by a link. Did I say operation, as in relevant to #1839516: Various subsystems require an Entity Operations API. Introduce one.? Why yes I did. :-)
What does "identified by a link" mean? I mean using one of the IANA standardized link relationships (which collects relationships defined by IETF RFCs as well as the W3C):
http://www.iana.org/assignments/link-relations/link-relations.xml
It's possible to define your own, but there's plenty there we can already use. And, we can then use that standardized knowledge all over the place; for entity operations, for rest.module, for link tags in HTML (semantic web, baby!), all kinds of stuff!
The natural way to identify such patterns, IMO, is in the annotation for an entity. It's data we explicitly want in the absence of an actual entity object, which therefore ends up in the annotation.
To wit, nodes might look something like:
links = {
"canonical" = "/node/{node}",
"edit-form" = "/node/{node}/edit",
"create-form" = "/node/add/{entity_subtype}",
"version-history" = "/node/{node}/revisions"
}
For Drupal-specific logic, we can add additional operations whose link is a namespaced property. For instance:
links = {
// ...
"drupal:field-configuration" = "admin/structure/types/manage/{entity_subtype}"
}
(To be strict, we should then define drupal:field-configuration as an alias for a URI that describes what the heck that means, but we may skip that for the moment.)
Benefits:
- We can, likely, generate the routes for all of those operations dynamically, thus resolving [#178396]. That would also imply a standard name format for entity-based routes, which would be good for DX predictability.
- rest.module can generate RESTful routes for all entity types it needs that match the path of the HTML version of the entity, which is what we want.
- rest.module can inject all of those relationships into the _links array of an entity when serialized, meaning we can tell client apps everything they need to know about how to use an entity, using industry-standard definitions. Hypermedia FTW.
- We can do the same in HTML
<link>
elements. Hypermedia FTW, again. (What could we do with that information client-side? The mind boggles!) - We can greatly simplify the logic in Entity::uri(), As it would just need to grab the canonical link and str_replace() in the entity ID. Or, get this, uri() can take *any* supported relation and generate that link. To wit:
<?php
public function uri($relation = 'canonical') {
$info = $this->entityInfo();
$pattern = $info['links'][$relation];
// Do some str_replace() to turn $pattern into a complete string.
return $uri;
}
?>(Or soething along those lines.) And now you can ask an arbitrary entity what the URI of that entity's edit form is, or its Field UI configuration page. How cool is that??
- It's not a complete entity operations API, but it can be leveraged by one fairly easily.
- I *think* (although I'm not certain), that this would allow for entity-specific paths that include other entities, such as /node/{node}/comment/{comment} or /myentity/{entity_subtype}/{myentity}, if any custom entities were so inclined. Because uri() is responsible for generating the actual links, and it can know its context and the meaning of different placeholders, that class would know how to get at the data it needs for that placeholder.
We likely could not support the entire uri-template spec, but
Remaining tasks
Agree on the above. :-) And then someone needs to code it, stat!
User interface changes
Probably none, or minimal.
API changes
hook_enity_bundle_info() becomes thinner, which it needs to since it's still designed for the old routing system.
Entity::uri() becomes a lot simpler.
We probably lose the ability to do per-bundle paths. But, really, with path aliases I don't think that's a problem.
Related Issues
Issues this would help, replace, or obsolete:
#1783964: Allow entity types to provide menu items
#1839516: Various subsystems require an Entity Operations API. Introduce one.
#1968970: Move translation_entity.module specific properties out of the Entity type annotation