Problem/Motivation
I want to be able to tell the entity param converter (EntityConverter) which bundles to be converted on route where a param is an entity ID. For instance, I want to be able to create such a route definition:
some_route:
path: /node/{node}/foo
defaults:
_controller: ...
...
options:
parameters:
node:
type: entiity:node
bundle:
- blog
- article
With this definition I would expect that passing a node ID of a node of type 'blog' or 'article' to get the {node} param converted into a node entity but when I pass a node ID of a 'page' node to get a 404, as this route business purpose is to deal only with those node types and the route should "not exist" for other kind of nodes.
Proposed resolution
I've implemented this in our project by swapping the class of the paramconverter.entity service but I see here some value that can be added to Drupal core.
Remaining tasks
None.
User interface changes
None.
API changes
Route parameters of type entity:* accept a new bundle definition property of type array.
Data model changes
None.
Release notes snippet
In order to limit the route entity parameters to certain bundles, a 'bundle' property, of type sequence, may be added to the parameter's definition.
| Comment | File | Size | Author |
|---|---|---|---|
| #4 | 3155568-4.patch | 4.59 KB | claudiu.cristea |
Issue fork drupal-3155568
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 3155568-filter-by-bundle
changes, plain diff MR !41
Comments
Comment #2
claudiu.cristeaPatch. Added also a change record.
Comment #3
claudiu.cristeaComment #4
claudiu.cristeaRenamed 'bundles' to 'bundle'. Improved docs.
Comment #5
claudiu.cristeaComment #6
claudiu.cristeaTitle change.
Comment #7
johnwebdev commentedWhat about the
_entity_bundlesrequirement? SeeEntityBundleAccessCheckComment #8
claudiu.cristea@johnwebdev, that is somehow similar just that is doing a different thing. If you pass a wrong bundle to a route that requires
_entity_bundles, you'll receive a 403 - 'access denied'.But there are circumstances when you really want a 404 - 'page not found'. Let's take this hypothetical case:
/node/{node}/sales.This route makes sense only for nodes of type 'vegetable' and 'fruits'. If a 'blog_entry' node is passed as parameter, I don't want a 403, which would mean "Hey the page exists but you cannot access it". Instead I want a 404 because that route makes no sense when a 'blog_entry' node is passed from a site's business logic perspective.
Comment #10
dimilias commentedFollowing the #8 I should say that indeed, there are cases where for different bundles you have distinct functionalities, e.g. Wanting to send a newsletter for the new blogpost content but not for a new page content. Returning 403 for the mail send page is not the best candidate to return in this case as it is really not a forbidden, it is not a functionality, non-existent.
This worked like a charm for me. Thanks.
Comment #11
catchSo #2858776: Make defining bundle-specific routes easier is the issue which added the _entity_bundles functionality, which I worked on a bit.
I think if we'd thought of this approach, we'd have done this instead. I don't think we need both - so IMO we should deprecate _entity_bundles as part of this issue, and use this approach instead - less code and doesn't require the piped format to list the bundles. Needs work to add the deprecation though.
Comment #13
claudiu.cristeaDeprecated the
_entity_bundlesroute requirement. Updated the change notice.Comment #14
claudiu.cristeaAdded #2858776: Make defining bundle-specific routes easier as related issue.
Comment #15
jonathanshaw#2858776: Make defining bundle-specific routes easier facilitates some things that this does not allow e.g node/vegetable/manufacture or node/vegetable/inventory. Just saying.Comment #16
claudiu.cristea@jonathanshaw, not sure I get your use-case. Could you elaborate?
Comment #17
jonathanshawSorry, I was not thinking properly.
Comment #18
claudiu.cristeaReady for RTBC again.
Added deprecation required in #11. Note that route access checkers cannot be deprecated following the class and service deprecation policy because such services are instantiated regardless they are not used by any route. See @Berdir's merge request comment. As per discussion I had on Slack with @catch and @Berdir, we agreed to deprecate access checker service in the same way as
LayoutSectionAccessCheckhas been deprecated in Drupal 8.However, as mentioned, we've discovered in this issue that all access checker services are instantiated regardless even some might not be used by any route. I've opened #3183036: Don't instantiate access checkers not used by any route as follow-up to fix that.
Comment #19
jonathanshaw1.
There's an inconsistency in the format here. I would have expected either
A:
or
B:
What we have in this patch is kind of hybrid. That seems OK to me though. (A) and the patch handle arrays better than (B), but (A) is not backwards compatible.
2.
Deprecated in drupal:9.2.0 and is removed from drupal:10.0.0Isn't that "will be removed before" by convention?3.
The singular
bundlemakes it sound likebundle: articleis OK. Maybe this should this bebundlesin plural. And ifbundle: articleworks, we should ideally have test coverage for it and mention in documentation.Comment #20
claudiu.cristea@jonathanshaw,
#19.1: That is out-of-scope and, anyway, that would be a change with very high disruptive potential. Moreover, #19.1.B is not allowing multiple bundles.
#19.2: I've strictly followed the Drupal core deprecation policy regarding classes.
#19.3: Well this was on my mind from the first time, but as this is an "machine readable" key we are using in many places singular, even we are referring an array. Still not strong feeling. The string fallback (when there's only one item) was also an my mind but I really don't like these kind of "magical tricks". I prefer to be strict, pass it as array or leave it.
Comment #21
jonathanshawFair enough.
Comment #23
catchCommitted f6bde54 and pushed to 9.2.x. Thanks!
Comment #25
extexan commentedI'm having trouble finding the right syntax to do what we need to do re: our "add content" routes.
Some example URLs in our client's site are:
We need to use the "edit" examples above instead of /node/123/edit in order for our menu highlighting to work. That is, when you are editing customer 123, the "Sales" menu is expanded, and the "Customers" submenu item is in bold.
The overrides we've added for edit and delete routes are working fine. But we also want to do the same for "add" routes, like this:
That's what I can't get working. It was working fine in D8, but not in D9. The biggest problem, it seems, is that we have no "variable" part of our route. Meaning {node_type} is not in the URL. Some of the things I tried before I found this issue worked, but made our URLs come out like this:
We don't want that in our URLs. I've also made it work by defining the paths like this:
But that makes the actual URL look like this:
...which we also don't want. Using the node.add configuration from the Drupal core node.routing.yml as a starting point, adding what I've learned from this thread, I've tried everything I can think of to get this to work. This config...
...gives a "Missing bundle for entity type node". I can "fix" that error by adding back something in our original config, which is:
Note the addition of "node_type" under "defaults". This gives a "Page not found" response.
I think the issue stems from the fact that this new "bundle" option is under the "node_type" entry of "parameters". We don't have (and don't want) a "node_type" parameter in our URL, so specifying those parameters in our routing config is not really doing anything.
Is there a way to get these URLs to work as we intended?
Comment #26
jonathanshaw@ExTexan please don't use closed issues for requesting support. The best way to get help with this is probaly Drupal Answers.
Comment #27
extexan commented@Jonathan,
Sorry about that. I figured this would be the appropriate place for my question for two reasons... 1) Previous comments have examples of how to use this feature (examples that didn't work for me), which would help anyone answering my question get up-to-speed on the issue more quickly; and 2) The devs who implemented this feature have commented here, so by default, they are following this issue; I thought they would be my best source for an answer.
But, no worries, I'll create a new support issue.
Comment #28
dpiCreated a revision version of this issue at #3273229: Filter by bundle in EntityRevisionParamConverter route param converter. Ideally this issue makes it in for 9.4/10, otherwise I'd prefer
EntityBundleAccessCheckdeprecated by this issue to be un-deprecated so we have a solution for revision parameters.Comment #29
donquixote commentedThe solution implemented here does not work if we have multiple routes with same path, but for different bundles.
The ParamConverter is only called _after_ the route was already picked in Router::matchCollection().
So it is too late at that point.
Comment #30
donquixote commentedI found that even the old mechanism would not have worked.
So nothing was made worse in this issue.
Sorry for the noise.
The Router::matchCollection() does not call the access checks.
It only calls $route->getCondition() which seems to provide some additional regex checks, but this does not really help to distinguish bundles.
So this is a dead end: We should not have multiple routes with the same path.