More than one controller in one Bundle (Symfony) - php

Is it possible to do more than one controller in Symfony? And if yes, how can I do it? (Controller, routing etc)
Because now I have:
Directory "Controller" and inside "DefaultController.php" (It has 1000 lines of code like now.)
Directory Resources->Config->Routing.yml (Here I have all routings).
Directory Resources->views->Default (In this dir I have all views).
Is it possible to make more controllers? For example one controller will have only indexAction(), other will have addclientAction() etc.

So you will have controlelrs at src/MyApp/SomeBundle/Controller/
class OneController extends Controller
{
public function indexAction()
{
....
}
}
class TwoController extends Controller
{
public function addclientAction()
{
....
}
}
you routing.yml should looks like
my_route_index:
pattern: /
defaults: { _controller: MyAppSomeBundle:One:index }
my_route_addclient:
pattern: /addclient/
defaults: { _controller: MyAppSomeBundle:Two:addclient }

Yes, of course it is possible.
You can see an example here:

Related

Controller class not found for Symfony 3 route

I am getting the error
`The _controller value "AppBundle:GasSupplier:overview" maps to a
"AppBundle\Controller\GasSupplierController" class, but this class was not found. Create
this class or check the spelling of the class and its namespace.`
But as far as I can tell, the class does exist, with the correct namespace, in the correct directory. My app/config/routing.yaml is
AppBundle:
resource: "#AppBundle/Resources/config/routing.yml"
and the contents of my src/AppBundle/Resources/config/routing.yaml is
homepage:
path: /
defaults: { _controller: AppBundle:Default:index }
gassupplieroverview:
path: /gassuppliers
defaults: { _controller: AppBundle:GasSupplier:overview }
and the contents of my src/AppBundle/Controller/GasSupplier.php is
<?php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class GasSupplierController extends Controller
{
public function overviewAction(Request $request)
{
$gas_suppliers = $this->getDoctrine()->getEntityManager()->getRepository('AppBundle:GasSupplier')->findAll();
return $this->render('gas_supplier/overview.html.twig', ['gas_suppliers' => $gas_suppliers]);
}
}
All the other answers I have found about this, the answer was almost always a typo - I have checked and checked again and I don't think I have any typos...
Using Symfony 3.
Typo: GasSupplierController in the class name, vs GasSupplier.php as the filename.
Wherever it says GasSupplier (including the filename), should be renamed/changed-to GasSupplierController, to match the class name.

Check routing in symfony2

I want to learn symfony and I start to create a small application. I have a question related to the routes. So, I have in my project the routes :
/admin/homepage, /admin/news, admin/galery
No if I write in url /admin, this route doesn't exist and I get as error No route found for "GET /admin/". Exist a way to check if route doesn't exist and redirect to another route for example ? Thx in advance and sorry for my english
My routes :
news_all:
path: /news/all/{page}
defaults: { _controller: AppAdminBundle:News:all, page: 1 }
requirements:
page: \d+
_method: GET|POST
news_add:
path: /news/add
defaults: { _controller: AppAdminBundle:News:add }
In your case the best solution would be to override default ExceptionController and add custom logic there, e.g. redirection to other page - according to the docs: http://symfony.com/doc/current/cookbook/controller/error_pages.html#overriding-the-default-exceptioncontroller
# app/config/config.yml
twig:
exception_controller: AppBundle:Exception:showException
Note:
Instead of creating a new exception controller from scratch you can, of course, also extend the default ExceptionController. In that case, you might want to override one or both of the showAction() and findTemplate() methods. The latter one locates the template to be used.
Symfony's good practise are to set rout in your controller, using annotations
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
and
/**
* #Route("/news/add", name="news_add")
*/
public function addAction()
{
// ...
}
Also, with annotation, you can set rout for a whole controller.
Which is, in your case, what you're looking for.
/**
* #Route("/admin")
*/
class NewsController extends Controller
{
/**
* #Route("/news/add", name="news_add")
*/
public function addAction()
{
// ...
}
}
Also, I advice you to take a look at #Template annotation.
Else, to answer your question, I think you can make a custom twig function (check this link for more information). Function that checks is the given name a valid route:
function routeExists($name)
{
// I assume that you have a link to the container in your twig extension class
$router = $this->container->get('router');
return (null === $router->getRouteCollection()->get($name)) ? false : true;
}

How to route to two different 'Action' functions in same controller using annotation - Symfony2

Recently I shifted from Symfony 2.4 to Symfony 2.7
So I was following the new docs. Now say I have 2 action functions in same controller.
public function indexAction() {}
public function changeRateAction()
Previously I would have route them using routing.yml
change_interest_rate_label:
path: /change_interest_rate
defaults: { _controller: appBundle:appInterestRateChange:index }
change_interest_rate_action_label:
path: /change_interest_rate_and_archived_action
defaults: { _controller: appBundle:appInterestRateChange:changeRate }
Now in 2.7 docs, annotations are encourages. Inside controller file
/**
* #Route("/home", name="homepage")
*/
This will fire the action method contains in the controller file. But how can I write annotations for 2 methods for different urls included in the same controller file ?
That means I have indexAction & changeRateAction in same controller file. I want to route url /home with index function and /change with changeRate function. How to do this using annotations ? I know how to do this using routing.yml
You use the annotation routing on a method, not a controller, really.
You just specify the route before every method. In your case it would be something like this:
/**
* #Route("/home", name="homepage")
*/
public function indexAction() {
...
}
/**
* #Route("/change", name="changerate")
*/
public function changeRateAction() {
...
}
Be sure to read more about routing in the documentation: http://symfony.com/doc/current/book/routing.html

FOSUserBundle override template

i'm very new to symfony and i'm stuck on an error.
I already searched for this over and over again but i didn't find any fix:
I installed the FOSUserBundle and i want to override layout.html.twig template to be my homepage of the website. I created a new bundle, made it a child of FOSUserBundle :
namespace Emag\UserBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class UserBundle extends Bundle {
public function getParent(){
return 'FOSUserBundle';
}
}
I made a new file in the src/Emag/UserBundle/Resources/views/layout.html.twig and a new controller
class HomeController extends Controller {
public function homeAction()
{
return $this->render('UserBundle\layout.html.twig');
}
}
but i get this error:
Unable to find template "UserBundle\layout.html.twig".
here's also my routing.yml file:
emag_magazine_homepage:
path: /emag
defaults: { _controller: UserBundle:Home:home }
You should use the correct namespace; EMAGUserBundle not UserBundle inside your routing file and in the homeAction to get your code well organised,
and then you have to change :
return $this->render('EmagUserBundle\layout.html.twig');
to
return $this->render('EmagUserBundle:layout.html.twig');

Setting $this->something in class body, can't use constructor dependency injection

I have a Symfony controller that uses a data_provider service. I can't figure out how to initialise this service.
I tried:
class DefaultController extends Controller {
public $dataProvider=$this->get('data_provider');
That causes an error, can't use constructor in a Controller so that leaves me with:
public $dataProvider=false;
public function someAction(){
$this->dataProvider=$this->get('data_provider');
...
public function anotherAction(){
$this->dataProvider=$this->get('data_provider');
...
So I have to set it every time in the controller action function. Is there an easy way to initialise the dataProvider when the controller is created?
The service is only for this bundle so it's defined in Symfony/src/mmt/myBundle/Resources/config/services.yml and that file is loaded by Symfony/src/mmt/myBundle/DependencyInjection/myExtension.php. Not sure if that makes a difference but I would prefer something that doesn't need changes to files outside the bundle.
Using symfony 2.3.4
[update]
After a seemingly endless list of instructions that cover less than half of what you need to do to get it working I got the injection part to work. Thanks to everyone giving me excellent advice.
My service is part of my bundle and don't want to change config files outside the bundle to load it. To make sure that Symfony/src/mmt/mrBundle/Resources/config/services.yml gets loaded you need a file called Symfony/src/mmt/mrBundle/DependencyInjection/mmtmrExtension.php (no, don't use just any name for the php file it's related to your application and bundle name).
What is in that file is explained here. I didn't need to do anything there because it was created when I created the bundle and have it create most of the files. (creating a bundle is in the standard documentation)
2.
Added a data_provider service in the services.yml file: (read standard documentation about setting up your db with doctrine)
data_provider:
class: mmt\mrBundle\Services\dataProvider
arguments: [ #doctrine.orm.entity_manager ]
Content of: Symfony/src/mmt/mrBundle/Services/dataProvider.php
<?php
namespace mmt\mrBundle\Services;
class dataProvider
{
protected $em;
public function __construct($em){
$this->em = $em;
}
public function getItem($id){
$item = $this->em->getRepository('mmtmrBundle:Item')
->find($id);
return $item;
}
public function saveItem($item){
$this->em->persist($item);
$this->em->flush();
}
}
?>
Now that I have the service I can use it in the controller like so:
$this->get("data_provider")->getItem(22);
But I would like my DefaultController have a $this->dataProvider when DefaultController is created. Preferably one depending on dev, prod, and test.
In comes dependency injection. Add the following to Symfony/src/mmt/mrBundle/Resources/config/services.yml
mmt.mr.DefaultController:
class: mmt\mrBundle\Controller\DefaultController
arguments: [#data_provider]
calls:
- [ "setContainer", [ #service_container ] ]
Now use the mmt.mr.DefaultController:indexAction (don't use mmtmrBundle:Default:index) in your routes:
/var/www/html/Symfony/src/mmt/mrBundle/Resources/config/routing.yml
mmtmr_homepage:
path: /{id}
requirements:
id: \d+
defaults: { _controller: mmt.mr.DefaultController:indexAction, id: false }
In Symfony/src/mmt/mrBundle/Controller/DefaultController.php should look like this:
<?php
namespace mmt\mrBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Session\Session;
class DefaultController extends Controller {
public $dataProvider;
public function __construct($data_provider){
$this->dataProvider = $data_provider;
}
public function indexAction($id) {
$item=$this->dataProvider->getItem($id);
return $this->render('mmtmrBundle:Default:index.html.twig',
array('item' => $item));
}
}
?>
I think that's it, of something is missing please let me know. Congrats; you now know how to inject dependency (If you didn't already). The bad news is that by the time you read this it's probably out of date and you have to go to the Symfony site. Documentation is good there but didn't mention any of the things that broke it for me.
You should inject it in the controller using Depency Injection.
Based on the classes in your question you can do the following:
Symfony/src/mmt/mrBundle/Resources/config/services.yml
mmt.mr.DefaultController:
class: mmt\mrBundle\Controller\DefaultController
calls:
- [ setContainer, [ #service_container ] ]
- [ setDataProvider, [ #data_provider ] ]
Symfony/src/mmt/mrBundle/Controller/DefaultController.php
public function setDataProvider($provider){
if($this->dataProvider===false){
$this->dataProvider=$provider;
}
}
Make sure you use the service name and then the action in your router, for example:
Symfony/src/mmt/mrBundle/Resources/config/routing.yml
mmtmr_homepage:
path: /{id}
requirements:
id: \d+
defaults: { _controller: mmt.mr.DefaultController:indexAction, id: false }
mmt.mr.DefaultController is the name used in your services.yml and :indexAction is the function called indexAction in your DefaultController.php
I recommend to use a method that returns the service.
Something like:
public function getDataProvider()
{
return $this->get('data_provider');
}
And create a 'AdvancedController' that extends the Symfony2 Controller, put this method in it, and let all your controllers extend it.
In the AdvancedController you can put all your global methods that you use in controllers, it's really comfortable.
After use the depedency injection, $data_provider must be correctly given to your controller so why do you not use it in:
public function dataInit($data_provider){
$logger = $this->get('logger');
$logger->notice('from dataInit so this works');
}
maybe it's must be:
public function dataInit($data_provider){
$logger = $data_provider->getLogger();
$logger->notice('from dataInit so this works');
}
If no, please paste you dataProvider class

Categories