-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Routing] allow no-slash root on imported routes #26284
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@@ -6,6 +6,8 @@ | |||
|
|||
<route id="app_homepage" path="/" controller="AppBundle:Homepage:show" /> | |||
|
|||
<route id="app_homepage_no_slash" path="" controller="AppBundle:Homepage:show" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this notation deprecated as of 4.1? Would make sense to add the non-deprecated notation while at it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are more in the fixtures, but we don't really care as that's not parsed anywhere in Routing, where it's just a string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well this is just a routing fixture and the controller is not used as a controller. So you can write anything in there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can even remove it, done :)
I agree that this is the cleanest and simplest solution. But I fear it's a BC break for some people. |
@Tobion yep.
|
@nicolas-grekas your code looks so easy, I tried it last year and stranded. Thank you very much. |
There is stll alot of potential for BC breaks. Like if someone used hardcoded relative links to assets, it doesn't work anymore. Or if you developed an API and the client does not support redirect, it breaks. |
@Tobion I don't understand what you mean. At the moment, we have this behaviour:
these urls match: /, /api/, /api/query #26283 will fix the last case, in my opinion it is a new feature and a good feature. The case why I had the problem is, that for api url reasons and / or SEO reasons, I want to replace my custom api controller with a bundle (or subfolder) and leave the url the same. api_query:
prefix: /api/query
resource: ../src/Controller/Api/Query
type: annotation and the controller /**
* @Route("")
*/
public function queryIndexAction() doesn't work at the moment. In a second application (api.domain.com) I can reuse this controller / bundle without prefix and the url will be / |
509d01e
to
2e4d4b0
Compare
Good news, that's actually not true!: loaders do not allow setting null, eg PR updated. |
2d72ff1
to
392747c
Compare
|
||
$route = new Route($node->getAttribute('path'), $defaults, $requirements, $options, $node->getAttribute('host'), $schemes, $methods, $condition); | ||
$route = new Route('null' !== $path ? $path : null, $defaults, $requirements, $options, $node->getAttribute('host'), $schemes, $methods, $condition); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is not a real null value in xml I assume. you need to make the path in the xsd nillable="true"
: https://github.com/nicolas-grekas/symfony/blob/392747ceed64a61ffef636e05f837a13e169e8cc/src/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd#L40
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure how that interacts with use="required"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually that's not possible on attributes, so we have to do as implemented I suppose, deal with null
as a special case
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I also figured. But I think it would be better to make path attribute optional in xml instead. null as a string is a hack and potential bc break.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make path attribute optional in xml instead
I fear this will hurt DX, as people will more easilly forget to add the path without having any reminder
null as a string is a hack and potential bc break
let's assume nobody configured a route on path "null", and that those who did wrote it as "/null", which is what we all do, isn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I fear this will hurt DX, as people will more easilly forget to add the path without having any reminder
Good point. Another brainstorming idea: Add a new attribute like empty-path=true|false
that defaults to false for BC. So one could use path="" empty-path=true
to achive that. Does not look nice but path="null" is not transform meaning at all and if you know xml (likely when you use xml config), you'd know it's not a real null value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
brainstorming: is it possible to use ~ also in xml like: ?
The meaning of this char would be / or null with prefix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done using empty-path="true"
2acb388
to
9913fc6
Compare
@@ -107,16 +107,20 @@ public function supports($resource, $type = null) | |||
*/ | |||
protected function parseRoute(RouteCollection $collection, \DOMElement $node, $path) | |||
{ | |||
if ('' === ($id = $node->getAttribute('id')) || !$node->hasAttribute('path')) { | |||
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must have an "id" and a "path" attribute.', $path)); | |||
if ('' === ($id = $node->getAttribute('id')) || (!$node->hasAttribute('path') && 'true' !== $node->getAttribute('empty-path'))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
an xml boolean can also be 1
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's make it accept only "true" (done)
@@ -37,7 +37,8 @@ | |||
<xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" /> | |||
|
|||
<xsd:attribute name="id" type="xsd:string" use="required" /> | |||
<xsd:attribute name="path" type="xsd:string" use="required" /> | |||
<xsd:attribute name="path" type="xsd:string" /> | |||
<xsd:attribute name="empty-path" type="xsd:boolean" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could make a restriction on the boolean that it can only be true. Putting false there does not make sense.
<xsd:attribute name="empty-path" type="onlyTrue" />
<xsd:simpleType name="onlyTrue">
<xsd:restriction base="xsd:boolean">
<xsd:enumeration value="true"/>
<xsd:enumeration value="1"/>
</xsd:restriction>
</xsd:simpleType>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with type="only-true"
and this, I get type 'only-true': The facet 'enumeration' is not allowed on types derived from the type atomic type 'xs:boolean'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, fixed
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must have an "id" and a "path" (or "empty-path") attribute.', $path)); | ||
} | ||
|
||
if ($node->hasAttribute('path') && 'true' === $node->getAttribute('empty-path')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since empty-path=false is useless, I would forbid specifing both ($node->hasAttribute('path') && $node->hasAttribute('empty-path'))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that's already covered by the previous check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The solution with empty-path or path attribute is very nice
9913fc6
to
174bbc6
Compare
#12141 (comment) made me realize that for annotations path="" works if you specify the prefix on the class. But for other use-cases path="" means something else. That inconsistency is unfortunate. @nicolas-grekas the difference between null and "" is probably hard to see for people. I think it would better in the long run to just use "" for this. Maybe we should reconsider adding a config/env variable to enable the new bahavior for path="". And when the config is not set and someone used an empty path so far, trigger a deprecation. |
4667b66
to
e181b39
Compare
Now tested, ready; |
e181b39
to
64e809f
Compare
(failures unrelated, PR ready) |
This looks good to me. But this needs a doc PR. And it would also be good to update the PR description with the final solution to reduce confusion. |
description updated |
@@ -50,6 +51,14 @@ public function __destruct() | |||
} | |||
if (!\is_array($prefix)) { | |||
$this->route->addPrefix($prefix); | |||
if (!$trailingSlashOnRoot) { | |||
$rootPath = (new Route(trim($prefix).'/'))->getPath(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The RouteCollection does a little more
$prefix = trim(trim($prefix), '/'); |
You need to do the same thing otherwise it does not work for /prefix//
. The same applies to the loaders.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
395c640
to
1245272
Compare
@@ -40,7 +41,7 @@ public function __destruct() | |||
* | |||
* @return $this | |||
*/ | |||
final public function prefix($prefix, string $namePrefix = '') | |||
final public function prefix($prefix, string $namePrefix = '', bool $trailingSlashOnRoot = true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bool arguments to change behavior are usually a bad design. an alternative would be to create a separate method like removeTrailingSlash(string $path)
that does exactly what is currently implemented but in a separate method that reads like prose instead of some non-saying argument.
1245272
to
5a98515
Compare
That is not possible: |
I somehow missed that your proposal has the path as argument. But that requires prior knowledge of which paths are the root, and doesn't work with localized paths (or requires knowledge of all the variants, which is bad coupling.) |
Thank you @nicolas-grekas. |
…las-grekas) This PR was merged into the 4.1-dev branch. Discussion ---------- [Routing] allow no-slash root on imported routes | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #12141 | License | MIT | Doc PR | - With this change, a collection is imported, its root can have no slash appended. e.g.: ```yaml import: resource: ... trailing_slash_on_root: false ``` Works also for XML and PHP-DSL. Commits ------- 5a98515 [Routing] allow no-slash root on imported routes
Hello, I am trying to understand why when we have perfixed routes (for example
The generator remove the ending slash when I try I go deep into the code (in RouteCollection and UrlGenerator) but can't find the explanations. I am using Symfony since 4.1. |
It's because you set a default value for slug. In case you want the trailing slash, you can remove the default value and instead change the requirement for slug to allow an empty value like |
Thank you for your reply. It may work, but if I remove the Re-reading the doc, I understand the behavior. It is what is expected (last line). |
Yes you need to provide the slug param then. Another solution to get the trailing slash is to create two routes:
|
If I do that, I loose my domain page with default locale : I try a few workaround, but I can't find a Solution (my best was to get I will end to do a twig function to correct the URL generator (I am not sur I can override directly the UrlGenerator class from a bundle). |
With this change, a collection is imported, its root can have no slash appended. e.g.:
Works also for XML and PHP-DSL.