This is a multi-faceted issue, which is why this meta is here to explain the big picture. This spans several subsystems, including routing, REST, and Entity API. I'm filing it under Entity API for now. Grab a drink. :-)
Pending changes
Although core does not currently enforce it, all entity route names MUST use the format entity.$entity_type.$link_relationship. In future betas some functionality will not operate correctly if entity routes do not follow that format.
As of beta1 entity definitions specify route links to route names. That will be changed in a future beta to specify uri templates (eg, "/node/{node}") instead. That will allow the HTML routes for entities to be auto-generated by the system rather than requiring module developers to redundantly define routes in a routing.yml file. Once that mechanism is in place defining entity routes manually in routing.yml will be discouraged.
Problem/Motivation
Currently, Entities define a set of "links" in their annotation. These links use the name of an IANA link relationship, but specify a route machine name. The "canonical" link is the only one that is currently used as far as I'm aware (with one exception below). That, per spec, identifies the "real" and official (ie, canonical) URI for a resource.
Currently, the links we specify in the HTML head of nodes are manually connected. That is, we have a custom controller for nodes that iterates all link relationships on the node and adds them as links to the HTML head. That is only marginally useful however (it's good, but not the best we could do) and we don't do much else with that data. In particular, we're not using it in the REST module.
Currently, we have no way to define new link relationships. That's a problem for anything RESTful.
Currently, REST module builds routes for entities dynamically based on the links defined on the Entity, specifically canoncial. However, because that means building routes off of not other routes, but just machine names for routes, we had to introduce an ugly hack of a stateful service into RouteBuilder. While it works, the general consensus is that it's a bad thing (stateful services generally are) and we can and should do better: #2158571: Routes added in RouteSubscribers cannot be altered
Currently, Entities are tightly coupled to the routing system because they have to define a route in order to be complete, and that route must (realistically) be a text/html route. That's bad, as there's no good reason that entities should be coupled to Symfony routing. There's also no reason to assume that an entity must have a text/html representation. Commerce, for instance, has several entity types that have no useful HTML representation but a good commerce API must allow for RESTful read/write of them.
Currently, when creating a new entity one has to manually define a series of routes, which may or may not require defining multiple controllers, and then reference those from the entity. That's not very friendly DX.
We want module developers to be able to leverage all the RESTful linky semantic goodness we can without having to learn what all that RESTful linky semantic goodness is, at least to the extent possible.
All of these issues overlap, and all relate to the same core issue: We need to fully embrace RESTful resources, and couple Entities not to routes but to URIs.
Proposed resolution
The following plan is the result of discussion between Crell, dawehner, EclipseGc, and pwolanin.
First, to clarify, Entities are persistent objects within a data store system. Resources are URIs in a RESTful system. That is, Resource and URI are 1:1. However, there is absolutely no requirement that an Entity and a Resource have to be 1:1, and if they are that's generally a bad sign.
For Drupal, we have already made the implicit decision that while not all Resources are entities, all entities are (or can be expressed as) Resources. The design of the REST module is built on that assumption. We should go ahead and make that decision explicit and leverage it in the Entity system.
(For example, /node/5 is an entity, and is a resource. /node/5/edit is a resource for the edit page of node 5, but it does not correspond to node 5. That is, /node/5/edit is a resource but not an entity.)
To that end, we want to treat Entities as RESTful resources; remember, HTML is a REST response, just one with a particularly complicated formatter. Doing so allows us to simplify a fair bit of code and automate a whole crapload of stuff, which is good for DX. It also lets us get closer to for-reals Level 3 Hypermedia (Hypertext As The Engine Of Application State).
The end-game we want to achieve is as follows:
1) We have a defintion of "link relationships", via plugins. That includes IANA relationships as well as our own custom ones. A link relationship consists of, at minimum, a machine name and a relationship name (a URI). In the case of web standard relationships those will be the same. That is, "canonical" is a relationship of "canonical" while "edit-form" is a relationship of "http://drupal.org/rels/edit-form" (which is how custom relationships are supposed to be defined). See #2113345: Define a mechanism for custom link relationships for more details.
2) All entities define the relationships to other resources (not necessarily entities) that make sense for that entity. The most obvious is canonical, but other common examples would include edit-form, delete-form, version-history, admin-form, etc. Some of those are IANA-defined, some not. These are defined as URI templates (ie, the same format we use in routes, with {} placeholders). To most module developers, these can be interpreted as "put my edit-form here, put my admin-form here, put my version-history here". To REST-savvy developers, these can be interpreted as "the relationship to my edit form is X, the relationship to my version-history is Y".
3) Entity module will automatically generate routes for entities based on a select number of link relationships. For the first cut this list will likely be hard-coded into a route callback service. (The example relationships above are all good examples of ones we would want to generate.) These routes would be for text/html, and leverage the full automation of Entity API. That is, entity form handlers, view handlers, entity_access, etc. A toggle on the Entity annotation can enable/disable this behavior if desired (eg, for Commerce to surpress the automatic HTML routes for entities for which those are not needed).
4) REST module, when enabled/configured, will generate routes for all entities as it does now using the canonical link relationship directly rather than extracting the pattern template from an existing route. It will also generate different routes for GET, POST, PATCH, DELETE, different Accept headers, etc. All of that logic already exists in HEAD aside from using the link relationship directly.
5) For entities that have a need of a non-standard controller, they may override the default route. After some discussion I believe the most straightforward way to do that is to simply allow them to use the existing Routing::ALTER event and do whatever they want. Alternatively, we could say that statically-defined routes that match the automatic route name are left intact and the auto-generating logic skips them. In practice, I expect this to be a fair number of core-defined entities (at least in the near term) but fairly few custom/contrib entities.
The net result for most module developers is that they can define a new typical entity type with just the one entity class and interface. All of the pages and displays and so forth can be built automatically, and the various handlers (nee controllers) for that entity type are already safe to leave at the defaults in most cases. Also, route names for entities become very predictable. That is a major DX improvement for Entity API, even without the RESTful benefits.
Once those pieces are in place, the following extensions become not only possible but really really cool:
6) REST module may attach *all* link relationships to an entity resource on generation. That is, a HAL-rendered entity will include links to its edit-form, version-history, etc. automatically.
7) Some step in the rendering pipeline (TBD) can automatically add all those same link relationships to the HTML page representation of an entity. That is, the HTML head element can automatically contain all of those link relationships with no further work by the developer.
8) Contrib modules may define their own additional arbitrary link relationships (plugins) as needed, in a fully supported fashion that can automatically be leveraged by points 6 and 7. (This is implicit in the existence of point 1.)
9) Modules may, via existing entity_info_alter functionality, add additional link relationships to an arbitrary entity. (This functionality already exists.) Those new relationships are then automatically included in points 6 and 7.
10) We can add a mechanism to allow per-entity addition of link relationships. (Exact mechanism TBD.)
11) The Entity Reference module and similar modules may, via plugin derivatives, automatically define a custom link relationship for all ER fields. It may then inject those relationships into entities using that field via the mechanism defined in point 10.
The net result is that simply putting a field onto a node type in the UI will result in a semantically appropriate link relationship being added to that node, both in its HTML page representation and its HAL representation. Site builders don't even need to know it's happening, just that, poof, they get semantic yummy goodness that makes RESTful APIs easier to write. Which is, let's face it, pretty damned cool and a key part of the "RESTify Drupal" efforts we've been working on for so long.
Remaining tasks
Points 1-5 above are API changes to core functionality or APIs. I believe they should all be treated as beta-blockers.
Points 6-11 should be doable without API changes, only API additions, if that. Therefore while we should do them sooner rather than later they are not beta-blocking. In fact, I believe most of them could be done in 8.1 if worst comes to worst. (If someone feels that's not the case, explain why and I can bump it up.)
To make the above happen, a couple of steps need to be taken with some ordered dependencies. The following must be done before RC, and are possible small API breaks:
- #2281645: Make entity annotations use link templates instead of route names - Switch entity link definitions to use uri templates, not route names.
- Convert entities back to use URI templates in links. This may be multiple issues.
- #2350503: Add route generation handlers for entities - Dynamically generate text/html routes for entities using the standard Entity API tools. (_entity_view, _entity_form, _entity_access, etc.)
- #2350509: Implement auto-route generation for all core entities and convert all of the core entities. - Use dynamic generation for all routes.
The following are non-API-changing, so can be done at any time before RC or in 8.1 (or maybe even contrib):
- #2113345: Define a mechanism for custom link relationships - Create the plugin system to define link relationships. We can do that independent of leveraging them in the Entity definitions as long as we don't mind a system that "isn't used yet but will be soon". (We've often balked at that; I believe in this case it would be unwise to do so as it would just slow down the rest of the process.)
- #2282029: Automate link relationship headers for all entity types, not just nodes - Automatically add link relationships to the HTML head of all entity pages. Doing so will allow us to remove some special case code and allow more entities to use the default routes.
- Automatically add link relationships to supporting formats in the Serializer pipeline. (Details of the implementation here are best decided by Lin Clark.)
- Automatically generate link relationships based on relational fields, such as Entity Reference.
- Devise a mechanism for adding links to individual entities at runtime. This needs to be done at the entity level, not the rendering level, which means that "just use a view subscriber and call $page->addLink()" is not sufficient as that would not affect HAL-rendered entities.
- Add Entity Reference et al links to relevant entities via the mechanism specified in the previous step.
User interface changes
Should be none or very few.
API changes
- The links array in the Entity annotation switches back to URI templates, not route names.
- Route names for Entity-based routes become largely automatic based on a pattern to-be-established. (I'd suggest $entity_type.relationship off hand, but we can bikeshed that elsewhere.)
- An API gets defined to define link relationships. This is an addition, not a change.
Comments
Comment #1
Crell CreditAttribution: Crell commentedComment #2
tim.plunkettComment #3
dawehner#2089757: Auto generate routing entries for entities based on an 'admin_path' and 'admin_permission' annotation is certainly one subissue.
Comment #4
dawehnerComment #5
fagoWhile I can see how the suggestion works out, I'm not sure I get the full problem and why the new solution is really better.
Entities are coupled to the routing system as they come with built in support for URLs. When the routes should control the URLs of an application, I think the the coupling makes sense.
I'm not sure why the route would have to be a text/html route though - because that's what people would expect? If yes, I do not see how the suggestion would fix that?
Auto-generating routes would be certainly a nice addition, yep.
Yes, I can see how that's a problem, but I'm not sure how that relates to defining routes. Adding a way to expose link relations seems reasonable as well as making use of that automatically, but I fail to see why that requires us to change the definition of the link relations? How would the new entity type annotation look like and what would solve it for us?
Comment #6
Crell CreditAttribution: Crell commentedThe new entity type annotation would look almost exactly like it looked originally, when the link array was added:
etc.
This issue straddles the entity system, rest system, and routing system, which is why it's been so hard to pin down and explain to date. Hence the big writeup. :-)
I think this is the key confusion point. Entities come with built in support for "being exposed as a REST resource" (which is sometimes an HTML page). They should not be coupled to a hand-crafted YAML file if we can help it. Too, the status quo, as noted, is a big problem for REST module, and it means we cannot do good dynamic link relationship handling (for HTML or HAL).
As is, we cannot auto-generate routes. Even without the RESTful implications, the DX win of defining all of an entity in one place and letting Drupal automate the rest (including standardizing machine names for entity routes, which in turn helps learnability just like EntityInterface does) is to me a compelling argument.
As for the dynamic HTML routes having a _format requirement of text/html, I suppose that's optional. I'd favor tighter-fltering routes in general, especially if automated, but it shouldn't be a big deal either way.
Comment #7
fagoI can follow that argument. But so far I thought it's the goal that developers can re-structure all/most of the URLs just by altering the routes, i.e. the routes are the place which control the URLs of a site. But when entities define the URLs, is it still possible to override it on route level? Or said differently, is the URL in the entity annotation just a default for the route or the URL what gets used everywhere?
Comment #8
xjmVery thorough writeup.
We discussed this issue this morning on the Drupal 8 call and agreed that the general API improvement here isn't beta-blocking. There's a risk of a race condition following #2158571: Routes added in RouteSubscribers cannot be altered that we should file an explicit critical bug report for (related issue: #2089757: Auto generate routing entries for entities based on an 'admin_path' and 'admin_permission' annotation). The rest of the issues from this meta will probably be non-critical issues that are either beta deadline (e.g., changing all route names/patterns) or RC deadline (e.g., changing entity annotations for the URI).
Comment #9
catchTagging this as 'beta target' so we get an overview of what's in/out by then.
Is there already the critical bug report for the race condition?
Comment #10
fagoMy concern in #7 is actually related on the routing system staying in control of the URLs used, but if crell does not think that's a problem I trust his evaluation here. The proposed DX in regard to the entity annotation looks good to me.
Comment #11
Grayside CreditAttribution: Grayside commentedAuto-generated URI's from Drupal internals are not always the best API design. So long as it's possible to create an alternate API surface while leveraging Drupal this is not a problem. If this does hard-wire Drupal's REST layer to the internal data structures, then Drupal is still stuck at Domain-Specific API DX: 2007 Edition.
Comment #12
Crell CreditAttribution: Crell commentedThe REST module is an automation tool for the common case. The routing system still lets you wire whatever-you-want to whatever-URI-you-want.
That said, URIs *shouldn't* matter to a HATEOAS/REST client.
Comment #13
Crell CreditAttribution: Crell commentedPer discussion with effulgentsia at DrupalCon, updating the issues list and moving some things to non-beta-blocking.
Comment #14
Crell CreditAttribution: Crell commentedComment #15
Crell CreditAttribution: Crell commentedComment #16
ioseb CreditAttribution: ioseb commented@Crell
Is there any reason why the "edit-form" is a relationship of "http://drupal.org/rels/edit-form"? Maybe I'm missing the point but "edit-form" and "create-form" link relation types are well defined by RFC6861(http://tools.ietf.org/html/rfc6861) and are registered in IANA registry(http://www.iana.org/assignments/link-relations/link-relations.xhtml)
Comment #17
Crell CreditAttribution: Crell commentedBecause we've been working on this line of work since before January 2013 when that RFC was published and I didn't realize they were there. :-) Great. More IANA standard relationships that we can leverage can only be a good thing for us.
Still, there will be non-IANA relationships we need so the mechanism/API need is the same.
Comment #18
jhodgdonHaving just gone through the **currently extremely painful** process of trying to define a new entity, in the course of writing a draft of the 2nd edition of my Drupal programming book, I think we should make this a beta blocker rather than a beta target.
In my opinion, the process of defining an entity now, with all the controllers and forms and routing and everything, is SO bad that I wouldn't wish it on anyone. It took me hours of debugging and I still ended up with some kind of lame code that works but is not elegant, because I couldn't get certain aspects to work... Having a system where all that stuff would be auto-generated makes a TON of sense.
What can we do to ensure this actually happens?
Comment #19
chx CreditAttribution: chx commentedTell me I am stupid please but I thought node/5/edit-form is not something REST needs; that it uses node/5 and POST to change the entity.
Comment #20
jhodgdonActually I see this is not really about routes, more about REST... I have filed a separate issue regarding the DX of entity definition in general: #2316171: [meta] Improve DX of entity defining (if you want a UI)
Comment #21
Crell CreditAttribution: Crell commentedREST module I don't think will be doing anything at node/5/edit. However, it can provide a link to it, and with it being a defined relationship we can provide that link automatically in the JSON and HTML versions of a page. And auto-create the routes for it. And auto-generate documentation like any other link relationship we have. And do all kinds of other automation later.
We can also, again, later, automate having a RESTful version-history of old revisions to go along with the HTML one. (Or, actually, automate the HTML one too if we really want.) Canonical is the most important, but every link we add is another piece of "automatic semantic web linking goodness" we can build on. (As well as a way to reduce the mental overhead for developers defining entities and wanting an automatic UI built out for them.)
Comment #22
Crell CreditAttribution: Crell commentedPer discussion at DrupalCon Amsterdam, and approved by catch...
Comment #23
Crell CreditAttribution: Crell commentedComment #24
andypostlooks all issues are complete, can we close this?
Comment #25
Crell CreditAttribution: Crell at Palantir.net commentedThere's still 2 child issues in CNW...
Comment #26
dschenk CreditAttribution: dschenk commentedHi guys. I'm not sure if the the following issue has already been raised, I'd like to make you aware of: #2454915: Entity link annotations in HTML head are not valid HTML.
In short, the following code is not valid HTML:
Output of the w3c validator:
Comment #27
BerdirThe short answer is, that validation there is stupid, the list of allowed types is partially defined by a wiki, there is no official standard. See #2406533: edit-form, delete-form etc. <link> tags added on /node/{node} are invalid according to W3C Validator
Comment #28
xjmComment #31
Wim LeersI was working on #2293697-124: EntityResource POST routes all use the confusing default: use entity types' https://www.drupal.org/link-relations/create link template if available, which led me to do issue queue archeology and stumble upon this issue.
The IS talks about "link relationships", but they're actually "link relation types". A link relationship uses a link relation type.
The IS has an end game listed:
This just landed (January 30, 2017).
This was done in #1970360: Entities should define URI templates and standard links.
Its design is flawed though: registered link relation types can use the registered name, but extension link relation types (i.e. Drupal-specific ones) must be identified via a URI. We can only fix this in D9.
This has kinda happened, with the concept of "route providers". See #2578955: Implement auto route generation but DON'T use it for all core entities.
This was done in #2019123: Use the same canonical URI paths as for HTML routes. Sadly, the
POST
route was not handled and in fact mostly ignored. Which is why to this day I'm struggling with #2293697: EntityResource POST routes all use the confusing default: use entity types' https://www.drupal.org/link-relations/create link template if available, which I've first posted a comment in on February 3, 2016. We're now a year further.Done; see the concept of "route providers" again. And route altering is indeed also still possible.
The IS then went on to say these cool things would be enabled:
As of #2113345: Define a mechanism for custom link relationships, that is the case :)
This has been the case since #1970360: Entities should define URI templates and standard links, but only for nodes. Even today,
EntityViewController
doesn't contain this, butNodeViewController
does. We can quite easily move that logic fromNodeViewController
toEntityViewController
though.The IS mentions #2282029: Automate link relationship headers for all entity types, not just nodes as the issue where this should happen. It hasn't been worked on since June 2014, but we can push it forward again, albeit using a slightly different approach to not break BC.
Correct, but sadly only as of two days ago. And sadly only for nodes for now.
This is basically the same as the previous point. (Also, it's
hook_entity_type_alter()
.)This has not yet happened. And I don't think it will happen soon.
Although arguably this is already supported in a different way: rather than adding the link to a single entity of a certain entity type, just add it to the entity type. It's then up to the implementation to only add the link relation if the URL it points to is accessible for the current user. That's what #2406533: edit-form, delete-form etc. <link> tags added on /node/{node} are invalid according to W3C Validator did.
Also not yet happened. But also, the same "although" applies, especially combined with
hook_entity_type_alter()
.In other words:
#6:
I don't think this is accurate. In fact, I think it's objectively a very euphemistic oversimplification. Entity type classes are completely unaware of any REST stuff. If entity types came with built-in normalization logic, then it'd be different. In fact, I almost wish this were the case at this point. Symfony's serialization system — which we adopted wholesale — is actually designed for exactly this kind of approach: given an object, specify its normalization. But our
Entity
objects containFieldItemList
objects which containFieldItem
objects which can be specialized to a particular subclass. Which means we need normalizers not just per entity type (which would be simple to understand), but for every possible field item. This makes sense because our entity objects are user-extensible (user-configurable fields). But it makes for a particularly brittle, painful architecture with massive BC headaches. As #2751325: All serialized values are strings, should be integers/booleans when appropriate and similar "serialization gap" issues in #2794263: REST: top priorities for Drupal 8.3.x make painfully clear.Given that most of this has been done, and the remainder of the work is in #2282029: Automate link relationship headers for all entity types, not just nodes and #2848767: Automatically define custom link relationships for every Entity Reference field on an entity, I'm very very tempted to close this issue. Because it hasn't received attention in almost 2 years. Alternatively, I'm happy to keep this open to finally finish this, if people actually want to push these two issues forward.
Comment #32
Wim LeersAlso: it's quite telling that I only found this issue after about a year of working on REST.
Comment #33
Wim LeersAdding related issues mentioned in #31.
Comment #34
Wim Leers#31 said:
There was one more issue that I didn't mention in that conclusion: #2293697: EntityResource POST routes all use the confusing default: use entity types' https://www.drupal.org/link-relations/create link template if available. That finally landed, a week ago.
So now it's really down to those two issues. I started pushing #2282029: Automate link relationship headers for all entity types, not just nodes forward again just before posting #31. And I pushed it forward again today.
Comment #41
Berdir> I'm very very tempted to close this issue. Because it hasn't received attention in almost 2 years.
3 years later, without a comment from anyone else. Lets do this. We clearly don't need this issue anymore.
Comment #42
kristiaanvandeneyndeIIRC, I only came here for this paragraph:
Really not a fan of undocumented expectations, but I think I had a separate issue for that and it got better documentation in the end? So AFAIAC, sure: Close it.