Explanation of annotations routing with symfony, - symfony

I want to ask you a little explanations of routing with annotations.
Is there a difference between
/**
*#Route("/{_locale"}
*/
and :
/**
*#Route("/{locale"}
*/
Thanks !

Certain parameters with an underscore in a route have special meaning for the resulting Request object. So your first route will change the locale setting, such that you can do $request->getLocale(), while your second route will set a parameter called locale e.g. $request->get('locale'). Special parameters can have a knock on effect for the rest of your application e.g. setting the response format.
The different "special" routing parameters are detailed in the routing documentation. Both are syntactically correct.

Related

How to use Symfony container parameters in annotations?

Is it generally possible to use container parameters in method/class annotations in Symfony 5.3+?
For example one of my action methods uses a #ParamConverter annotation using the FOSRest RequestBodyParamConverter:
class SomeController extends AbstractController {
/**
* #ParamConverter("post", converter="fos_rest.request_body")
*/
public function sync(Request $request, Post $post): Response {
...
}
}
Instead of explicitly specifying the the converter here, I would like to use a container parameter instead. However, this does not seem to work.
I have checked that the parameter is defined correctly:
$ php bin/console debug:container --parameters
...
my.post.converter fos_rest.request_body
But I cannot use it:
/**
* #ParamConverter("post", converter="%my.post.converter%")
*/
No converter named '%my.post.converter%' found for conversion of
parameter 'post'.
I found other questions which indicate, that using parameters should be possible. At least when working with other annotations like #Route.
Is this a limitation of the #ParamConverter annotation (= a special feature of the #Route annotation), or is there anything wrong with my setup?
I know that there are other ways of specifying a different converter in this example. However, this is just an example and the question is, whether using a parameter here should be possible or if I made some error.
Not all annotations are equal. Each has its own processor, and each annotation argument has different accepted inputs.
And the annotations simply enable some underlying component, so sometimes (probably most of the time) if you can use a container parameter in an annotation, it's because the underlying component supports using a container parameter for that argument (as is the case with the Routing component, that can be configured via other ways than annotations/attributes)
Using a container parameter on the question provided example would not be particularly useful, if at all.
That argument expects a service ID, and since those IDs are defined on the container configuration (the same place where you would configure the parameters), moving the definition to a parameter would gain one nothing.
If you want to have it "configurable", maybe use a custom service id that you could define as an alias to the real service, that way you can have multiple converters and alias the correct one via configuration.
Not convinced that that would be something useful in real life, but your application constraints might be altogether different.

The difference between the attribute name, value and path of the annotation #RequestMapping in SpringMVC

I am struggling to understand the differences between the attribute name, path and value in #RequestMapping. I looked into the API, but it doesn't illustrate it clearly. I googled for hours but it just works partially. Can you tell me the details? Thank you!
Let's see the javadoc. Start with path:
The path mapping URIs (e.g. "/profile").
So that's the path(s) that will be used by Spring to decide if this methd should be called or not, based on the actual path of the request.
Now value:
The primary mapping expressed by this annotation.
This is an alias for path(). For example, #RequestMapping("/foo") is equivalent to #RequestMapping(path="/foo").
So, it's quite clear: value and path are exactly equivalent.
And finally name:
Assign a name to this mapping.
So this is completely different. It gives a name to this mapping. Why could a name be useful for a mapping? You can discover that by clicking on the "See also" classes linked to attrbibute in the documentation. The javadoc for HandlerMethodMappingNamingStrategy says:
Applications can build a URL to a controller method by name with the help of the static method MvcUriComponentsBuilder#fromMappingName [...]
So, as you see, the name of a mapping can be used to construct a URL that will match to a given controller method by using the name of its mapping. That makes it possible to change the path of a mapping without changing it everywhere, by using names rather than paths.
The reference documentation, in addition to the javadoc, should be your first destination. This is usually way more effective when you want to learn about the framework than googling.

How to pass arguments to controller from route in Symfony2

I'm working on a Symfony2 project and am trying to figure out how to pass parameters from the route configuration to the controller. I know I can configure default values in the route configuration, and retrieve the values in the controller using the appropriate var name in the function declaration, but that isn't exactly what I want.
My use case is the following. I have a standard method in my controller that I want to access from 2 or 3 different routes. Depending on which route is being called, I want to "configure" the method differently. I can accomplish this in a few ways:
In my controller, check the route name using `$this->container->get("request")->get("_route"), but that is ugly, and then I am hardcoded to the route name. Moves configuration to the controller, which should just be logic - not configuration.
Create a base controller class, and subclass each method for my different routes. Each subclassed method would then have the necessary configuration within the method. Cleaner soln than #1, but still "heavy" in the sense of having multiple classes for a simple need and still pushes configuration data into the business logic.
Put configuration data into the route configuration. In the controller, access the configuration data as required. Ideal solution, but don't know how.
I can use the route default array to specify my arguments, but then must make sure to use a regex to ensure that the params are not overridden at the URL level (security risk). This is functional, but still kinda cludgy and not a pretty hack.
I presume that there must a better way to do this, but I can't seem to figure it out. Is there a way to access the routing object from the controller, and access the different configuration parameters?
You can pull the actual route from the router service. Something like:
$routeName = $this->container->get("request")->get("_route");
$router = $this->container->get("router");
$route = $router->getRouteCollection()->get($routeName);
Not sure if this would be such a great design though. Consider passing a $configName to your controller method, adding a parameter with the same name in a config file then using getParameter to access it. That would eliminate the route stuff from the equation.
Something like:
zayso_arbiter_import:
pattern: /import
defaults: { _controller: ZaysoArbiterBundle:Import:index, configName: 'someConfigName' }
public function importAction(Request $request, $configName)

Symfony2: Why weren't query string parameters included in the routing component?

I am porting a legacy application to Symfony2 and I am struggling because routing doesn't include query string parameters. Some quick examples: Suppose you have a search page for books where you can filter results based on criteria:
http://www.bookstore.com/books?author=Stephen+King&maxPrice=20
The nice thing about query string parameters in a case like this is you can have any number of filters, use the ones you want for any given request, and not crowd the URL with filters you're not using.
Let's say we rewrote the routing for the above query using the Symfony2 routing component. It might look like this:
http://www.mybookstore.com/book/any_title/stephen%20king/any_release_date/max_price_20/any_min_price/any_format/any_category
Even not taking into account how arbitrarily long an unclean that URL is I still don't think it is as intuitive because each 'segment' of that route is not a key value pair but instead just a value (e.g. author=Stephen+King > /stephen%20king/).
You can of course access query string parameters in the controller by passing the Request object into the action method (e.g. indexAction(Request $request) {) but then validating them and passing them into other parts of the application becomes a hassle (i.e. where I find myself now). What if you are using the Knp Menu Bundle to build your sidebar and you want parts to be marked as .current based on query string parameters? There is no functionality for that, just functionality to integrate with Symfony2 routes.
And how to validate that your query string parameters are acceptable? I am currently looking at validating them like a form to then pass them into the database to generate a query. Maybe this is the way the Symfony2 team envisioned handling them? If so I'd just really like to know why. It feels like I'm fighting the application.
I ended up actually asking Fabien this question at Symfony Live San Francisco 2012. There was another talk at the same conference in regards to this question and I will share with you the slides here:
http://www.slideshare.net/Wombert/phpjp-urls-rest#btnNext
Basically in the slides you can see that the author agrees with me in that query string parameters should be used for filtering. What they should not be used for is determining a content source. So a route should point to a product (or whatever) and query string parameters should then be used in your controller to apply whatever logic you require to filter that content source (as per Fabien).
I ended up creating an entity in my application that I bind all my query string parameters to and then manipulate, much the same way forms are handled. In fact when you think about it it's basically the same thing.
Like in Symfony1, query strings are independent from the route parameters.
If you have a path defined as #Route("/page/{id}", name="single_page"), you can create a path in your view like this:
{{ path('single_page', { id: 3, foo: "bar" }) }}
The resulting URL will be /page/3?foo=bar.

Custom catch-all parameter in routing

I recently want to have a special routing rule : {*whatever}/details/{Id}/{itemName}
I know an exception will be thrown once I run the application. In my application, for example my url pattern is www.domain.com/root/parent/child/.../child/details/30/itemname
but the current routing doesnot support this. How can custom the routing handler to make it work?
A class has been written that supports this
I've written such a class that can handle catch-all segment anywhere in the URL. There's quite some code to it, but it works as expected and I've used it on a real life project.
Check it out yourself and see if it fulfils your needs.
The problem is... how will it know when to stop?
the {*whatever} segment will match:
/foo/
/foo/bar
/foo/bar/details/4/moreFoo
/foo/bar/andmore/details/4/moreFoo
Because the catch-all parameter includes anything, it will never stop.
The only way to implement this would be to create a different route for each place you use details...
eg:
games/details/{id}/{itemName}
widgets/details/{id}/{itemName}
books/details/{id}/{itemName}
Of course, that is already provided in the default {controller}/{action}/{id} route
I think you may want to look at extending the System.Web.Routing.RouteBase class and override the GetRouteData() method. With it you can look at the requested url and decide if matches your pattern and if so construct and return a new instance of RouteData that points to the controller and action that you want to handle the request. Otherwise if you don't match the requested url you return null.
See the following for examples:
Pro ASP.NET MVC Framework
By Steve Sanderson
Custom RouteBase

Resources