I'm attempting to learn how to use the Symfony 2.3 framework. I thought it would be a good first exercise to modify Acme\DemoBundle\DemoController::helloaction() to provide a default name when none was entered.
This is the original:
/**
* #Route("/hello/{name}", name="_demo_hello")
* #Template()
*/
public function helloAction($name)
{
return array('name' => $name);
}
It works with urls like localhost/Symfony/web/demo/hello/SOMENAME and fails with urls like localhost/Symfony/web/demo/hello/SOMENAME/, localhost/Symfony/web/demo/hello and localhost/Symfony/web/demo/hello/
This is what I did:
/**
* #Route("/hello", name="_demo_hello", defaults={"name" = "World"})
* #Template()
*/
public function helloAction($name)
{
return array('name' => $name);
}
It works with localhost/Symfony/web/demo/hello and fails with localhost/Symfony/web/demo/hello/SOMENAME, localhost/Symfony/web/demo/hello/SOMENAME/ and localhost/Symfony/web/demo/hello/
How do I make the routing work with and without a name and with and without a trailing slash?
You can set a default value like this:
/**
* #Route("/hello/", defaults={"name" = "John"})
* #Route("/hello/{name}", name="_demo_hello")
* #Template()
*/
public function helloAction($name) { ... }
It's also important to know that you can have more than one route on the same action, so no need to duplicate actions.
See documentation: http://symfony.com/doc/2.2/book/controller.html And: #Route Documentation
I think your solution should also work if you append a / after your route /hello.
Related
I understand when allowing similarly accessible routes, that the order of the routes matter.
Where I'm confused is why when submitting a DELETE request to this route, does it match to the GET route, instead of ignoring it and trying the matched method one below it?
/**
* #Route("/{game}")
* #Method({"GET"})
*/
public function single(Request $request, GameSerializer $gameSerializer, Game $game) {
$out = $gameSerializer->bind($game);
return new JsonResponse($out);
}
/**
* #Route("/{game}")
* #Method({"DELETE"})
*/
public function remove(Request $request, Game $game) {
$em = $this->getDoctrine()->getManager();
$em->remove($game);
$em->flush();
return new JsonResponse([], 200);
}
Full disclosure
I understand why it matches the top most route based on strictly patterns
I dont understand why the access method is getting ignored when doing so
So, just to test, I adjusted to move the DELETE based route up above the GET route
/**
* #Route("/{game}")
* #Method({"DELETE"})
*/
public function remove(Request $request, Game $game) {
$em = $this->getDoctrine()->getManager();
$em->remove($game);
$em->flush();
return new JsonResponse([], 200);
}
/**
* #Route("/{game}")
* #Method({"GET"})
*/
public function single(Request $request, GameSerializer $gameSerializer, Game $game) {
$out = $gameSerializer->bind($game);
return new JsonResponse($out);
}
only.. for this to happen when I tried getting an existing non-test record by performing a basic operation of visiting the url in a browser (so, GET)
and oh boy, did it ever delete that record.
Why is the Access Method being ignored?
First of all, careful of which SensioFrameworkExtraBundle version you are using because the #Method annotation from SensioFrameworkExtraBundle has been removed in latest version. Instead, the Symfony #Route annotation defines a methods option to restrict the HTTP methods of the route:
*
* #Route("/show/{id}", methods={"GET","HEAD"})
*
But in your case, if you're using HTML forms and HTTP methods other than GET and POST, you'll need to include a _method parameter to fake the HTTP method.
See How to Change the Action and Method of a Form for more information.
I think you have to add route name and it must be unique.
Try with following way:
/**
* #Route("/{game}",name="api_remove")
* #Method({"DELETE"})
*/
public function remove(Request $request, Game $game) {
...
}
/**
* #Route("/{game}",name="single_remove")
* #Method({"GET"})
*/
public function single(Request $request, GameSerializer $gameSerializer, Game $game) {
...
}
Is it possible to set up a search for multiple entities, using the Florian Semm Solr Bundle in Symfony? I'm completely lost, have looked in the documentation of Solarium itself, but couldn't figure out how to set up a search for our Symfony project.
Here's what I did so far:
Solarium and the SolrBundle are both successfully installed.
I indexed 3 entities (for now) with the Solr annotations like that:
/**
* #Solr\Document(repository="UserBundle\Entity\User")
* #Solr\Document(index="*")
*/
class User {
/**
* #Solr\Id
*/
protected $id;
/**
* #Solr\Field(type="string")
*/
protected $firstName;
/**
* #Solr\Field(type="string")
*/
protected $lastName;
}
I set up a controller where I call the solr.client but that's basically how far I got. I can show you my code but it's throwing error messages, because I'm basically just trying around:
class SearchController extends Controller {
/**
* #Route("/search-result/", name="searchBundle")
*/
public function searchAction(Request $request) {
$client = $this->get('solr.client');
$query = $client->createSelect();
$query->setQuery($request->query->all());
// this executes the query and returns the result
$resultset = $client->execute($query);
return $this->render('SearchBundle::search.html.twig', array(
'resultset' => $resultset
));
}
}
How do I get the controller/the bundle to search within all the three bundles/indexed properties?
How do I structure the output?
Couldn't find any tutorials/example codes/guidelines for that specific bundle unfortunately.
What you probably want to do inside your controller is get a repository for the search;
$resultset = $this->get('solr.client')->getRepository('UserBundle:User')->findAll();
return $this->render('SearchBundle::search.html.twig', array(
'resultset' => $resultset
));
Source: https://github.com/floriansemm/SolrBundle#query-a-field-of-a-document
I have a controller which handles a GET request. I need to set requirement parameters for GET request, e.g.: 'http://localhost/site/main?id=10&sort=asc
My controller class
class IndexController extends Controller {
` /**
* #Route
* (
* "/site/main",
* name="main"
* )
*
* #Method("GET")
*/
public function mainAction(Request $request)
{
return new Response('', 200);
}
}
How could I do that?
UPD: I need to set requirement for URL parameters like
id: "\d+",
sort: "\w+"
Etc.
The same as symfony allows to do with POST request.
You can specify the requirements in the "#Route" annotation like this:
class IndexController extends Controller {
` /**
* #Route
* (
* "/site/main",
* name="main",
* requirements={
* "id": "\d+",
* "sort": "\w+"
* })
* )
*
* #Method("GET")
*/
public function mainAction(Request $request)
{
return new Response('', 200);
}
}
#Method is what you need http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/routing.html#route-method
If you try to use this route with POST, you will have 404
I couldn't understand your question well.
However, if what you need is to set up a filter mechanism for the GET method parameters as it is already available for the URL using route requirements, I think there is no ready to use tools for this in the Route component, as commented #Yoshi.
I had to do this kind of work myself and used this. I hope it helps you too
public function indexAction(Request $request)
{
// Parameter names used in the current request
$current_request_params=array_keys($request->query->all());
// $ALLOWED_INDEX_PARAMS should be declared as Class static property array and hold names of the query parameters you want to allow for this method/action
$unallowed_request_params=array_diff($current_request_params,PersonController::$ALLOWED_INDEX_PARAMS);
if (!empty($unallowed_request_params))
{
$result=array("error"=>sprintf("Unknown parameters: %s. PLease check the API documentation for more details.",implode($unallowed_request_params,", ")));
$jsonRsp=$this->get("serializer")->serialize($result,"json");
return new Response($jsonRsp,Response::HTTP_BAD_REQUEST,array("Content-Type"=>"application/json"));
}
// We are sure all parameters are correct, process the query job ..
}
I have a Symfony project that uses two different domains for two different countries. The site is a client login interface and is basically the same for both domains except for branding and minor name differences. That part is okay.
The two domains are defined in my parameters as us_domain and ca_domain, and in my routing.yml I have:
clients:
resource: #ClientsBundle/Resources/config/routing.yml
host: "clients.{domain}"
prefix: /
requirements:
domain: %us_domain%|%ca_domain%
defaults: { domain: "%us_domain%" }
In Twig, I have my menu using:
<li><span>Home</span></li>
The problem is that although the page will come up on either domain, the paths being generated always use the us_domain, apparently pulling it from defaults in my routing. (I can switch this to ca_domain and the paths do switch).
My question is, why isn't the current domain being detected and used? It seems like the default should be overridden by whatever domain is actually being used?
I'm running Nginx if that matters.
I somehow had missed that the routing variables weren't going to be auto-detected, but you had to pass them as part of your calls. I ended up solving this with a custom Twig filter.
Where I used my path, I passed the domain variable pulling from app.request.host, such as
<li><span>Home</span></li>
And then just wrote Twig extension that parsed/evaluated the host and passed back the domain
<?php
namespace AppBundle\Twig;
use JMS\DiExtraBundle\Annotation as DI;
/**
* Class TwigUrlExtension
* #DI\Service("twig.extension.url")
* #DI\Tag("twig.extension")
*
*/
class TwigUrlExtension extends \Twig_Extension
{
protected $ca_domain;
protected $us_domain;
/**
*
* #DI\InjectParams({
* "us_domain" = #DI\Inject("%us_domain%"),
* "ca_domain" = #DI\Inject("%ca_domain%")
* })
* #param $us_domain
* #param $ca_domain
*/
public function __construct($us_domain, $ca_domain)
{
$this->us_domain = $us_domain;
$this->ca_domain = $ca_domain;
}
/**
* {#inheritdoc}
*/
public function getFunctions() {
return array(
'domainFromHost' => new \Twig_Function_Method($this, 'toDomain')
);
}
/**
* #param string $string
* #return int
*/
public function toDomain ($string) {
if(stripos($string,$this->ca_domain) !== false)
return $this->ca_domain;
if(stripos($string,$this->us_domain) !== false)
return $this->us_domain;
return $string;
}
/**
* {#inheritdoc}
*/
public function getName() {
return 'twig_url_extension';
}
}
After reading documentation and looking for it with Google, I've to ask you.
I want to switch between 3 languages: ca_ES, es_ES and en_GB
So I did a controller like this:
/**
* #Route("/canviar-idioma/{locale}", name="change_lang")
* #Template()
*
* #return array
*/
public function canviarIdiomaAction($locale){
$request = $this->getRequest();
if ($locale == 'cat'){
$this->get('translator')->setLocale('ca_ES');
return new Response('ca');
} else if ($locale == 'es'){
$this->get('translator')->setLocale('es_ES');
return new Response('es');
} else if ($locale == 'eng'){
$this->get('session')->set('_locale', 'en_GB');
return new Response('en');
}
return new Response(null);
}
This controller is being called by ajax, when an user clicks a flag with the language. I receive the "ca" or "es" or "en" correctly, so controller is "working" somehow.
As you can see, I've tried using it by session or getting the translator. Both ways same results.
But, I made this controller to check if my locale really changed:
/**
* #Route("/quinlocaletinc", name="quinlocaletinc")
* #Template()
*
* #return array
*/
public function quinlocaletincAction(){
$request = $this->getRequest();
return new Response($request->getLocale());
}
And this locale ALWAYS gives "ca_ES" as it's the one defined in my parameters file:
locale: ca_ES
And my config.yml:
default_locale: %locale%
translator: { fallback: %locale% }
You need to use the "special" _locale variable in the route, Symfony will then properly set the locale for your application.
You can read more about this in the documentation
Your route should look like this:
/**
* #Route("/canviar-idioma/{_locale}", requirements={"_locale" = "ca_ES|es_ES|en_GB"}, name="change_lang")
* #Template()
*
* #return array
*/
public function canviarIdiomaAction() {
$locale = $request->getLocale();
// ...
Your second route will also need the parameter
/**
* #Route("/quinlocaletinc/{_locale}", name="quinlocaletinc")
* #Template()
*
* #return array
*/
public function quinlocaletincAction() {
$request = $this->getRequest();
return new Response($request->getLocale());
}
A good convention is to prefix all routes with the locale rather than postfix
/**
* #Route("/{_locale}/quinlocaletinc", name="quinlocaletinc")
* #Template()
*
* #return array
*/
public function quinlocaletincAction() {
$request = $this->getRequest();
return new Response($request->getLocale());
}
By using the _locale variable in Symfony, everything just "works" (i.e. if you visit /ca_ES/page all links on that page will include the right url).
Also when using the _locale parameter in your route, $this->get('translator')->setLocale('ca_ES'); is un-necessary as it will happen automatically.
Your annotation routing and Controller argument should be {_locale} and $_locale.
/**
* #Route("/canviar-idioma/{_locale}", name="change_lang")
* #Template()
*
* #return array
*/
public function canviarIdiomaAction($_locale)
{
// ...