Symfony 2 - When and why does a route parameter get automatically converted? - symfony

I have this route:
pfs_platform_home:
path: /{page}/{reset}
defaults: { _controller: PFSPlatformBundle:Advert:index, page: 1, reset: true }
requirements:
page: \d*
reset: true|false
If I use a link without specifying anything for reset, the router uses the default value and in my indexAction, the reset parameter is automatically converted to Boolean true.
i.e.:
<li>Inicio</li>
But when I do that, this time $reset appears as a string 'false' in my indexAction, not a Boolean:
{{ p }}
What am I missing?

URL paths and parameters are always strings. If you have an URL like
http://example.com/page/true?foo=2&bar=false
the server cannot know that the true should be interpreted as boolean, while foo is supposed to be an integer and bar is supposed to be boolean, too.
If you want to process URL parameters, always pass and treat them as strings.
Later, you can validate them (e.g. is_numeric will tell you if a string represents a number) or transform them to other types.
What you're also experiencing here is YAML's handling of unquoted strings:
Strings may generally be left unquoted, if they don't contain a character that has a meaning in YAML.
But: true in YAML is boolean true. Therefore, your default reset: true is indeed a boolean value. Declare it as reset: "true" and it should work. The reset: true|false should be fine, IMO (didn't test it, but this is treated as a regex, so it should be interpreted as string.)

You have already set the defaults for your params:
pfs_platform_home:
path: /{page}/{reset}
defaults: { _controller: PFSPlatformBundle:Advert:index, page: 1, reset: true }
requirements:
page: \d*
reset: true|false
Check out defaults: line it says if you do not supplu page it will be 1 and if you do not supply value for reset it will default to true.

Related

Why is this routing mismatched?

A page contains this route: <a href={{ path('app_gut_food_vector') }}> but triggers a not found by the #ParamConverter annotation error. The log shows this: Matched route "app_gut_show". . I cannot figure out how this could be. My question: How to get the correct route matched?
Pieces of the puzzle:
log entry:
INFO 23:53:50 request Matched route "app_gut_show".
{
"route": "app_gut_show",
"route_parameters": {
"_route": "app_gut_show",
"_controller": "App\\Controller\\GutController::show",
"id": "vector"
},
"request_uri": "http://diet/gut/vector",
"method": "GET"
}
template includes:
Click <a href={{ path('app_gut_food_vector') }}> here</a>
Controller includes:
#[Route('/vector', name: 'app_gut_food_vector', methods: ['GET', 'POST'])]
public function vector(Request $request, GutRepository $gutRepository, VectorService $vectorSvc)
{
...
}
debug:router includes:
app_gut_index GET ANY ANY /gut/
app_gut_new GET|POST ANY ANY /gut/new
app_gut_show GET ANY ANY /gut/{id}
app_gut_edit GET|POST ANY ANY /gut/{id}/edit
app_gut_delete POST ANY ANY /gut/{id}
app_gut_food_vector ANY ANY ANY /gut/vector
and the incorrectly matched route:
#[Route('/{id}', name: 'app_gut_show', methods: ['GET'])]
public function show(Gut $gut): Response
{
...
}
Note: removing the methods from the vector method does NOT prevent the mismatch.
Depends on what symfony version you use, you could try to define route priority.
If you use older version than 5.1, try to move your annotaion to yaml file and define /gut/vector route higher than /gut/{id} route
For higher version view Symfony doc
I think you're somehow messing with route orders.
As a matter of fact /gut/vector matches also /gut/{id} as regex are used to match routes.
I think you should declare your routes (so controller methods) in other order to avoid this kind of collisions.
Alternatively you can ask esplicitly for id to be an integer (if it is an integer; if it is a UUID, just to pick one random example, you should stick to UUID format).

symfony routing, use value from Request as default

I've got the following route definition
my_route:
path: /actual-path/
defaults:
_controller: MyBundle:MyController:detail
id: application_id
requirements:
methods: GET
id: \d+
The controller requires a parameter called $id.
But I don't want to use the $id in the url, I want to use a value that is available in $request->attributes->get('application_id')
There is a listener that will inject two parameters (application_id and application) into the request object as attributes prior to the routing process, so this value is in there. (It would be easy to also inject it into the RequestContext).
Is there a way I can use attributes values from the Request or RequestContext object in my routing as defaults?
Now I could simply do $request->attributes->get('application_id') in my controller. But this controller will be used in several cases. In other cases the $id is to be passed through the url. I find it cleaner to set the id in the routing than build a if-else clause in the controller somewhere.
It appears not to be possible to do "mapping" between variables in the request object and the parameters you need to be required in your route/controller-action. I think this would be a good thing, as it would become quite complex otherwise.
I went with the solution to extend the controller and build in a small switch there.
Basically, if the route specifies id === null, it will do a fallback to application_id that is in the Request object. Otherwise it will use the value provided. I just need to set a requirement on id on the routes that must not use the fallback.
All without running an additional Listener which might be a bit "expensive" in processing time (for each request).
Example of routes:
my_route:
path: /actual-path/
defaults:
_controller: MyBundle:MyController:detail
id: null
requirements:
methods: GET
my_other_route:
path: /other-path/{id}
defaults:
_controller: MyBundle:MyController:detail
requirements:
methods: GET
id: \d+
And how to handle this in your controller:
// fallback to system id
if ($id === null) {
$id = $request->attributes->get('administration_id', null);
}
Because you extend the controller/action, you could also change the name of the parameter in the action (as long as the type does not change).
I did not do this, as I could quite easily put the switch between the provided id and the fallback from the listener in another method.

Symfony2, no route found and defaults

Simple question but I have issues with it.
We have simple route
profile_api_info:
pattern: /api/info/{apiID}
defaults: { _controller: SiteProfileBundle:Api:info, apiID: null}
When we use such url as
http://some.site/api/info/123
we'll get proper result of controller.
But when we use this one
http://some.site/api/info/
we'll have an error, why?
No route found for "GET /profile/api/info/"
We'll have already setuped 'defaults' for our 'apiID' but symfony2 says that no route. Can someone suggest how to deal with it? I want routes
http://some.site/api/info
http://some.site/api/info/
have the same controller as
http://some.site/api/info/123
but with 'apiID' = null or false, no matter.
You have two options.
Option 1:
Pass your default parameter.
profile_api_info:
pattern: /api/info/{apiID}
defaults: { _controller: SiteProfileBundle:Api:info, apiID: null}
However you will not be able to have trailing /.
This would be correct: http://some.site/api/info
This would be incorrect http://some.site/api/info/
Option 2:
set up an additional route.
(This would be my preference.)
profile_api_info_woId:
pattern: /api/info/
defaults: { _controller: SiteProfileBundle:Api:info}
In your controller make sure set the default for $apiID to null.
public function infoAction($apiID = null){...}
Using two routes with one controller method should work for all of the following urls:
http://some.site/api/info
http://some.site/api/info/
http://some.site/api/info/123
I've ran into this multiple times and clearing the cache so that Symfony can rebuild the route definitions usually fixes the issue.
Syntax and everything looks correct. However, the route with the trailing slash (http://some.site/api/info/) won't work but http://some.site/api/info should.

Symfony2.2 : default_locale always applying in twig translations

I'm having a strange issue with Symfony2.2. I have a project using two languages : en/fr. So I create as usual (like Symfony2.0) two translation files "messages.en.yml" and "messages.fr.yml" in Ressources/Views/translations/. But Translations in twig could not change even if we set the request object and the locale session. Translation is always set by the default_locale (config.php).
Example : if default_locale = en, all my website (in twig) is translated in en, even if i set the _locale object in fr (request and session). Of course if I manually change the default_locale to fr the website is naturally in fr...
However, _locale session works but I don't know if locale request works, and of course translation works in controllers too...
There is my files :
config.yml:
framework:
#esi: ~
translator: { fallback: %locale% } # = en
# ...
default_locale: %locale% # = en
Controller :
public function indexAction()
{
$this->get('session')->set('_locale', 'fr');
$this->getRequest()->setLocale($lang);
exit($this->getRequest()->getLocale()); // = fr
exit($this->get('translator')->trans('Symfony2 is great')); // = Symfony2 est génial
return $this->render('TestBundle:Controller:test.html.twig');
View :
{% block content %}
<p>lang : {{ app.request.locale }}</p> {#} = "fr", OK{#}
<p>{{ 'Symfony2 is great'|trans }}</p> {#} = "Symfony2 is great", WAIT WHAT?{#}
I must resign myself to force the locale at the beginning of the method controller to have the requested locale (stored in session) like that :
Controller:
if($this->get('session')->get('_locale')){
$lang = $this->get('session')->get('_locale');
$this->getRequest()->setLocale($lang);
}
In other words, I do have a problem with the registration of the request object... Because the last code works well in the controller, and shows well the locale in twig page with app.request.locale, but not the translations... (sorry for my bad english and thanks for helping)
I had the same issue due to the low priority of my event listener. The locale would be overridden by the Translator's TranslatorListener. Increasing the priority of my event listener did the trick for me:
services:
app.locale_listener:
class: AppBundle\EventListener\LocaleListener
tags:
- { name: kernel.event_listener, priority: 11, ... }
Source: https://github.com/symfony/symfony/issues/12878#issuecomment-68628808
Parameter _locale in routing holds your locale value.
Look here on this page
Symfony - Book - Translation - Local and the URL
From Symfony 2.1 they have this kind of logic:
Since you can store the locale of the user in the session, it may be tempting to use the same URL to display a resource in many different languages based on the user's locale. For example, http://www.example.com/contact could show content in English for one user and French for another user. Unfortunately, this violates a fundamental rule of the Web: that a particular URL returns the same resource regardless of the user. To further muddy the problem, which version of the content would be indexed by search engines?
A better policy is to include the locale in the URL. This is fully-supported by the routing system using the special _locale parameter:
Now when you want to sel local, this doesn't work any more
$this->get('session')->set('_locale', 'fr');
You can use request insted of session now but you can not have session-logic with _local from Symfony 2.0 unless you simulate it with event listener on kernel request.

How do I use Period in Route in Symfony2?

I'm trying to have a route in symfony that matches the following url:
/filename/version/
where filename is an image, say "image.png" and version as say "foo".
MyImageBundle_resize:
pattern: /{filename}/{sizename}/
defaults: { _controller: MyImageBundle:ImageResize:resize }
and this matchs the pattern /myfile/XXL/, for example. But when I use myfile.jpg, as in /myfile.jpg/XXL/ Symfony seems to break on the period, and returns 404. I found this article which suggests that everything except the "/" will match (which doesn't make any sense if the period is breaking here).
Is there a way I can have the route match on the period?
You don't need to allow slash in URL. Just use simple route:
file_check:
pattern: /check/{filename}/{version}
defaults: { _controller: AcmeDemoBundle:File:check }
And in your controller use:
public function checkAction($filename, $version)
{
//some action
}

Resources