In Symfony is it possible to access a controller through a route that potentially can contain indefinite number of slug, taking advantage from internal routing system?
e.g.
my_route:
pattern: /{slug_parent}/{slug_child}/{slug_nephew}/{slug_...}/...
as
www.mydomain.com/math/arithmetic/fractions
but also
www.mydomain.com/tech/android
It can be done, but instead of creating routes in config YML I would recommend to use #Route annotations. Add annotations in the controller class, like this:
/**
* #Route("/{slug_parent}/{slug_child}")
* #Route("/{slug_parent}/{slug_child}/{slug_nephew}/")
* #Route("/{slug_parent}/{slug_child}/{slug_nephew}/{slug_...}/")
*/
public function yourControllerAction()
{
...
}
http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/routing.html
Related
I use annotations to define routes in my project. I have a controller method that has such a route:
/**
* #Route("/{slug}", name="showcompany")
*/
public function show(Company $company): Response
{
...
}
This is done to catch requests like website.com/company1, website.com/company2, etc...
I get this error:
"App\Entity\Company object not found by the #ParamConverter
annotation."
Since obviously all other routes such as website.com/signup are considered as slugs of the Company entity.
The question is how to avoid this behavior without moving all routes to routes.yaml and ordering them manually? I want all routes to be prioritized over this /{slug} route.
I'm creating an application with Symfony 3.2.9, and I would to do a Admin panel to manage application. Application works like CMS, so is creating new pages with URL, like domain.com/pagename1 and also domain.com/pagename1/subpagelevel2 ect. Problem is when I want to create URL for Admin panel, and URL should looks like: domain.com/admin, but also admin panel need some sub pages, like domain.com/admin/manage or domain.com/admin/manage/edit/1 ect.
I created DefaultController with routing like :
/**
* #Route("/", name="homepage")
*/
and AdminController with routing like :
/**
* #Route("/admin", name="admin")
*/
Problem is that when I want to dynamically create new sub page of application I need to create routing like:
/**
* #Route("/{page}")
*/
But this overwrite my Admin panel sub pages (eg. domain.com/admin/manage).
Is it way, to exclude or overwrite path from default DefaultController by AdminController? I want to have possibility to create all URL-s from DefaultController excepts paths beginning like domain.com/admin ... and so on.
From documention in https://symfony.com/doc/current/routing.html you can use the requirements parameter to specify a more strict match
I guess something like this whould work:
DefaultController:
/**
* #Route("/", name="homepage")
*/
AdminController:
/**
* #Route("/admin", name="admin")
*/
Other Controller:
/**
* we exclude page=admin from this controller
* #Route("/{page}", requirements={"page": "^(?!admin).+"}))
*/
Routes are searched in the order they are listed - so put the most generic at the end of the list, and it will find and use /admin before /{page}
For example, one of my last routes at the bottom of app/conf/routing.yml is
# http://symfony.com/doc/current/routing/redirect_trailing_slash.html
remove_trailing_slash:
path: /{url}
defaults:
_controller: AppBundle:Default:removeTrailingSlash
requirements:
url: .*/$
methods: [GET]
Cleanest on your use case:
Why not simply create a separate Bundle for Admin and put a prefix in AdminBundle routes?
Depend on routing orders and/or REGEX in routes is not recommended just to avoid a Bundle Creation. This is for what Bundles have been thought.
app/config/routing.yml
admin:
resource: "#AdminBundle/Controller/"
type: annotation
prefix: /admin
Then, all controllers/routes under AdminBundle will work under /admin prefix.
For example, IndexController/DefaultController/WhatEverController with this route inside AdminBundle:
/**
* #Route("/")
*/
public function indexAction()
{
//My code
}
Will match "/admin" instead of "/"
And:
/**
* #Route("/{page}")
*/
public function indexAction()
{
//My code
}
Will match "/admin/{page}" instead of "/{page}"
Since Symfony 5.1, you can define route priority :
/**
* #Route("/admin", name="admin", priority=10)
*/
/**
* #Route("/{slug}", name="pages", priority=99)
*/
Just imagine, I have a controller with several actions in it.
And to reach each action I have to define it strictly in routing.yml file like this:
admin_edit_routes:
pattern: /administrator/edituser
defaults: { _controller: MyAdminBundle:Default:edituser }
admin_add_routes:
pattern: /administrator/adduser
defaults: { _controller: MyAdminBundle:Default:adduser }
And I can have plenty of such pages. What I want to achieve is to define necessary action in my URI, just like Kohana routes do.
I mean, I want simply to use one route for all actions:
(code below is not valid, just for example)
admin_main_route:
pattern: /administrator/{action}
defaults: { _controller: MyAdminBundle:Default:{action} action:index}
It will pass all requests to /administrator/{anything} to this route, and after that, according to the action, stated in the uri, the needed action is gonna to be called.
If action is not stated in the uri, then we got thrown to index action.
Is it possible in Symfony 2 in any ways and if yes, then how?
Thanking in advance.
This doesn't feel right to do so. By exposing your controller API (methods) "on the fly" directly via GET method, you open your application to security vulnerabilities. This can also easily lead to unwanted behaviours.
Moreover, how would you generate routes in twig for example ? By giving explicit constants tied to the method names?
{{ path('dynamic_route', { 'method': 'addUser' }) }}
This is not how Symfony works nor what it recommands. Symfony is not CodeIgniter nor Kohana. You don't need a second "Front Controller"
If this is a laziness matter, you can switch your routing configurations to annotations. And let Symfony auto-generate the routes you need, with minimal efforts.
namespace Acme\FooBundle\Controller;
/**
* #Route("/administrator")
*/
class DefaultController extends Controller
{
/**
* #Route
*/
public function indexAction(Request $request);
/**
* #Route("/adduser")
*/
public function addUserAction(Request $request);
/**
* #Route("/edituser")
*/
public function editUserAction(Request $request);
/**
* #Route("/{tried}")
*/
public function fallbackAction($tried, Request $request)
{
return $this->indexAction($request);
}
}
The fallbackAction methods, returns any /administrator/* route which does not exist to the indexAction content.
To sum up, I advise not to use dynamic routing "on the fly" directly to method names.
An alternative would be to create a command which will generate routes once executed.
Well, maybe that's not the best way to do that, but it works. Your dynamicAction accepts action string parameter and checks if such action exists. If so, it executes given action with params.
/**
* Default Controller.
*
* #Route("/default")
*/
class DefaultController extends Controller
{
/**
* #Route("/{action}/{params}", name="default_dynamic")
*/
public function dynamicAction($action, $params = null)
{
$action = $action . 'Action';
if (method_exists($this, $action)) {
return $this->{$action}($params);
}
return $this->indexAction();
}
}
I'm curious if someone have any other ideas :>
I'm going to create a modules system in my Symfony 2 app. Each module will be a bundle.
I don't know how to I can dynamically (in my service code) load routes from file (eg. AcmeSomeModuleBundle/Resources/config/routing.yml) and apply them with some prefix (or host). Like it's done by embedding code below in app/config/routing.yml:
berg_applications:
resource: "#AcmeSomeModuleBundle/Resources/config/routing.yml"
host: foobar.com
Any solutions?
You need custom route loader IMO: http://symfony.com/doc/current/cookbook/routing/custom_route_loader.html
For one project, I had to build route loader which loaded routes by fetching them from remote URL via CURL and it worked perfectly.
Documentation is very clear and it's silly easy to build one yourself when you look at the example. Basically, key things are:
"type" when you're defining a route resource. You should make your custom type so that your route loader recognizes it and takes it for processing.
::load() method.
If you have any concrete problems you stumble upon don't hesitate to post question in comment. Basically, your RouteLoader will receive "resource" in it's load method and should do whatever it needs to do with it to add new Route to Router.
If you do a true bundle approach for each module, then the easiest way to accomplish what your trying to do is use the JMS Security-Extra bundle with attribute-based routing.
To your composer.json file, add this:
"require": {
...
"jms/security-extra-bundle": "1.5.*",
Update your composer file with
php composer.phar update
Then in your BundleName/Resources/config/routing.yml file do this:
some_name:
type: annotation
resource: "#SomeBundle/Controller"
Finally, for each action in your controller, decorate it with #Route attributes:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
/**
* #Route("/SomeBundle/SomeController")
*/
class SomeController extends Controller {
/**
* #Route("someAction", name="myAction")
* #Method("GET") OR
* #Method({"GET", "POST"})
*/
public function someAction() {
}
}
Some of the other attributes in the JMS bundle make things really nice as well. For example, I use the #Template attribute on my actions quite a bit. This means that I no longer have to do:
public function recentListAction() {
...
return $this->render(
'AcmeArticleBundle:Article:recentList.html.twig',
array('articles' => $articles)
);
}
I can simply do:
/**
* #Route("/Articles/List")
* #Template()
*/
public function recentListAction() {
...
return array('articles' => $articles);
}
And as long as I have a Resources/views/ControllerName/recentList.html.twig file, everything will be weaved together for me automatically.
I just downloaded symfony2 and am beginning to play with routing via annotations. I have my app/config/routing.yml set in the bundle I created to use annotations, and have deleted the Acme bundle and all routing references to it. That said, I've tried creating a couple different route annotations in my controller like #Route("/") and #Route("/hello/{name}") but I'm always greeted with a 404 error (using the dev environment). If I add the route in routing.yml it works just fine even though the routing is configured to use annotations. For whatever reason, my annotations are seemingly being ignored.
Here is my app/config/routing.yml:
DanDefaultBundle:
resource: "#DanDefaultBundle/Controller/"
type: annotation
prefix: /
And here is my controller method:
/**
* #Route("/")
* #Template()
*/
public function indexAction()
{
return array('name' => 123);
}
I've included the Sensio\Bundle\FrameworkExtraBundle\Configuration\Route namespace - everything as far as I can tell is correct with what I've seen in the documentation. What am I overlooking that's causing symfony2 to seemingly ignore my routing annotations? Again, if I add the routes to the routing yaml everything works so my bundle is working - but annotations seem to be ignored.
Thanks!
Dan
UPDATE:
It looks like I had to add the routes to routing_dev.yml in addition to routing.yml since I was operating in the dev environment. I suppose that's so you have have different routes in-between development and production? I suppose special care will have to be taken to make sure those routes stay in sync?
You accidentally removed the inclusion of routing.yml from routing_dev.yml.
if you use Route Prefix in your routing.yml
you must declare about your prefix above your class declaration like that :
/**
* #Route("/")
*/
class PostController extends Controller
{
/**
* #Route("/")
* #Template()
*/
public function indexAction()
{
}
/**
* #Route("{id}")
* #Template()
*/
public function showAction($id)
{
}
}
Like into Sensio FrameworkExtra Bundle Documentation