symfony2: trying to pass parameters on the URL - symfony

I have this routing.yml inside a bundle:
project_backend_update_item:
path: /update-item/{id}
defaults: { _controller: ProjectBackendBundle:Default:updateItem }
and this inside my controller:
public function updateItemAction(Request $request)
{
$repository = $this->getDoctrine()
->getRepository('ProjectFrontendBundle:Item');
var_dump($request->query->get('id'));
And when I request: "app_dev.php/update-item/1" I get NULL. Why? I expected "1".

$request->query give you the $_GET parameters (for example with /update-item?id=5)
Your parameter 'id' is not passed with _GET, but with routing.
You must do :
public function updateItemAction($id)
{
var_dump($id);
}
Or
public function updateItemAction(Request $request, $id)
{
var_dump($id);
}

In Symfony, whenever you have a parameter in your route pattern, you may add a variable with the same name in your controller action.
In your case, you should do something like this :
public function updateItemAction($id, Request $request)
{
$repository = $this->getDoctrine()
->getRepository('ProjectFrontendBundle:Item');
var_dump($id);
}

Related

Symfony3 FOSRest cgetAction route not found

The project is based on Symfony 3.1 + FOSRest 2.0.
I have a controller with following methods:
...
public function cgetCategoryAction()
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('AppBundle:Category')->findAll();
if (!$entity) {
return [];
}
return $entity;
}
public function getCategoryAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('AppBundle:Category')->find($id);
if (!$entity) {
throw new NotFoundHttpException(sprintf('The resource \'%s\' was not found.', $id));
}
return $entity;
}
...
GET /api/categories/1 delivers the result, but GET /api/categories leads to 405 Route not found. Adding slash to the end doesn't solve the issue.
According to names convention cgetAction should deliver a collection of entities by GET / request. What I'm doing wrong?
Update
routing.yml:
app:
type: rest
prefix: /api
resource: "#AppBundle/Resources/config/api-routing.yml"
NelmioApiDocBundle:
resource: "#NelmioApiDocBundle/Resources/config/routing.yml"
prefix: /api/doc
routing-api.yml:
api_Category:
type: rest
resource: "#AppBundle/Controller/CategoryController.php"
name_prefix: api_
api_Product:
type: rest
resource: "#AppBundle/Controller/ProductController.php"
name_prefix: api_
I just encountered the same issue. looks like the action declared first in the controller gets overwritten by the second. ie. if cgetCategoryAction is declared first, the getCategoryAction replaces it. so if you swap their order, you'll get the opposite problem. Only one of the routes gets generated.
I resolved it through
implicit resource naming
add the implements ClassResourceInterface, and remove the 'Category' from the Action names - FOSRestBundle will work that bit out from the Controller name.
use FOS\RestBundle\Routing\ClassResourceInterface;
class CategoryController extends Controller implements ClassResourceInterface {
public function cgetAction() {...}
public function getAction($id) {...}
}
and you can check what the automatically generated routes look like from the console with the command:
php app/console router:debug
By the way, in your code:
if (!$entity) {
return [];
}
This won't work the way you expect. $entity is a results set. So you need to check the count:
if ( count($entity) == 0 ) {
return [];
}
The above checks if it is empty.

Get symfony route from controller action annotations

I have a controller action like this:
/**
* #Route("/post/delete/{id}", name="delete_post_modal")
*/
public function deleteAction(Post $post)
{
// ...
}
The annotation #Route tells symfony to execute method deleteAction when route matches delete_post_modal.
All of this works fine.
Question: Is there a way to reverse this functionality and get the route name from method name?
From the example above:
I have: PostController::deleteAction() (or self::deleteAction)
I need to get: delete_post_modal
thanks!
try this
update
$router = $this->container->get('router');
foreach ($router->getRouteCollection()->all() as $route => $params)
{
$defaults = $params->getDefaults();
if ( strpos($defaults['_controller'],'PostController::deleteAction') ) {
$myroute = $route;
break;
}
}

symfony2 routing parameters and request

I have this route
edit_project:
pattern: /edit/{id}
defaults: { _controller: CpjProjectsBundle:Project:edit }
requirements:
id: \d+
and this is the controller:
public function editAction(Request $request)
{
}
inside the controller I'm unable to receive id
$this->query->get('id'); //empty
if I change the method signature, it works:
public function editAction($id)
but I need the Request to handle the form, usualy in this way
$form->handleRequest($request);
any suggestion for a workaround?
many thanks
Your final URL looks like this: http://my.domain.com/edit/123 so there is no query part of URL (...?id=123). You should accept both the Request object and id as arguments of your function:
public function editAction(Request $request, $id)
{
var_dump($request, $id);
}
Using that url if you want access to the id you will pass it as another parameter to your function. But it seems like you may want to get the Project entity associated with that id. You can save a step in your controller and have symfony look it up for you by type hinting the $id variable like so:
<?php
namespace Cpj\ProjectsBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Cpj\ProjectsBundle\Entity\Project;
//Other use statements
class PrjectController extends Controller
{
public function editAction(Request $request, Project $id)
{
var_dump($id);
// This will be and instance of Cpj\ProjectsBundle\Entity\Project
//if you need the actual ID of it you can do the following
$realID = $id->getId()
}
}

Pass path parameters automatically

I'm building a site where the user can choose a country, state and city he wants.
Once he selects these parameters he goes to a page like this: en.example.com/spain/madrid/madrid/
The problem is, every time I want to build a new url, I must pass these 3 variables and I was wondering if I could do something to make them just like the _locale variable which symfony itself passes it to the parameters.
Thanks
After searching more I found this post: http://blog.viison.com/post/15619033835/symfony2-twig-extension-switch-locale-current-route
I just used the idea and made the changes I needed and this is the final code for my extension
<?php
namespace Comehoy\CoreBundle\Twig\Extension;
use Symfony\Bundle\FrameworkBundle\Routing\Router;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\HttpKernel;
class PathExtension extends \Twig_Extension
{
private $request;
private $router;
public function __construct(Router $router) {
$this->router = $router;
}
public function onKernelRequest(GetResponseEvent $event) {
if ($event->getRequestType() === HttpKernel::MASTER_REQUEST) {
$this->request = $event->getRequest();
}
}
public function getFunctions()
{
return array(
'l10n_path' => new \Twig_Function_Method($this, 'getPath')
);
}
public function getPath($name, $parameters = array())
{
$parameters = array_merge($parameters, [
'country' => $this->request->get('country'),
'state' => $this->request->get('state'),
'city' => $this->request->get('city'),
]);
return $this->generator->generate($name, $parameters, false);
}
public function getName()
{
return 'twig_my_path_extension';
}
}
And as for the configuration its the same as the post
services:
twig.localized_route_extension:
class: Acme\CoreBundle\Twig\PathExtension
tags:
- { name: twig.extension }
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
arguments: [#router]
And for the routes that I use country, state and the city I put them in the prefix to avoid repeating them in each route.
acme_core:
resource: "#AcmeCoreBundle/Controller/"
type: annotation
prefix: /{country}/{state}/{city}
Hope it helps someone else.

Symfony2 how to redirect to an action without hardcoding route name?

I've an abstract CRUDController extending Controller. In my newAction, os success, i'd like to redirect to showAction($slug) using redirect method:
return $this->redirect($this->generateUrl($route, $params));
But newAction is actually called in the subclass UserController, so i can't specify route name $route in my CRUDController.
class UserController extends CRUDController { }
abstract class CRUDController extends Controller
{
/** #Template */
public function showAction($slug) { }
/** #Template */
public function newAction(Request $request)
{
$model = $this->createModel();
$form = $this->createForm($this->createType(), $model);
if('GET' == $request->getMethod())
return array('form' => $form->createView());
$form->bindRequest($request);
if(!$form->isValid()) return array(
'errors' => $this->get('validator')->validate($model),
'form' => $form->createView()
);
$em = $this->getDoctrine()->getEntityManager();
$em->persist($model);
$em->flush();
// Success, redirect to showAction($slug)
}
}
An example of routes:
users_show:
pattern: /users/show/{slug}
defaults: { _controller: AcmeSecurityBundle:User:show }
requirements:
_method: GET
users_new:
pattern: /users/new
defaults: { _controller: AcmeSecurityBundle:User:new }
requirements:
_method: GET
users_create:
pattern: /users/new
defaults: { _controller: AcmeSecurityBundle:User:new }
requirements:
_method: POST
You can work with the whole concept of OO and have an interface method called getRouteName() in your abstract class:
abstract public function getRoute();
And then, on your concrete class, or subclass, UserController, you just override and implement that:
public function getRoute()
{
return 'whatever:Route:YouWant';
}
So when, on your abstract class, call the actual interface method, the OO will handle everything like magic:
public function newAction(Request $request)
{
...
return $this->redirect($this->generateUrl($this->getRouteName(), $params));
}
Maybe try that and let us know if does the job right.
I sometimes create pseudo-interface when I need to do actions like this in sf:
interface specialRedirect
{
public function specialRedirect($slug);
}
So then you could do something like this in your abstract class:
public function newAction(Request $request)
{
// ...
$this->specialRedirect('user', $slug);
}
public function specialRedirect($slug)
{
// If you don't want to do anything special, just act like normal
// Success, redirect to showAction($slug)
}
But then in UserController you extend/implement the interface to your liking:
public function specialRedirect($slug)
{
//here is where you can specify your route name.
$route = 'Foo_Bar';
return $this->redirect($this->generateUrl($route, $params));
}

Resources