I want to have a url with a wildcard at the end site.com/{username} after trying to match url's like site.com/photos and site.com/blog. I'm using annotations and have two controllers.
I found an answer here Ordering of routes using annotations
But the folder structure is different in version 4 and in the answer they are using YAML while I'm using annotations, so I don't fully understand where to put what.
Video Controller
/**
* #Route("/videos", name="videos")
*/
public function index()
{
// Show video homepage
}
User Controller
/**
* #Route("/{username}", name="profile")
*/
public function index()
{
// Hello {username} !
}
In this order, site.com/videos reaches User controller instead of videos. Do I have to switch to manually putting all URL structures in yaml format or is there a way to set priority in annotations?
So far the only method I found was to create a controller starting with the letter "Z", and putting the action/route in there, that seems to run the route last.
The question you linked to is actually very relevant. You are asking just the same as the other guy:
But how can you do this if you define your routes as annotations in your controllers? Is there any way to specify the ordering of this routes in this case?
In Symfony 4, your config/routes.yaml is probably empty and config/routes/annotations.yaml probably like this:
controllers:
resource: ../../src/Controller/
type: annotation
You can replace the above YAML with this – notice that the order does matter:
Video:
resource: App\Controller\VideoController
type: annotation
User:
resource: App\Controller\UserController
type: annotation
Then site.com/videos reaches the video controller first.
You have to define your routes with yml/xml to be able to fully configure their order.
If you really want to use annotation you could add a prefix like user-:
/**
* #Route("/user-{username}", name="profile")
*/
public function index()
{
// Hello {username} !
}
Related
I'm using Symfony 4 and the way to capture a route and direct it to a method of a controller is as follows:
/**
* #Route("/my/route", name="name_of_route")
*/
public function myMethod() {
/* some logic that render a view */
}
Within my application, I have a shopping cart. Let's suppose a hypothetical example where I have the following nesting of categories:
Solid
└>sand
└>refined sand
└>gross sand
...etc...
if the user click on refined sand, I want to generate the follow URL:
www.myWeb.com/solid/sand/refined-sand
The problem is, how do I capture this URL with annotations since it is dynamic? That is, I can not do the following:
/**
* #Route("/{category}/{subcategory}/{product}", name="name_of_route")
*/
public function myMethod() {
/* some logic that render a view */
}
Because I do not know how many nesting levels the category has.
I hope my question was understood.
I would suggest creating a catch all route, such as:
public function catchAllAction($url)
{
// ...
}
You can then split the $url into segments on the / character, and match the categories against your entities as required.
$categories = explode('/', $url);
As you're using annotations for your routes, you would need to make sure this was the last route registered in your application to prevent it from mistakenly matching other routes. You can do this in the routing.yml config file.
app:
resource: '#AppBundle/Controller/'
type: annotation
catch_all:
path: /{url}
defaults: { _controller: AppBundle:Category:catchAll }
methods: [ GET ]
requirements:
url: '.+'
If possible, I would also suggest prefixing the route to limit the route patterns it might match. E.g. /categories/{url}
Clean setup of Symfony 3 framework, added locale listener from here:
http://symfony.com/doc/current/cookbook/session/locale_sticky_session.html
Example action code:
/**
* #Route("/foo/")
* #Route("/{_locale}/foo/", name="foo", requirements={"_locale"="en|ru|tk"})
*/
public function fooAction(Request $request)
{
return new Response('true');
}
This syntax has to be repeated for every action. Is there a way to make it less verbose without using a Bundle? I'd like the requirements portion to reside in a config somewhere, if possible.
Ideally, I would like to move the defaults={"_locale"="en"}, requirements={"_locale"="%allowed_locales%"} part inside the Locale Listener, but from what I've tried, it seems the listener is called after the route has been matched, and so that is not possible, which is really a shame.
Yes, check out How to Use Service Container Parameters in your Routes which is linked from The Locale and the URL in the Symfony Translations documentation, and states:
Read How to Use Service Container Parameters in your Routes to learn how to avoid hardcoding the _locale requirement in all your routes.
You can essentially set those routes in a global parameter like so:
# app/config/config.yml
parameters:
app.locales: en|ru|tk
Then in your route annotations do the following:
/**
* #Route("/foo/")
* #Route("/{_locale}/foo/", name="foo", requirements={"_locale"="%app.locales%"})
*/
public function fooAction(Request $request)
{
return new Response('true');
}
I'm not sure why the documentation only shows that for defining routes in YAML / XML / PHP but it should work just the same using annotations.
one question about Symfony2 and the routes.
In my routing file i have this route:
offers_category:
pattern: /offers/{category}
defaults: { _controller: MyBundle:Offers:category}
In this way i can call any url and all of them will respond with an 200 (HTTP CODE).
The categories list is dynamic, inserted by an admin panel (created with Sonata) and saved on db.
I would like to check if the "category" exist and then respond with 200 or 404.
There is a way to tell to Symfony2 (in the route file) the dictionary available for the placeholder?
I think that i have to check inside my controller calling a query on db, but i hope to find a better or cleaned solution.
Thanks all
I found the solution!
Thanks to #lenybernard for the precious advice on his post.
With the #lenybernard solution i was able to configure a route with an indexed field like:
www.sitename.com/offers/1
but i needed a url like:
www.sitename.com/offers/travel
and to do that i used this method to mapping a label not indexed to an url:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use NameProject\MyBundle\Entity\Category;
/**
* #Route("/offers/{category}")
* #ParamConverter("type", class="NameProjectMyBundle:Category", options={"mapping": {"category": "name"}})
*/
...and everthing works!
There is a simple and nice way called ParamConverter to do but there is nothing to do in the routing file directly (furthermore this is not its role) and you're right, this is ControllerSide :
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use My\Bundle\MyBundle\Entity\Offers\Category;
class OffersController
{
/**
* #param Category $category
* #ParamConverter("category", class="MyBundle:Offers/category")
*/
public function showAction(Category $category)
{
...
}
}
As said in the documentation, several things happen under the hood:
The converter tries to get a MyBundle:Offers/category object from the
request attributes (request attributes comes from route placeholders
-- here id); If no Category object is found, a 404 Response is generated; If a Category object is found, a new category request attribute is defined
(accessible via $request->attributes->get('category')); As for other
request attributes, it is automatically injected in the controller
when present in the method signature. If you use type hinting as in
the example above, you can even omit the #ParamConverter annotation
altogether because it's automatic :
So you just have to cast the variable and the param converter will throw a 404 automatically, cool right ?
use My\Bundle\MyBundle\Entity\Offers\Category;
class OffersController
{
/**
* #param Category $category
*/
public function showAction(Category $category)
{
...
}
}
Hey all I have a bit of a problem with root annotations in Symfony2.
I have two different controllers that call methods from the same URL positions /test.
Controller 1:
**
* #Route("/test", service="myProject.test.controller.art")
* #Cache(expires="+5 minutes", public=true)
*/
class BlogController
{
/**
* #Route("/{text}", defaults={"text" = null})
* #Route("/topic/{tag}", defaults={"tag" = null})
* #Method({"GET"})
*/
public function listAction(ArtQuery $query)
{
//.................
}
}
Controller 2:
**
* #Route("/test" , service="myProject.test.controller.sitemap"))
* #Cache(expires="+5 minutes", public=true)
*/
class SitemapController
{
/**
* #Route("/sitemap.xml/")
* #Method({"GET"})
*/
public function sitemapAction()
{
//..................
}
}
The problem is that the second Controller is never matched only if is add in my #route("/sitemap.xml/") but I realy want the route to be only #route("/sitemap.xml").
I think the problem is when i input the url /test/sitemap.xml Symfony treats sitemap.xml as /{text} variable route in first controller.
Can I make a exception so that first controller ends as soon as it hits sitemap.xml....?
I read something about requirements but dont quiet understand this concept
The router will use the first route that matches the path.
The only way to prioritize a route over another which could match is to ensure that the stricter requirements are check before the weaker ones.
Normally this would be accomplished by placing the sitemapAction method above listAction.
However since you have a controller for each of these, you will have to put the controllers in the correct order.
To do this you will need to add the controllers to the config individually like this:
app_sitemap:
resource: "#AppBundle/Controller/SitemapController.php"
type: annotation
prefix: /
app_blog:
resource: "#AppBundle/Controller/BlogController.php"
type: annotation
prefix: /
This way the controllers will be iterated in this order.
However it is better if you can give each route a unique path, perhaps:
#Route("/query/{text}", defaults={"text" = null})
according to documentation
Earlier Routes always Win
What this all means is that the order of the routes is very important.
If the blog_show route were placed above the blog route, the URL
/blog/2 would match blog_show instead of blog since the {slug}
parameter of blog_show has no requirements. By using proper ordering
and clever requirements, you can accomplish just about anything.
http://symfony.com/doc/current/book/routing.html
i suggest to use yml or xml file for routing
or you can make a requirement in your first route
/**
* #Route("/{text}", defaults={"text" = null}, requirements={"text" = "^(?!sitemap\.xml)$"})
* #Route("/topic/{tag}", defaults={"tag" = null})
* #Method({"GET"})
*/
public function listAction(ArtQuery $query)
{
//.................
}
is there anyway to prioritize routes in Symfony2?
i'm using annotation it looks like this
Controllers
//TestController.php
/**
* #Route("/test")
*/
class TestController extends Controller
{
/**
* #Route("/a", name="test_a")
*/
.....
//DummyController.php
/**
* #Route("/")
*/
class DummyController extends Controller
{
/**
* #Route("/{varA}/{varB}", name="dummy_one")
*/
.....
Config
//routing.yml
acme_bundle:
resource: "#Acme/Controller"
type: annotation
Goal
URL , Actual , Goal
/test/a , DummyController , TestController //Wrong
/test/b , DummyController , DummyController //Good
How can i force TestController to be tested first ?
Thanks
from your example i can assume that your dummy and test controller are in the same bundle, if this is the case then you just lists the controllers in that bundle individually in your routing.yml. the order you list them is the order they will be checked.
acme_test:
resource: "#Acme/Controller/TestController.php"
type: annotation
acme_dummy:
resource: "#Acme/Controller/DummyController.php"
type: annotation
if they are in different bundles, just list the bundle with the test controller first.
see the symfony routing doc for details. http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/routing.html#activation
So Symfony will consume the controllers in an alphabetical order and will add the routes one by one.
there is no way up to add priority at the moment without using another bundle for that currently version 2.5
https://github.com/symfony-cmf/Routing is a great bundle if you are looking for advanced routing.
You don't need to reorder everything. Just overwrite the rules for DummyController at the desired ranking. You can do this by creating a route to the controller at the end of your routing.yml.
So in your routing.yml as the last lines you will add:
app_dummy:
ressource: "#YourBundle/Controller/DummyController.php
type: annotation
Adding an answer as it might help other users viewing this question. As of Symfony 5.1, a priority can be assigned to each route:
#[Route(path: '/', name: 'home')]
public function home(): Response
{
return $this->render('home.html.twig');
}
#[Route(path: '/about', name: 'about', priority: 10)]
public function about(): Response
{
return $this->render('about.html.twig');
}
In this case (it's just a dummy example), the about route with a priority of 10 will be evaluated before the home route, which has the default priority of 0.
Check out the feature introduction on the Symfony blog.