-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Twig] added subpath function #3965
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
This is indeed the wrong way as it makes Twig unusable from the CLI |
So injecting the container? |
Magic was killed in 2010, and I don't think it's a good thing to generate URLs like this. Probably you should integrate this to your project, maybe a bundle or an article. But I don't think it should be part of the framework. |
It's not magic. It just exposes the information ( |
public function getUrl($name, $parameters = array()) | ||
/** | ||
* Returns the path to a route (defaults to current route) with all current route parameters merged | ||
* with the passed params. Optionally one can also include the params of the query 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.
You should separate the short and long description.
FYI, Zend Framework also has the possibility to merge the current request params as this PR proposes. |
+1 here, we do need to have a way to generate links to current path while being able to alter a single parameter and keeping others the same as they were. I18n locale switching is just one of the cases here. |
@kix Symfony 2.1 introduced an attribute making it quite easy to do this locale switching: {% set route_params = app.request.attributes.get('_route_params') %}
{# merge the query string params if you want to keep them when switching the locale #}
{% set route_params = route_params|merge(app.request.query.all) %}
{# merge the additional params you want to change #}
{% set route_params = route_params|merge({'_locale': 'fr'}) %}
{{ path(app.request.attributes.get('_route'), route_params) }} |
I know this should be working well, though it seems like it's way too much Yet, thanks for the snippet, I assume I'll stick to it till there's a |
* @param ContainerInterface $container A ContainerInterface instance | ||
* @param UrlGeneratorInterface $generator A UrlGeneratorInterface instance | ||
*/ | ||
public function __construct(ContainerInterface $container, UrlGeneratorInterface $generator) |
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.
Depending on the container makes it impossible to use this extension in a project using Twig and Routing without using the DI component (Silex projects for instance)
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.
So should i make it __construct(UrlGeneratorInterface $generator, ContainerInterface $container = null)
and then assert that the container is set in getSubPath
?
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.
@stof ping
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.
@stof would that be better? Silex would only not be able to use the subPath function then, or?
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 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 but when using on-invalid="ignored" the setter will not be called when request is null. Should work just as fine, doesnt 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.
The setter must be called so that your object knows that it is acting outside the handling of a request. If not, you will still have access to an older Request object.
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.
Hm, actually we don't really want the subrequest info. Only the master request matters. When generating a relative path in a subrequest (e.g. a partial with ESI), it would be wrong from a browser perspective. Because the browser would resolve the relative link based on the master/main/user-entered URL. So the relative link in a subrequest probably goes to the wrong target.
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.
But this is just a documentation thing and it should be clear that relative paths in a subrequest don't make much sense. This is nothing special for the new subpath function; the same applies for the url and path methods.
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 then also fixed by the request stack
There is an issue that makes it impossible to really re-generate the current URL: So the only way to fix it would be that |
As there is no way to really regenerate the current URL with the UrlGenerator (see above), we cannot use it in this case. But I just figured we don't need to. We can simply use |
|
* Generates a relative or absolute path depending on which is shorter, | ||
* e.g. "/dir/file" vs "../dir/file". | ||
*/ | ||
const SHORTEST_PATH = 'short'; |
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 should be a separate PR as it is a separate feature (and I don't really see a need for 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.
Yeah I will remove it. Was just experimental.
public function getPath($name, $parameters = array(), $relative = false) | ||
{ | ||
return $this->generator->generate($name, $parameters, $relative ? UrlGeneratorInterface::RELATIVE_PATH : UrlGeneratorInterface::ABSOLUTE_PATH); | ||
} | ||
|
||
public function getUrl($name, $parameters = array(), $schemeRelative = false) | ||
/** | ||
* Sets the current request that is needed for the subpath function. |
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 should use the request stack instead.
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 don't understand what u mean
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.
Instead of relying on a setRequest method, you can take the request stack as an argument and call ->getMasterRequest()
whenever you need the request. That way, you always have the right master request. The current way won't work if you handle two master requests in the same PHP process (without rebooting the Kernel of course.)
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.
ah i see. it's about #8904
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
@fabpot this is ready. I updated the main description. |
@@ -81,6 +81,7 @@ | |||
|
|||
<service id="twig.extension.routing" class="%twig.extension.routing.class%" public="false"> | |||
<argument type="service" id="router" /> | |||
<argument type="service" id="request_stack" /> |
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.
twig bundle doesnt have the framework bundle as dependency yet. but since the twig bundle relies on service definitions in framework bundle, shouldn't it be the 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.
no, the Twig bridge is used by Silex without the definition as we are using Pimple there, so this is not a hard dependency.
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 the twig bundle, not the bridge.
use the current route by default add possibility to remove existing params by passing null for it added tests for the twig routing extension use synchronized request
@fabpot ping ready |
@Tobion Documentation is missing |
The more I think about it, the more I find the feature confusing. The probability that a route takes the same arguments as the current one is rather thin. I know that you can remove arguments by setting them to null, but then, I think it's better to just pass the arguments you need. And if you use I can definitely see the use case for variation of the current route, but then, we won't need the route name as an argument. So, I'm still -1 on adding this in core as is. |
Let me explain my feeling a bit more. Let's say that my current page is This URL has been generated with Now, on the page, I want to generate a link to the next article, 13, using I do understand the usefulness of the feature but the implementation does not convey the right meaning. What about doing something like: {{ path('article', { id: 14 }|merge_current_path_attributes) }} instead (replace Let's take another example, a list: I have several link to change the different values of the route attributes. I can do Of course, we could also be able to remove some attributes: |
So you are just not satisfied with the name
Could work as well. But stilll some problems
|
but the only feature added by this PR is the easy reuse of attributes. Generating a relative url is already supported since 2.2 through |
@stof I think nobody said something different. |
quoting you: thus, the filter-based implementation proposed by @fabpot above would be compatible with the 4 url generation styles, not only with the relative path. |
Huh? I didnt say its a feature added here. I just said why I chose the name subpath because that is what fabpot does not agree with. But I'm open to any other name if you find one. |
I think you are both right ;) Indeed I don't like the name, but @stof is right as the feature you want to add can be more easily done with a filter and would work with all generator strategies. |
I still don't see how to use the current route with a filter without bc break. So if the 3 problems are solved above, I'm fine using a filter. Although I find a seperate function more clean. |
If we don't provide a shortcut, we could say, we don't actually need to do anything. People can already use something like |
A shortcut like {{ path(app.request.route, app.request.params|merge({ page: 5 })) }}? Will not be BC? |
@dewos There is no reason to have a |
From a neophye's perspective, I see a lot of value in easily generating new paths in the response from the request's route's attributes. I agree, however, with @stof and @fabpot that it is not a "sub" path. It is not a hierarchy, but instead the request's attributes used to generate a whole new path in the response (atomic?). I do think the @Tobion idea for a much more simplified filter is important, because as a new implementer, I would never conceive of something as intricate as:
but...
or...
... readily makes sense to me and conveys the intent of generating a new path in the response using the request's path's attributes. (notice the small edit I made to the @fabpot example above -- it's not the "current" path, but belongs to the request's path/route in my mind. The is no temporal or hierarchical component to the function.) I hope this can yet make it into 2.4. |
Why not make the use of the underscore generic in the router ? For example,
As we do today, I think about the variable "_locale" that does not need to be redeclared each url generation. I found a solution to do this properly. Just add a route listener
and instantiate it as a service
I hope this can help you. ;-) |
The problem with |
Very useful function. Is it possible ship it with 2.6? |
We cannot agree how to provide this feature. So I don't think it will make it to 2.6 I'm summing up the proposals and arguments against it:
|
How about |
On a second thought it's not a good idea, since |
In my opinion, option four is a step in the right direction. The Some things we then still need to solve are:
For the first issue, I could imagine that this is something that could be added to Twig itself (like it is done the other way around with the For the second issue, I don't have a concrete idea right now. Maybe adding another Twig global or function could be the solution. |
It really depends on which use cases we are talking about. From the issue description, I think that only the first one is relevant. |
I'm closing this very old PR as there is no activity, and no consensus on what to do. |
The new function
subpath
merges the variables of the current route request with the ones you optionally passed to the function. On top of that you can also add the query params (disabled by default).It will generate a path-relative URL once #3958 gets merged but this PR is not necessarily depending on it. Since the target URL, that inherits some params from the current request, is probably a sub-page in the URL hierarchy (which is why it's called
subpath
), it makes sense to default it to a relative path, which makes the links shorter.Use cases:
Example: We are at
/user-slug/article-slug/
(article_show) and want to generate the link to comments of the article (article_comments). At the moment we would have to always repeat the current variables like{{ path('article_comments', {'user': 'user-slug', 'article': 'article-slug'}) }}
and would for example get/user-slug/article-slug/comments
.With
subpath
the advantage is you don't need to specify the variables of the current request again.So simply
{{ subpath('article_comments') }}
is enough for the relative URLcomments
in this case. But you can optionally overwrite some request variables with{{ subpath('article_comments', {'article': 'different-article-slug'} ) }}
which would generate../different-article-slug/comments
(i.e. same user, but different article).null
as value for the unwanted param.Generating the same URL.I really thought much about this use-case. And I came to the conclusion it's not necessary for it to have a specific twig function. Of course you can also generate the same URL with path, url and subpath functions. But with path and url, you need to pass all variables again. And with subpath it will just generate an empty string''
because that's the relative path for the current URL. So you don't need to call subpath at all. And if you need an absolute path or url, then you can just access the request information directly (Request::getRequestUri
, i.e. in twig{{ app.request.requestUri }}
. It's also faster because it doesn't go through the routing url generation at all. So all in all, it's already easy to get the same url. It would also not have much to do withsubpath
but instead it would be more appropriate to expose a new function like{{ self() }}
. But as I said, it's not really needed and not part of this PR.