Redirect to route - symfony

I want some routes to "exists", but to redirect to another route.
For example, this is what I did with the / route
/**
* Homepage exists but redirect to projet
*
* #Route("/", name="homepage")
* #Method("GET")
*/
public function indexAction() {
return $this->redirectToRoute('projet_index');
}
/**
* #Route("/projets/", name="projet_index")
* #Method("GET")
*/
public function indexAction() {}
The thing I want to know is if it's this the best method to do it?

Nope the best way to do that is simple as that:
/**
* #Route("/", name="homepage")
* #Route("/projets/", name="projet_index")
* #Method("GET")
*/
public function indexAction() {
// your code here
}

This is somewhat off-topic, but there's a somewhat dirty trick you can use in a similar scenario, where you want to have several URLs resolving to a single route, using placeholders with requirements and default values:
/**
* #Route("/{path<projets/|>?}", name="projet_index")
*/
public function someAction() {
// ...
}
The way it works is by defining an optional placeholder (with the {...} syntax) called "path" (though you can call it something else). That placeholder is given requirements with the <...> syntax: it can be either "projets/" or the empty string "". Therefore, both the "/" and the "/projets/" URLs match that route, and nothing else, as they're off the form "/{path}" with a path placeholder that matches its requirements.
So far, so good. But there's still one thing we need to do: give it a default value with the ?, otherwise methods like redirectToRoute or the Twig function path will complain that we aren't given them a value for all the placeholders. Note that you could also use "/{path<projets/|>?projets/}" to make the default value URL "/projets/" instead of "/".
I know it's not exactly what OP wanted, but I think it can be a useful trick to know, and someone having a question similar to OP's might find it useful.

Related

Symfony getUser type hinting

I find it somewhat annoying to have to constantly use #var on getUser. It seems sloppy.
So I was thinking about starting to use this instead
<?php
// in the controller
$user = Customer::isCustomer($this->getUser());
// in the entity
/**
* #param Customer $user
*
* #return Customer
*/
public static function isCustomer(Customer $user)
{
return $user;
}
Is this a good idea? Bad idea? Horrible idea?
A type hint is the better option in this case.
Why would you write more code by adding checks manually rather than adding a simple type hint to your param.
Your four lines of codes representing two conditions give exactly the same result as:
/**
* #param Customer|null $user
*
* #return Customer|null
*/
public static function isCustomer(Customer $user = null)
{
// If $user is null, it works
// If $user is a Customer instance, it works
// If it's other, an exception is thrown
return $user;
}
Type hinting optimises and give more readability to a code.
It's a convention in symfony2, php and more.
It's commonly used as a constraint (or contract) with you and your method.
Also, it's the only alternative for an interface or an abstract class to add requirement to a parameter, because they don't have a body, and so cannot write conditions.
Update
In SensioLabs Insight, Object type hinting represents a warning using the following message :
The parameter user, which is an object, should be typehinted.
Because the verb should is used, I consider it's not a mandatory requirement, just a very good practice in case of it doesn't cause any problem.
Also, you can use the example you given without making your code horrible.

how to set the routing translation based on annotation?

All the routing of my website is realized based on the annotations. Now, I want to translate my routing. To realize that, I tried to use the bundle JMSI18nRoutingBundle.
Nevetheless, the documentation does not give any example how to specify the route for each locale.
This is an action with its routing, how to translate it?
/**
* #Route("/welcome", name="welcome")
* #Template()
*/
public function welcomeAction() {
return array();
}
Thanks,
Question after being edited
/**
* #Route("/welcome", name="welcome", defaults={"_locale" = "en"})
* #Route("/bienvenue", name="welcome", defaults={"_locale" = "fr"})
* #Route("/willkommen", name="welcome", defaults={"_locale" = "de"})
* #Template()
*/
public function welcomeAction() {
return array();
}
Now, what is happening with this new annotations:
the selected route is always the last one which is /willkommen (if you change the order the routes, the selected route is still the last one)
the _locale is set the the locale of the last route which 'de' according to the annotation above.
So, any proposal?
Thanks...
I found the solution. You just have to set run the following command
php app/console translation:extract fr --bundle=MinnTestBundle
--enable-extractor=jms_i18n_routing --output-format=yml
Then, minn/TestBundle/Ressources/translations/routes.fr.yml file will be generated. Customize you route translations & that is it!
Hope it will help others...
You can add multiple route annotations.
/**
* #Route("/welcome", name="welcome", defaults={"_locale" = "en"})
* #Route("/bienvenue", name="welcome", defaults={"_locale" = "fr"})
* #Template()
*/

Can I implement my own Symfony2 annotations easily?

Is there anything in the Symfony annotations modules that allow me to use them for other uses?
I know for #Route and #Method you need to extend existing libraries, so its just not that easy i'm guessing.
Currently, i'm working with the JS History API, and would LOVE to put the popState data for my JS files in the annotations. So they are already available when the routing generates the URL.
Q Doesn't this makes sense to have a, HTML5 annotated title, or some attribute here? It would be great to have the ability to define this data, as annotated, right next to the already existing route name and stuff.
Q Is there anybody that has tweaked with the annotations before?
I wanted to clarify my intentions here as I think I left out some crucial details (the mention of History API) for understanding my use case.
There is a few SPA front ends that have been integrated through a front-end bundle, and this connected via AJAX calls to a backend bundle which was a straight RESTful API, with the addition of a very fun-to-develop PHP API class I made that intereprets and processes (routes) the AJAX in a fashion that directly executes other PHP class controller `methods.
I use a lot of ajax for this Symfony 2 app (fosjsrouter) to handle routing. So instead of URLs triggering the routes and actions, the SPA click event fires off AJAX to the back end router, with a large JSON payload, not limited to PHP control parameter's (class/method/var names), and data sets.
OK, so getting back on track; Given the above scenario; In the JS class object end of the router, inside this I thought it was the best place to add some JS History API functionality to it, (state, back button, etc.)
The above class can be called if a history flag was called, which could become responsible for assigning initial state data. Primarily, this is because the JSON data object that's being around in this JS method contains already a lot of the crucial route data, and param information for that route needed in the backend PHP, which comes from the annotations.
So the idea is if I add accessibility for a history state title and URL to the annotations, then I will have access to that information right there available to define the initial state, if flagged, right inside the an ajax.done(), inside this main JS routing method.
Now we can pass state back and forth two ways between the db and realtime client-side async. You can use an observer, or anything, from there front-end, and jobs/queues on the backend to keep it fully reactive. (use React too :-))
EDIT I'm not so sure that's what I was thinking, it looks like its making me set the values of the title and url for this inside the return statement of the PHP function, where I want it set in the annotation (see return 'Matthias Noback';)
So I'm trying this, but where do I set these titles at?
<?php
namespace Blah\CoreBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
/**
* #Annotation
*/
class HistoryAnnotationController
{
//history state params are out properties here..
/**
* #var
*/
private $url;
/**
* #var
*/
private $title;
/**
*
*/
public function __construct()
{
}
/**
* #return mixed
*/
public function getTitle()
{
return $this->title;
}
/**
* #return mixed
*/
public function getUrl()
{
return $this->url;
}
}
I want to set it WAY back here, so the ajax that calls this route has access to it.. (look for #historyApiTitle in this code, etc..)
<?php
namespace Blah\Bundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller,
Symfony\Component\HttpFoundation\JsonResponse,
Sensio\Bundle\FrameworkExtraBundle\Configuration\Method,
Sensio\Bundle\FrameworkExtraBundle\Configuration\Route,
Sensio\Bundle\FrameworkExtraBundle\Configuration\Template,
Blah\Bundle\Entity\Test,
Doctrine\ORM\Query; //for hydration
class StuffController
{
/**
* #Route("/some/route/name/{test}", name="some_route_name", options={"expose"=true})
* #param $test
* #return mixed
* #historyApiTitle('This is the get something page')
* #historyApiUrl('/get_something')
*/
public function getSomethingAction($test)
{
$em = $this->getDoctrine()->getManager();
$dql = "
SELECT s
FROM BlahBundle:Stuff s
WHERE s.test = :test";
$query = $em->createQuery($dql);
$query->setParameter('test', $test);
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate($query,
$this->get('request')->query->get('page', 1), 1000);
return $this->render('BlahBundle:Stuff:get_something.html.twig', array('pagination' => $pagination));
}
}
Q So looking at these TWO code examples, how do I connect the dots between the two to get this to work?
Yes you can annotations classes you can follow the following tutorial Creating Custom annotations Classes
Basic rules are the follows:
Your class should have the #Annotation -phpdoc comment
/**
* #Annotation
*/
class CustomAnnotation
{
public function __construct($options) {}
}
In Your Needed class just use it in standard way;
class Person
{
/**
* #CustomAnnotation("option")
*/
public function getName()
{
return 'some stuff';
}
}
You should looks at the AOPBundle, it allows you to do treatement from your personnals annotations. But I don't thinks trying to do annotations in the view is a good idea. You need to parse the javascript with php, and it sounds bad.

Symfony Newb Routing Issue

I have just started using Symfony and I am having a routing problem. Here is the routing fromt the controller:
/**
* #Route("/social/{name}/", name="_speed1")
* #Route("/social/drivers/")
* #Route("/social/drivers/{name}/", name="_driver")
* #Route("/social/", name="_speed")
* #Template()
*/
public function unlimitedAction()
{
If I go to speed/social/ or speed/social/bob or speed/social/drivers/ or speed/social/drivers/bob all of those pages render with no problem. However I need the name being passed in so I changed
public function unlimitedAction()
{
to
public function unlimitedAction($name)
{
If I go to speed/social/drivers/ or speed/social/drivers/bob it returns fine. However, if I go to speed/social/ then I get the following error:
Controller "MyBundle\Controller\DefaultController::unlimitedAction()"
requires that you provide a value for the "$name" argument (because there is
no default value or because there is a non optional argument after this one).
I can't understand why it works for one route but not the other.
So my question is, how can I acheive my routing so that I can go to:
speed/social/
speed/social/drivers/
speed/social/drivers/bob
And be able to pass the variable to the action without error.
Thanks!
To answer your question: you have to provide a default value for name parameter, for each route without the {name} parameter in the url. I can't test it right now and I can't remember the syntax when using annotations, but should be something like this:
/**
* #Route("/social/{name}/", name="_speed1", defaults={"name"=null})
* #Route("/social/drivers/{name}/", name="_driver", defaults={"name"=null})
* #Template()
*/
public function unlimitedAction($name)
{
}
This way you should be able to call /social/ and /social/foo as well as /social/drivers/ and /social/drivers/foo.
But, really, this is not the right way to go. Just define more actions, each binded to a single route:
/**
* #Route("/social", name="social_index")
* #Template()
*/
public function socialIndexAction() { } // /social
/**
* #Route("/social/{name}", name="social_show")
* #Template()
*/
public function socialShowAction($name) { } // /social/foo
As a general rule, each method (each action) should be focused to do just one thing and should be as short as possible. Use services and make your controllers do what they are supposed to do: understand user input, call services and show views.

Redirect using #Route annotations

Is there a way to redirect using annotations?
/**
* Delete user
*
*
* #Route("/deleteUser/{user_id}", name="delete_user_from_id")
* #Template()
*/
public function deleteUserAction($user_id)
{
//....
return $this->redirect($this->generateUrl('acme_demo_homepage'));
}
Can we use annotations instead to avoid this extra line? Something like #redirect?
Nope, it is not possible.
Have a look into FrameworkExtraBundle. A redirect must be triggered in a controller (often after some logic ;) )

Resources