I'm facing a problem that I don't understand. I simply create a Controller with make:controller and everything works, but when I want to create a new method, my route annotation doesn't work (whereas the default one works normally). T
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
class DefaultController extends AbstractController
{
/**
* #Route("/default", name="default")
*/
public function index()
{
return $this->render('default/index.html.twig', [
'controller_name' => 'DefaultController',
]);
}
/**
* #Route("/", name="home")
*/
public function home() {
return $this->render('default/home.html.twig');
}
}
The / route redirects me to the default page of Symfony and any other name for the route returns the No route found error.
Also, PHPStorm tells me that Import' Symfony\Component\Routing\Annotation\Route is never used when you can see that they are there...
How to solve this ? Thanks !
Check your config/routing.yaml
Related
Impossible to load Stripe classe in my Symfony controller see :
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Stripe\Stripe;
class PointsController extends Controller
{
/**
*
* #Route("/points/buy", name="points_buy")
*/
public function buyAction(Request $request)
{
\Stripe\Stripe::setApiKey('sk_test_');
return $this->render('points/buy.html.twig', [
]);
}
}
Stripe added with composer in vendor directory
I tried Stripe::setApiKey('sk_test_') but same error ...
Any idea?
If you're importing Stripe\Stripe in your use statement then I don't think you need the fully qualified class name.
use Stripe\Stripe;
//// etc.
/**
*
* #Route("/points/buy", name="points_buy")
*/
public function buyAction(Request $request)
{
Stripe::setApiKey($secretKey);
}
Here's a Stripe service I created for one of my projects.
I have written a series of working controllers within the directory /src/AppBundle/Controller/Admin, such as DoctorAdminController, PatientAdminController, etc. The top of these controllers look like so:
<?php
namespace AppBundle\Controller\Admin;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
/**
* #Route("/admin")
*/
class DoctorAdminController extends Controller
{
/**
* #Route("/doctor", name="admin_doctor_list")
*/
public function indexAction()
{
// stuff
}
}
I can navigate to each page exactly as expected:
http://example.com/admin/doctor
I would like to have a page sitting at http://example.com/admin, but for the life of me can't get get a working route. I have this as the controller for the index page:
<?php
namespace AppBundle\Controller\Admin;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
/**
* #Route("/admin")
*/
class DefaultAdminController extends Controller
{
/*
* #Route("/", name="default_admin")
*/
public function indexAction()
{
$this->render('admin/admin_base.html.twig');
}
}
I have my routing.yml file set to annotations:
app:
resource: "#AppBundle/Controller/"
type: annotation
When I try to access http://example.com/admin, I get a "No route found for GET /admin" error. Consistent with this, when I use ./bin/console debug:router there is no route listed for /admin, but I do see routes for all others (/admin/doctor, etc.)
What am I missing?
This is because you have your routing annotation before the class. You shouldn't do that, but instead put it in front of your actions only, like so:
<?php
namespace AppBundle\Controller\Admin;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultAdminController extends Controller
{
/*
* #Route("/admin", name="admin")
*/
public function indexAction()
{
$this->render('admin/admin_base.html.twig');
}
}
I also show this with the "name" using "admin" as well, so you can also specify "admin" in Twig and redirects using the same name; this is so it will make sense to you. Same thing for your DoctorAdminController, remove the routing annotation before the class.
That should fix the problems.
I want to create event listener that add some results of db query to all symfony actions
for example:
class BlogController extends Controller
{
/**
* #Route("/blog/")
* #Template()
*/
public function indexAction()
{
....
return array(
'entries' => $posts
);
}
}
This controller is passing entries variable to the view, I want to create listener that take the returned value of all actions and inject another index to the returned array to be (for example)
array(
'entries' => $posts,
'categories' => $categories
);
so I can call the $categories var from any where in my application views
I hope my question is clear to you guys. Thanks in advance.
You should consider creating a global variable or twig extension to make categories available in your templates, you can't do that by using events (since the template is parsed inside the controller, not before/after it)
This approach, although valid and commonly used in some frameworks, is not very common in Symfony as it suits more MVC than HMVC architecture.
I would suggest you a different one with the same result:
Instead of adding parameter to every controller return, render another controller which returns just a subview of what you're trying to show. Simple example:
// article/index.html.twig
<div class="category-bar">{{ render(controller('MyVendorMyBundle:CategoryController:bar')) }}</div>
<div class="article-list">
{% for article in articles %>
{# Print article here #}
{% endfor %}
</div>
// CategoryController
class CategoryController extends Controller
{
/**
* #Template
*/
public function barAction()
{
return ['categories' => $this->fetchCategoriesSomehow()];
}
}
So when you render your article list action, twig will fire a subrequest to render categories bar above it.
Furthermore, if you don't like making subrequests, nothing stops you from creating a twig extension service which would fetch categories and render template for you.
In most cases I would go with #Wouter J's suggestion and create a twig extension or a global variable.
However, what you want to do is actually possible (regardless if that's the right solution or not).
The #Template annotation has a vars attribute, which lets you to specify which atttributes from the request should be passed to the template:
/**
* #ParamConverter("post", class="SensioBlogBundle:Post")
* #Template("SensioBlogBundle:Post:show.html.twig", vars={"post"})
*/
public function showAction()
{
}
Note, that request attributes can be set by you:
$request->attributes->set('categories', []);
So, you could implement a listener which would set the categories attribute on the request and than configure the vars on the #Template annotation:
/**
* #Template("SensioBlogBundle:Post:show.html.twig", vars={"categories"})
*/
public function showAction(Post $post)
{
}
Have a look at the TemplateListener from the SensioFrameworkExtraBundle for more insight. The listener defines template vars on kernel.controller and uses them to render the view on kernel.view.
You could avoid defining vars on the annotation if your listener was registered after the TemplateListener::onController(). It would have to add categories to the _template_vars request attribute.
Use Twig extension to create function that will return list of available categories
<?php
class CategoriesExtension extends \Twig_Extension
{
public function getFunctions()
{
return [
new \Twig_SimpleFunction('getCategories', [$this, 'getCategoriesList'])
];
}
/**
* #return null|string
*/
public function getCategoriesList()
{
return CategoryQuery::create()->find();
}
/**
* Returns the name of the extension.
*
* #return string The extension name
*/
public function getName()
{
return 'list_categories';
}
}
You can pass parameter to function if You would like do some conditions on query.
The trick is to get the twig service in your listener and then use addGlobal to add your categories
namespace Cerad\Bundle\CoreBundle\EventListener;
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\HttpKernel\HttpKernel;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class MyEventListener extends ContainerAware implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
KernelEvents::CONTROLLER => array(
array('doCategories', -1100),
);
}
public function doCategories(FilterControllerEvent $eventx)
{
// Query your categories
$categories = array('cat1','cat2');
// Make them available to all twig templates
$twig = $this->container->get('twig');
$twig->addGlobal('categories',$categories);
}
# services.yml
cerad_core__my__event_listener:
class: '%cerad_core__my__event_listener__class%'
calls:
- [setContainer, ['#service_container']]
tags:
- { name: kernel.event_subscriber }
I am using the FOSRestBundle to create a REST application but since REST features is only a part, I am also using some of Symfony2 built-in automation tools to generate my CRUD code. Everything works fine but I am unable to correctly map the route and I will appreciate some insight and example on how to do this manually. I have read the manual route definition in the FOS manual stating to use the given annotations but how do I do this since the CRUD code created by Symfony2 uses a different annotation?
Here is an example:
class UserController extends Controller
{
/**
* Lists all User entities.
*
* #Route("/", name="user")
* #Method("GET")
* #Template()
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('CompanyWebServicesBundle:User')->findAll();
return array(
'entities' => $entities,
);
}
FOSRest manual gives the annotation for GET as
use FOS\RestBundle\Controller\Annotations\Get;
/**
* GET Route annotation.
* #Get("/likes/{type}/{typeId}")
*/
When I use the route as /index, it gives me an error and my route definition in config.yml is:
index:
type: rest
resource: Company\WebservicesBundle\Controller\UserController
How can I fix this problem?
If I were you, I would create separate bundles for your REST controllers and your generic CRUD controllers. This will make things easier to maintain (in my opinion). For example, create a AcmeMainBundle and a AcmeRestBundle, and then create a separate class to actually perform the actions that you will call from both bundles. Something like this:
// src/Acme/MainBundle/Crud/User.php (create this folder structure)
class User{
private $em;
public function __construct($em){
$this->em = $em;
}
public function getUser($id){
return $this->em->getRepository('AcmeMainBundle:User')->find($id);
}
}
Then:
// src/Acme/MainBundle/Controller/UserController.php
use Symfony\Component\HttpFoundation\Request;
use Acme\MainBundle\Crud\User;
class UserController extends Controller {
public function getAction($request){
$em = $this->getDoctrine()->getManager();
$getUser = new User($em);
$user = $getUser ->getUser($request->query->get('user_id'));
// return response
}
}
And then:
// src/Acme/RestBundle/Controller/UserController.php
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Routing\ClassResourceInterface;
use Symfony\Component\HttpFoundation\Request;
class UserController extends Controller implements ClassResourceInterface {
/**
* #Rest\View()
*/
public function getAction($id){
$em = $this->getDoctrine()->getManager();
$getUser = new User($em);
$user = $getUser ->getUser($id);
// return using the default format defined in config.yml
return array(
"success"=>'true',
"user" => $user
);
} // get_user [GET] /users/{id}
}
Please note that using the ClassResourceInterface means your method names will be used to generate the routes. see FOSRestBundle Docs for more info on that.
You can do something similar to this for all your CRUD, that way you keep your routes separate and maintainable, but still have a single code base to update.
Hi Have followed the instructions to enable the SensioFrameworkExtraBundle here: http://symfony.com/doc/2.1/bundles/SensioFrameworkExtraBundle/index.html
Thereafter I create the below controller:
namespace Acme\DemoBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class MyController
{
/**
* #Template
*/
public function indexAction()
{
}
}
If set up a route pointing to the indexAction on this controller and browse to it, I get the following error:
The controller must return a response (null given). Did you forget to add a return statement somewhere in your controller?
It looks like the SensioFrameworkExtraBundle is not actually enabled, but I cannot figure out why. I'm looking for advice.
The #Template annotation is working. As the error suggest, you must return something. If return array, it will be sent to template engine. Make sure the template exists.
public function indexAction()
{
...
$somedata = 'fill data';
return array('somedata' => $somedata);
}