Symfony 2 #Route annotation case-sensitive - php

I'm trying to create a url with annotations of the route.
The problem is that I can write any URL large, small or different.
#Route("/{staat}/", name="showStaats",requirements={"location" = "berlin|bayern|brandenburg"})
This URL can be accessed both from www.example.com/berlin and under www.example.com/Berlin.
I would, however, that it is attainable only under www.example.com/berlin.

Answering the question "How to make case-insensitive routing requirement":
You can add case-insensitive modifier to requirement regexp like so:
(?i:berlin|bayern|brandenburg)

You have "/{staat}/", but your requirements set "location" = ..., these should match, so maybe that's the cause of your problem.
If you don't want to hardcode the list of states in your route, you could inject a service containter parameter with a list of states. Just see How to use Service Container Parameters in your Routes in the documentation for how to do that.
If you just want to check, whether that state is all lower-cased you could try the following requirement:
staat: "[a-z-]+"
This should match only lowercase characters and dash (e.g. for "sachsen-anhalt"). But I'm not entirely sure if this will work as the router's regex-detection is a bit quirky.
You could also create a custom Router Loader which will create routes programmatically, e.g. by fetching the list of states from a database or file.
edit:
As I wrote in my comment I would add the list of params as a Service Container parameter, e.g. %my_demo.states% containing a list of states. I'm not sure however if this will work with annotations. So here is a quick workaround how to get it working.
In your app/config/config.yml you append the %my_demo.states% parameter:
my_demo:
states: ["berlin", "brandenburg", "sachsen-anhalt", ... ]
In your app/config/routing.yml there should be something like this:
my_demobundle:
resource: "#MyDemoBundle/Controller/"
prefix: /
type: annotation
The type: annotation and #MyDemoBundle is the relevant part. Add the following route before this one, to make sure it takes precedence:
showStaats:
path: /{state}
defaults: { _controller: MyDemoBundle:State:index }
requirements:
state: %my_demo.states%
This will add a route which will apply before your annotations using the list of states as parameters. This is a bit crude, as you are mixing yml/annotation-based routing, but it's imo still better than cramming a list of 16 states in the annotation, not to mention its easier to maintain.

Related

Is it possible to construct a Symfony path using only parameters?

I would like to reduce the number of repetitive code and give a canonical URL in my Drupal 8 application. Since the routing system is built on Symfony, I included it in the title.
I am constructing paths under routes in my mymodule.routing.yml file. I want to match a specified number of different strings in the first argument, and a slug which can be any string in the second argument. It looks like this:
entity.my_entity.canonical:
path: '/{type}/{slug}'
defaults:
_controller: '\namespace\PostController::show'
requirements:
_permission: 'perm'
type: different|strings|that|can|match|
Now, when I try to access using for example /match/some-slug then it just says "Page not found".
If I something static to the path, for example path: '/j/{type}/{slug}', then it works as expected when I open /j/match/some-slug in the browser.
My boss doesn't like any unnecessary characters in the URL though, so I would like to achieve this by using two parameters, like shown in the first example.
As Yonel mentioned in the comments you can use debug:router to check all your routes. I don't see anything wrong with your code.
Try running bin/console router:match "/match/blaaa" and if you see some controller that isn't the one you want then you'll need to change the route. It shouldn't be the case though because you're getting a 404.
Here's my exact setup that works
routing.yml:
entity.my_entity.canonical:
path: '/{type}/{slug}'
defaults:
_controller: 'MyBundle:Something:foo'
requirements:
type: different|strings|that|can|match|
Inside MyBundle\SomethingController:
public function fooAction($id)
{
return new Response("bar");
}
Then going to http://localhost/match/fom shows the "bar" response.
I have read the documentation again (RTM), and found out that it is not possible in Drupal 8, while it is possible in Symfony.
Note that the first item of the path must not be dynamic.
Source: Structure of routes in Drupal 8

Doubts around Symfony2 Routing component usage

I'm setting up FOSRestBundle and have some doubts, don't know if related to Symfony2 Routing component or can be done in any other way. Here,
1) How do I check if X-PDONE-SESSION-ID is set on request headers before execute the method? Can this be done using annotations on routing? Any ideas on how to check that?
2) I need to use this RegEx \b(?:(?:https?):\/\/|www\.)[-a-z0-9+&##\/%?=~_|!:,.;]*[-a-z0-9+&##\/%=~_|] as requirements in the route #QueryParam(name="token", requirements="") for check valid URLs, how?
I have read here and here but wasn't helpful at all.
(1) can be done using the method described in the book article you referenced as not being helpful:
As you've seen, a route can be made to match only certain routing wildcards (via regular expressions), HTTP methods, or host names. But the routing system can be extended to have an almost infinite flexibility using conditions:
contact:
path: /contact
defaults: { _controller: AcmeDemoBundle:Main:contact }
condition: "context.getMethod() in ['GET', 'HEAD'] and request.headers.get('User-Agent') matches '/firefox/i'"
The condition is an expression, and you can learn more about its syntax here: The Expression Syntax. With this, the route won't match unless the HTTP method is either GET or HEAD and if the User-Agent header matches firefox.
In (2), you show a regex for a complete URL and not just a query parameter. It isn't possible to change the whole regex used by the Routing component by default as far as I know.

"Name" of the route in Symfony2 relevance

I am wondering, what is the use of the "name" of the route in Symfony2 routes.yml file
_welcome:
pattern: /
defaults: { _controller: AcmeDemoBundle:Welcome:index }
For example here, pattern and defaults are obviously keywords, however, what the _welcome stands for? Is it arbitrary or a is it kind of predefined keyword for every bundle? Thank you in advance.
The route name is useful for route debugging and generating urls. You will find the route name is used extensively in Twig templates when generating links with the path() function. You can also generate urls from the route name in a controller. More information here
It is good to follow a logical convention when naming routes. Something like:
bundle_name.controller.action is a good place to start.
In this case _welcome is an arbitrary, unique id for each route you have in your project. You need it if you want to generate a url out of a template or if you want to overwrite a vendor-route...

In Symfony2 where is the correct place to store an app wide parameter?

I'd like to store a few application specific values for example:
a default Id number for a particular user choice if it's not set yet
keys/tokens/secrets for various services API's like facebook or flickr
Closest I've found so far is http://symfony.com/doc/2.0/cookbook/bundles/best_practices.html#configuration
If I used app/config/parameters.ini it would look like:
[flickr]
callbackUrl = http://example.com/approve
requestTokenUrl = http://www.flickr.com/services/oauth/request_token
consumerKey = 123a1237a29b123a5541232e0279123
[app]
default_layout = 2
these should be available in different bundles and also in templates
these should be available in different bundles and also in templates
They are. As long as you can access the container, you can access the parameters. From the docs you linked to:
$container->getParameter('acme_hello.email.from');
I think there's an error in your parameters.ini example. 'flickr' and 'app' shouldn't be wrapped in brackets. Also, the first element of parameters.ini should be [parameters].
Personally, I like using an app.yml file because I'm used to using it in Symfony 1.x projects (and because I don't see the reason for using an .ini file.). You can create app/config/app.yml and import it into your app/config/config.yml file like this:
imports:
- { resource: app.yml }
Your app.yml would look like this:
parameters:
flickr:
callbackUrl: http://example.com/approve
requestTokenUrl: http://www.flickr.com/services/oauth/request_token
consumerKey: 123a1237a29b123a5541232e0279123
app:
default_layout: 2
And this is how you would access data:
$container->getParameter('flickr.callbackUrl');
A third option is to define your parameters directly in app/config/config.yml. The code would be exactly the same as my example for app/config/app.yml. I don't recommend doing this though because app/config/config.yml can get pretty filled up with bundle configuration parameters, and I think it's cleaner to keep your own app params in a separate file. But of course, it's all up to you.

Routing in Symfony2

How to setup default routing in Symfony2?
In Symfony1 it looked something like this:
homepage:
url: /
param: { module: default, action: index }
default_symfony:
url: /symfony/:action/...
param: { module: default }
default_index:
url: /:module
param: { action: index }
default:
url: /:module/:action/...
I was looking through the cookbook for an answer to this, and think I've found it here. By default, all route parameters have a hidden requirement that they match any character except the / character ([^/]+), but this behaviour can be overridden with the requirements keyword, by forcing it to match any character.
The following should create a default route that catches all others - and as such, should come last in your routing config, as any following routes will never match. To ensure it matches "/" as well, a default value for the url parameter is included.
default_route:
pattern: /{url}
defaults: { _controller: AcmeBundle:Default:index, url: "index" }
requirements:
url: ".+"
I don't think it's possible with the standard routing component.
Take a look to this bundle, it might help :
https://github.com/hidenorigoto/DefaultRouteBundle
// Symfony2 PR10
in routing.yml:
default:
pattern: /{_controller}
It enables you to use this kind of urls: http://localhost/MySuperBundle:MyController:myview
Symfony2 standard routing component does not support it, but this bundle fills the gap Symfony1 left:
https://github.com/LeaseWeb/LswDefaultRoutingBundle
It does what you expect. You can default route a bundle using this syntax:
FosUserBundle:
resource: "#FosUserBundle"
prefix: /
type: default
It scans your bundle and automatically adds routes to your router table that you can debug by executing:
app/console router:debug
Example of automatically added default routes:
[router] Current routes
Name Method Pattern
fos_user.user.login_check ANY /user/login_check.{_format}
fos_user.user.logout ANY /user/logout.{_format}
fos_user.user.login ANY /user/login.{_format}
...
You see that it also supports the automatic "format" selection by using a file extension (html, json or xml).
Here is an example: http://docs.symfony-reloaded.org/master/quick_tour/the_big_picture.html#routing
A route definition has only one mandatory parameter pattern and three optionals parameters defaults, requirements and options.
Here's a route from my own project:
video:
pattern: /watch/{id}/{slug}
defaults: { _controller: SiteBundle:Video:watch }
requirements: { id: "\d+", slug: "[\w-]+"
Alternatively, you can use #Route annotation directly in a controller file. see https://github.com/sensio/SensioFrameworkExtraBundle/blob/master/Resources/doc/annotations/routing.rst
As for the default routes, I think Symfony2 encourages explicit route mapping.
Create a default route is not a good way of programming. Why? Because for this reason was implemented Exception.
Symfony2 is built just to do right things in the right way.
If you want to redirect all "not found" routes you should use exception, like NotFound404 or something similar. You can even customise this page at your own.
One route is for one purpose. Always. Other think is bad.
You could create your own bundle that handled all requests and used URL parameters to construct a string to pass to the controller's forward method. But that's pretty crappy, I'd go with well defined routes, it keeps your URLs cleaner, and decouples the URL and controller names. If you rename a bundle or something, do you then have to refactor your URLs?
If you want to create a "catch all", your best bet would be to hook on the KernelEvents::EXCEPTION event. This event gets triggered whenever an Exception falls through to the HttpKernel, this includes the NotFoundHttpException thrown when the router cannot resolve a route to a Controller.
The effect would be similar to Symfony's stylized 404 page that gets rendered when you send the request through app_dev.php. Instead of returning a 404, you perform whatever logic you're looking to.
It depends... Some of mine look like this:
api_email:
resource: "#MApiBundle/Resources/config/routing_email.yml"
prefix: /
and some look like
api_images:
path: /images/{listingId}/{width}/{fileName}
defaults: { _controller: ApiBundle:Image:view, listingId: null, width: null, fileName: null }
methods: [GET]
requirements:
fileName: .+

Categories