Ive just started playing with symfony2 and im working on adding url etc.
I cant seem to get my twig template to pick up on my function when passed its name using #Route.
Any ideas why?
Controller:
/**
* #Route("/cube/{number}", name="get_cubed")
*/
public function indexAction($number)
{
$cube = $number * $number * $number;
return $this->render('NumberCubedBundle:Default:index.html.twig',
array('number' => $number, 'cube' => $cube)
);
}
My Twig File:
{% extends '::base.html.twig' %}
{% block title %}Cube Number Generator{% endblock %}
{% block body %}
{{ number }}^3 = {{ cube }}
Cube 40
{% endblock %}
The Error:
An exception has been thrown during the rendering of a template ("Unable to generate a URL for the named route "get_cubed" as such route does not exist.") in NumberCubedBundle:Default:index.html.twig at line 5.
Any help would be massively appreciated.
Thanks
EDIT: why the annotation didn't work
Most probably you didn't include Sensio's vendor bundle as explained in annotation routing and sensio framwork EXTRA bundle.
The default routing mechanism uses the routing file (routing.yml) which does not need the extra bundle.
The routing through annotations, on the other hand, are considered an additional feature that is not always desired and thus has been extracted to a separate and optional bundle.
You need to configure the route in the routing file:
app/config/routing.yml
Define the route get_cubed using the standard Symfony 2 routing syntax.
Much like this:
get_cubed:
path: /cube/{number}
defaults: { _controller: NumberCubedBundle:Default:index }
requirements:
number: \d+
Now you should be able to get the page with the route:
.../app_dev.php/cube/40
The Solution for pattern annotation is:
on app/config/routing.yml add :
tuto_produit_bundle:
resource: "#ProduitBundle/Controller/"
type: annotation
prefix: /cat
and o URL:
http://localhost/produits/Symfony/web/app_dev.php/cat/{adresse}/{route action}
like http://localhost/vente%20produits/Symfony/web/app_dev.php/cat/categorie/
Related
I would like to configure multiple URLs to one Action in Controller (internationalization purposes).
According to this answer it was surely possible in symfony2 to:
Make double annotation route.
Use 3rd party Bundle (for example "BeSimple's").
But I'm using Symfony 3.0.3 which prohibits me from doing so until I change the route's name (example):
/**
* #Route("/welcome", name="welcome", defaults={"_locale" = "en"})
* #Route("/bienvenue", name="welcomeFR", defaults={"_locale" = "fr"})
* #Route("/willkommen", name="welcomeDE", defaults={"_locale" = "de"})
*/
But adding additional "FR/DE" chars to routes change their presence and ruins my URL generating logic in template, I'm forced to make on all links:
{# homepage example #}
{% if _locale = 'en' %}
{{ path('welcome') }} {# Routes from set only for "en" #}
{% elseif _locale = 'fr' %}
{{ path('welcomeFR') }} {# "fr" only links #}
{% endif %} {# and so on #}
Anyone found the proper solution for this problem?
AFAIK, this is the preferred way to point multiple routes to an unique controller action. So, your current problem is to regenerate the current path, depending on which route is being used
Maybe you don't have to modify your logic if you use {{ app.request.get('_route') }} to get the name of your current routing. This way, you could use:
{{ path(app.request.get('_route')) }}
UPDATE:
What about create an action per route and forwarding them to the main language action? Maybe it is not the best practice, but could work fine
/**
* #Route("/welcome", name="welcome", defaults={"_locale" = "en"})
*/
public function welcomeAction()
{
/* code here */
}
/**
* #Route("/bienvenue", name="welcomeFR", defaults={"_locale" = "fr"})
*/
public function welcomeFrAction()
{
$response = $this->forward('AppBundle:ControllerName:welcome');
}
/*
* #Route("/willkommen", name="welcomeDE", defaults={"_locale" = "de"})
*/
public function welcomeDeAction()
{
$response = $this->forward('AppBundle:ControllerName:welcome');
}
After wasting 10 hours on finding solution on Symfony3 I've made those assumptions:
None of the route trick with exactly same "name" wouldn't work,
Any kind like double annotation, double "routing.yml" importing, host based matching gives the same effect - only one route is matched, usually the last one. Would be ("/willkommen", name="welcomeDE") in my example.
Both of the i18n bundles does not work on Symfony3 through the versioning constraints,
Composer wouldn't even let us install the bundle.
Locale Listener and Loader (Routing Component) is dying on caching.
My solution to match the "_locale" and pass it to the LocaleLoader in order to load "routing_en.yml/routing_fr.yml" etc. files respectively is cached on the first match by the Symfony and after that, changing the "_locale" does not affect route's mapping.
In conclusion
Symfony3 seems not supporting route "example" with more than one "url" at all. Through the constraints and caching.
After the disappointment I'm considering going back to Symfony 2.8, have no idea what was pointing Symony's masters to block "double annotation" solution and what is their current idea on links internationalization.
Hope someday it will be possible to achieve with S3, as link i18n is quite common SEO practice.
EDIT: Confirmed, working like a charm on 2.8.5 with BeSimple's i18n.
I've created a custom Sonata page
Simple route
medapp_adminStreamCommands:
path: /admin/stream
defaults: { _controller: MedAppBundle:VideoChat/VideoChat:adminStreamCommands }
Controller that returns the admin pool
public function adminStreamCommandsAction(Request $request)
{
return $this->render('#MedApp/AdminSonata/Stream/stream_commands.html.twig', array(
'admin_pool' => $this->get('sonata.admin.pool')));
}
Plain view template
{% extends '#MedApp/AdminSonata/standard_layout.html.twig' %}
{% block content %}
foobar
{% endblock content
This works, I can access it on my website with /admin/foo and I get a page which has the Sonata admin template with my 'foobar' content.
My question is, how can I add this route to the left and top navbar without having to modify the default template?
That is because the left menu is rendered by a KNP menu:
{% block side_bar_nav %}
{% if app.user and is_granted('ROLE_SONATA_ADMIN') %}
{{ knp_menu_render('sonata_admin_sidebar', {template: admin_pool.getTemplate('knp_menu_template')}) }}
{% endif %}
{% endblock side_bar_nav %}
And I somehow need to add my new page to be rendered by this menu.
Normally, a page is added through a service, but these are built on top of an entity:
servicename:
class: Bundle\Class
arguments: [~, Bundle\Entity\Entityname, ~]
tags:
- { name: sonata.admin, manager_type: orm, group: admin, label: CustomName}
My page is not using an entity, though, just static content or content that is not dependant on an entity.
I know already that I can modify the blocks that generate the menus, but I was thinking that the best way would be to add my class as a service tagged as sonata.admin that doesn't have an orm manager_type, in other words, is not an Entity. How can that be done?
You should override standard_layout and modify content of side_bar_nav block. This is simple and fast way. Or you can dig into sonata code to find how to inject something into admin_pool.dashboardgroups - have fun :)
I don't think that's possible, you have to create a new layout, copy the sonata admin layout and customize it to your need.
You can change the layout used by changing the yml configuration for sonata_admin (templates -> layout) or extending the SonataAdmin bundle and creating your own layout.html.twig.
I've not been able to find anything useful about this in the Twig or Symfony2 documentation, so thought I would ask here.
Does anybody know if it's possible to include a Twig template in Symfony2 relative to the current bundle, without specifying the name? Something along these lines:
{% include .:Foo:bar.html.twig %}
I'm just a bit fed up of having to enter the long, ugly bundle name when they're all in the same bundle. Also means if the bundle name ever changed for whatever reason, I'd have to find & replace every single include.
Back in the days when I was using bundles, I came up with a quick solution that you could base upon:
{% set bundle = app.request.get('_template').get('bundle') %}
{% set controller = app.request.get('_template').get('controller') %}
{% include bundle ~ ':' ~ controller ~ ':foo.html.twig' %}
Here you can find my n-th question on Symfony2.
I'm working with a pagination bundle that uses the route name provided in the routing.yml file.
From my perspective, this approach is not flexible and lead to a dirty code, since if I change the name of the route, then I have to look at all the Twig templates or PHP files to update the route name. This is ok for small Web applications, but will provide such a bug for larger applications and also need an high burden for the developer.
So, I was wondering to pass a string variable x to the Pager object provided by the above mentioned bundle. The string x should be initialized within the controller and has to provide the desired route name as given in the routing.yml file.
Let me give an example. The routing file is the following:
//routing.yml
AcmeTestBundle_listall:
pattern: /test/page/{page}
defaults: { _controller: AcmeTestBundle:List:listall, page: 1 }
requirements:
page: \d+
Then the related controller is:
//use something....
class ListController extends Controller
{
public function exampleAction($page)
{
$array = range(1, 100);
$adapter = new ArrayAdapter($array);
$pager = new Pager($adapter, array('page' => $page, 'limit' => 25));
return array('pager' => $pager);
}
}
Then, in the twig template, the $pager receives the route name that refer to the above bundle
{% if pager.isPaginable %}
{{ paginate(pager, 'AcmeTestBundle_listall') }}
{% endif %}
{% for item in pager.getResults %}
<p>{{ item }}</p>
{% endfor %}
Any idea of how to get the 'AcmeTestBundle_listall' string value at runtime just inside the controller?
You can use the app global variable that is available in twig to get the current route from the request.
{% if pager.isPaginable %}
{{ paginate(pager, app.request.get('_route') }}
{% endif %}
More about app here and here.
How can i create a link to the current in my template?
I want to create a language switcher which should link to the current page in varios languages, so all parameters should be the same exept for the locale.
I end up rolling my own function for this. I though at first it was included somewhere in FrameworkBundle but did not find anything about it. Here the steps I took.
At first, I created a Twig extension function that would output the same route as the one the user is visiting currently (parameters and query string included). I left this step to you. You can look at this link from a good tutorial on Symfony2 for the description of how to create a Twig extension if you don't know how already. I could help you with it if you need it.
Next step is to create the function itself that will switch the locale of the current route. This function will need the Request and Router objects as dependencies. In my personal case, I put this function in a dedicated service named RoutingHelper service. This service is then used by my Twig extension function. Here the service definition I added to the dependency container:
acme.helper.routing:
class: Application\AcmeBundle\Helper\RoutingHelper
scope: "request"
arguments:
request: "#request"
router: "#router"
And the constructor of my service:
protected $request;
protected $router;
public function __construct($request, $router)
{
$this->request = $request;
$this->router = $router;
}
The $locale parameter is new locale would like to switch to. Here the function:
public function localizeCurrentRoute($locale)
{
$attributes = $this->request->attributes->all();
$query = $this->request->query->all();
$route = $attributes['_route'];
# This will add query parameters to attributes and filter out attributes starting with _
$attributes = array_merge($query, $this->filterPrivateKeys($attributes));
$attributes['_locale'] = $locale;
return $this->router->generate($route, $attributes);
}
Essentially, it does what others have put so far but it also handles parameters and query string. The method filterPrivateKeys will remove private attribute from the route attributes. Those attributes are the one starting with an underscore and should not be passed back to the route generator. Here its defintion:
private function filterPrivateKeys($attributes)
{
$filteredAttributes = array();
foreach ($attributes as $key => $value) {
if (!empty($key) && $key[0] != '_') {
$filteredAttributes[$key] = $value;
}
}
return $filteredAttributes;
}
In the end, I'm able to this in my Twig view to create links to switch locales:
{% block language_bar %}
English
Français
{% endblock %}
Edit:
Here my twig extension service definition:
acme.twig.extension:
class: Application\AcmeBundle\Twig\Extension\AcmeExtension
arguments:
container: "#service_container"
tags:
- { name: twig.extension }
And in the twig extension function I have this call: $routingHelper = $this->container->get('acme.helper.routing');
This should solve the scope widening exception happening because the twig extension is not in the request scope.
Update:
It is now possible with Symfony 2.1 to have a locale switcher in an easier way than before. Indeed, the 2.1 version of Symfony introduced a new parameter for routes that make it more easy to do a locale switcher. Here the code, all in twig
{% set route_params = app.request.attributes.get('_route_params') %}
{# merge the query string params if you want to keep them when switching the locale #}
{% set route_params = route_params|merge(app.request.query.all) %}
{# merge the additional params you want to change #}
{% set route_params = route_params|merge({'_locale': 'fr'}) %}
{{ path(app.request.attributes.get('_route'), route_params) }}
It is still a few lines of twig code, but could be included in a Twig block for easier reuse. Credits to stof, from the Symfony community, for the code above.
Hope this is what you are looking for.
Regards,
Matt
Current page
Similar question: language switching without changing the current page
English