Symfony2: Moving Away From the Base Controller

When you start learning Symfony2 using the book, you are introduced to the base controller. The base controller has some helpful methods to make common tasks easier. After a while though you may want to move away from extending this controller, in fact, it is the advised best practice for controllers in shared bundles. When you do, you will no longer have access to these useful methods, in this post I am going to look at how to accomplish the same tasks without them.

I have created a simple controller for some basic form interaction with a model which uses the base controllers methods. I am going to go through how to change this to a service which does not extend the base controller. The starting point looks like this:

<?php
 
namespace LimeThinking\SpringBundle\Controller;
 
use LimeThinking\SpringBundle\Entity\Testimonial;
use LimeThinking\SpringBundle\Form\TestimonialType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
 
class TestimonialController extends Controller
{
 
    public function viewAction($id)
    {
        $testimonial = $this->getTestimonial($id);
 
        return $this->render(
             'LimeThinkingSpringBundle:Testimonial:show.html.twig',
             array(
                'testimonial' => $testimonial,
             )
        );
    }
 
    public function addAction()
    {
        $testimonial = new Testimonial();
        return $this->renderForm($testimonial);
    }
 
    public function editAction($id)
    {
        $testimonial = $this->getTestimonial($id);
        return $this->renderForm($testimonial);
    }
 
    protected function renderForm($testimonial)
    {
        $form = $this->createForm(new TestimonialType(), $testimonial);
        $request = $this->get('request');
        if ($request->getMethod() == 'POST')
        {
            $form->bindRequest($request);
 
            if ($form->isValid())
            {
                $em = $this->getDoctrine()->getEntityManager();
                $em->persist($testimonial);
                $em->flush();
                $uri = $this->generateUrl(
                    'admin_testimonial', 
                    array('id' => $testimonial->getId())
                );
                return $this->redirect($uri);
            }
        }
 
        return $this->render(
             'LimeThinkingSpringBundle:Testimonial:form.html.twig',
             array(
                'form' => $form->createView(),
             )
        );
    }
 
    protected function getTestimonial($id)
    {
        $testimonial = $this->getDoctrine()
            ->getEntityManager()
            ->getRepository('LimeThinkingSpringBundle:Testimonial')
            ->find($id);
 
        if (!$testimonial) {
            throw $this->createNotFoundException('Testimonial not found.');
        }
 
        return $testimonial;
    }
 
 
}

It uses a form type object LimeThinking\SpringBundle\Form\TestimonialType for the form which looks like this:

<?php
namespace LimeThinking\SpringBundle\Form;
 
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
 
class TestimonialType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder->add('name')
        ->add('intro')
        ->add('quotation');
    }
}

The controller as it stands has actions for displaying, adding and editing testimonials. I have extracted the common tasks, retrieving a testimonial from the database and rendering the form, into their own methods. It is routed to like this:

admin_testimonial_add:
    pattern:  /admin/testimonials/add
    defaults: { _controller: LimeThinkingSpringBundle:Testimonial:add}
 
admin_testimonial:
    pattern:  /admin/testimonials/{id}
    defaults: { _controller: LimeThinkingSpringBundle:Testimonial:view}
 
admin_testimonial_edit:
    pattern:  /admin/testimonials/{id}/edit
    defaults: { _controller: LimeThinkingSpringBundle:Testimonial:edit}

Removing Helper Methods

To start with I am going to change the helper methods to use services still retrieved using the get() helper method which is a short cut for getting services from the container. After this is complete we can convert to a service and inject the required services instead.

The methods we need to replace are:

  • getDoctrine()
  • generateUrl()
  • render()
  • createForm()
  • redirect()
  • createNotFoundException()
  • get()

Once all these are gone we can stop extending Symfony\Bundle\FrameworkBundle\Controller\Controller. To start we will actually introduce more use of get() as this allows us to access services from the Dependency Injection Container (aka Service Container).

getDoctrine()

The easiest to remove is getDoctrine():

$em = $this->getDoctrine()->getEntityManager();

becomes

$em = $this->get('doctrine')->getEntityManager();

generateUrl()

Next up generateUrl():

$uri = $this->generateUrl('admin_testimonial', array('id' => $testimonial->getId()));

becomes

$uri = $this->get('router')->generate(
    'admin_testimonial', 
    array('id' => $testimonial->getId())
);

render()

return $this->render(
    'LimeThinkingSpringBundle:Testimonial:form.html.twig',
        array(
            'form' => $form->createView(),
        )
    );

becomes:

return $this->get('templating')->renderResponse(
    'LimeThinkingSpringBundle:Testimonial:form.html.twig',
        array(
            'form' => $form->createView(),
        )
    );

createForm()

$form = $this->createForm(new TestimonialType(), $testimonial);

becomes:

$form = $this->get('form.factory')->create(new TestimonialType(), $testimonial);

redirect()

Now for a couple that are slightly different:

return $this->redirect($uri);

becomes:

return new RedirectResponse($uri);

We also need to add:

use Symfony\Component\HttpFoundation\RedirectResponse;

to the use statements at the top of the file.

createNotFoundException()

throw $this->createNotFoundException('Testimonial not found.');

becomes:

throw new NotFoundHttpException('Testimonial not found.');

with:

use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

added to the use statements.

get()

    $this->get('service_name');

is just a shortcut for

    $this->container->get('service_name');

So we can replace all the get() calls throughout.

Now that all the methods from the base controller have been removed we can change to extending ContainerAware instead. doing so means that the container is available to the controller. So the class now looks like this:

<?php
 
namespace LimeThinking\SpringBundle\Controller;
 
use LimeThinking\SpringBundle\Entity\Testimonial;
use LimeThinking\SpringBundle\Form\TestimonialType;
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
class TestimonialController extends ContainerAware
{
 
    public function viewAction($id)
    {
        $testimonial = $this->getTestimonial($id);
 
        return $this->container->get('templating')->renderResponse(
             'LimeThinkingSpringBundle:Testimonial:show.html.twig',
             array(
                'testimonial' => $testimonial,
             )
        );
    }
 
    public function addAction()
    {
        $testimonial = new Testimonial();
        return $this->renderForm($testimonial);
    }
 
    public function editAction($id)
    {
        $testimonial = $this->getTestimonial($id);
        return $this->renderForm($testimonial);
    }
 
    protected function renderForm($testimonial)
    {
        $form = $this->container->get('form.factory')->create(
            new TestimonialType(), 
            $testimonial
        );
        $request = $this->container->get('request');
        if ($request->getMethod() == 'POST')
        {
            $form->bindRequest($request);
 
            if ($form->isValid())
            {
                $em = $this->container->get('doctrine')->getEntityManager();
                $em->persist($testimonial);
                $em->flush();
                $uri = $this->container->get('router')->generate(
                    'admin_testimonial', 
                    array('id' => $testimonial->getId())
                );
                return new RedirectResponse($uri);
            }
        }
 
        return $this->container->get('templating')->renderResponse(
             'LimeThinkingSpringBundle:Testimonial:form.html.twig',
             array(
                'form' => $form->createView(),
             )
        );
    }
 
    protected function getTestimonial($id)
    {
        $testimonial = $this->container->get('doctrine')
            ->getEntityManager()
            ->getRepository('LimeThinkingSpringBundle:Testimonial')
            ->find($id);
 
        if (!$testimonial) {
            throw new NotFoundHttpException('Testimonial not found.');
        }
 
        return $testimonial;
    }
 
 
}

Defining the Controller as a Service

We can also now move to defining our controller as a service. See this cookbook recipe and my post Symfony2: Controller as Service for details on how to do this and how to set up an Dependency Injection extension to load a service definition for a bundle. In this case we would change to routing to the controller like this:

admin_testimonial_add:
    pattern:  /admin/testimonials/add
    defaults: { _controller: lime_thinking_spring.testimonial:addAction}
 
admin_testimonial:
    pattern:  /admin/testimonials/{id}
    defaults: { _controller: lime_thinking_spring.testimonial:viewAction}
 
admin_testimonial_edit:
    pattern:  /admin/testimonials/{id}/edit
    defaults: { _controller: lime_thinking_spring.testimonial:editAction}

with a services XML config like this:

<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
 
    <services>
 
        <service id="lime_thinking_spring.testimonial"
        class="LimeThinking\SpringBundle\Controller\TestimonialController" 
                 >
                 <call method="setContainer">
                      <argument type="service" id="service_container" />
                 </call>
        </service>       
 
    </services>
 
</container>

Note that the container is explicitly injected into the service using the method defined in ContainerAware as this is no longer automatically done.

Injecting Services

Now for the more controversial bit. In my opinion injecting the container in this way is not the correct way to use it. I have written about this in these posts: When Dependency Injection goes Wrong and In Defence of Dependency Injection Containers. This is not the opinion of everyone and not an official best practice, it is just my recommendation.

I have also looked at how to inject dependencies in Symfony2 in these posts: Symfony2: Dependency Injection Types and Symfony2: Injecting Dependencies Step by Step. So I am not going to go over how to do this in detail again this time, the end result of removing the services is that the class looks like this:

<?php
 
namespace LimeThinking\SpringBundle\Controller;
 
use LimeThinking\SpringBundle\Entity\Testimonial;
use LimeThinking\SpringBundle\Form\TestimonialType;
use Symfony\Bundle\DoctrineBundle\Registry;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\RouterInterface;
 
class Testimonial
{
 
    protected $doctrine;
    protected $formFactory;
    protected $templating;
    protected $request;
    protected $router;
 
    public function __construct(Registry $doctrine,
                                FormFactoryInterface $formFactory,
                                EngineInterface $templating,
                                Request $request,
                                RouterInterface $router)
    {
        $this->doctrine    = $doctrine;
        $this->formFactory = $formFactory;
        $this->templating  = $templating;
        $this->request     = $request;
        $this->router      = $router;
    }
 
    public function viewAction($id)
    {
        $testimonial = $this->getTestimonial($id);
 
        return $this->templating->renderResponse(
             'LimeThinkingSpringBundle:Testimonial:show.html.twig',
             array(
                'testimonial' => $testimonial,
             )
        );
    }
 
    public function addAction()
    {
        $testimonial = new Testimonial();
        return $this->renderForm($testimonial);
    }
 
    public function editAction($id)
    {
        $testimonial = $this->getTestimonial($id);
        return $this->renderForm($testimonial);
    }
 
    protected function renderForm($testimonial)
    {
        $form = $this->formFactory->create(new TestimonialType(), $testimonial);
        if ($this->request->getMethod() == 'POST')
        {
            $form->bindRequest($this->request);
 
            if ($form->isValid())
            {
                $em = $this->doctrine->getEntityManager();
                $em->persist($testimonial);
                $em->flush();
                $uri = $this->router->generate(
                    'admin_testimonial', 
                    array('id' => $testimonial->getId())
                );
                return new RedirectResponse($uri);
            }
        }
 
        return $this->templating->renderResponse(
             'LimeThinkingSpringBundle:Testimonial:form.html.twig',
             array(
                'form' => $form->createView(),
             )
        );
    }
 
    protected function getTestimonial($id)
    {
        $testimonial = $this->doctrine
            ->getEntityManager()
            ->getRepository('LimeThinkingSpringBundle:Testimonial')
            ->find($id);
 
        if (!$testimonial) {
            throw new NotFoundHttpException('Testimonial not found.');
        }
 
        return $testimonial;
    }
 
 
}

and the services XML like this:

<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
 
    <services>
 
        <service id="lime_thinking_spring.testimonial"
        class="LimeThinking\SpringBundle\Controller\TestimonialController" 
                 >
                     <argument type="service" id="doctrine" />
	             <argument type="service" id="form.factory" />
	             <argument type="service" id="templating" />
	             <argument type="service" id="lime_thinking_spring.request" />
	             <argument type="service" id="router" />
        </service>       
 
        <service id="lime_thinking_spring.request" 
                 class="Symfony\Component\HttpFoundation\Request"/>
 
    </services>
 
</container>

Edit: As per Stof’s comment the predefined request service should be used. As this is scoped to request the controller will also need to be scoped to request to avoid scope widening. The services.xml should then look like this:

<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
 
    <services>
 
        <service id="lime_thinking_spring.testimonial" scope="request"
        class="LimeThinking\SpringBundle\Controller\TestimonialController" 
                 >
                     <argument type="service" id="doctrine" />
	             <argument type="service" id="form.factory" />
	             <argument type="service" id="templating" />
	             <argument type="service" id="request" />
	             <argument type="service" id="router" />
        </service>       
 
    </services>
 
</container>

There are still some objects being created using the new keyword. These are all effectively value objects though rather than services. That is to say that they hold specific values and would not be reusable in other classes in the same way that the other injected services are. The RedirectResponse, for example, is specific to the testimonial not being found and is not something we could inject using the container. There is an argument that this is still a dependency that should be removed. If you are that way inclined then a way to do it would be to instead inject Factory objects to create these objects on demand. The factory could then be mocked for testing or a different factory used if you wanted to change the object returned. If you do want to do this then at the moment you will have to write your own though.

You can leave a response, or trackback from your own site.

33 Responses to “Symfony2: Moving Away From the Base Controller”

  1. Stof says:

    you should inject the “request” service instead of creating a new instance of the Request class which will be decoupled from all uses of the request in the lifecycle of your app.

  2. GromNaN says:

    What is the problem of using the Controller class ?

    To replace getDoctrine(), its:
    $em = $this->container->get(‘doctrine’)->getEntityManager();

    Otherwise, you must copy and paste the function get() from the default Controller class…

    • John Kary says:

      I don’t think the official documentation or this article are explicit enough in telling readers /why/ they shouldn’t rely on FrameworkBundle’s base Controller.

      The idea of extending a base Controller is not the major issue here. It’s the fact that FrameworkBundle is distributed with a base Controller, and that your Bundle is now dependent on FrameworkBundle’s implementation of its Controller class.

      By extending a class from another bundle, you are creating a dependency on that Bundle. If your Bundle’s controller extends Symfony\Bundle\FrameworkBundle\Controller\Controller, then every time someone installs your Bundle, they must also install FrameworkBundle.

      Bundles are meant to be independently reusable. Meaning, they should not depend on classes that are not contained 1) Within your Bundle itself; or 2) Within a component. If you are writing a Bundle that’s meant to be reused across many projects, you want to place as much of that code as possible within your Bundle’s namespace/file structure.

      Having said this, I bet the majority of people who will be developing using Symfony2 components will continue to use FrameworkBundle. But it is still an external dependency to YOUR Bundle, and this article shows how to remove that dependency to write your Controllers in a way that makes them easier to reuse.

      If you wanted to, you could write your own base Controller class and stick it in the namespace of your Bundle. For example, LimeThinking\SpringBundle\Controller\Controller. This class would have similar helper methods used by Symfony\Bundle\FrameworkBundle\Controller\Controller (or even a direct copy of it with only the class name changed). This Controller would be distributed with your Bundle and would not rely on FrameworkBundle as an external dependency.

      The official Symfony2 documentation simply offers up the idea of “don’t extend the base Controller” without much support for why.

      Most first generation MVC-based frameworks extend a base Controller. It’s a concept most developers coming to the framework will be accustomed to. Because it’s a large paradigm shift to the holy grail of wiring your Controller as a service, I think we need to explain this method of thinking in more detail.

      I’m willing to add this type of explanation to the manual where it makes sense if others agree on the points I’ve made.

      • Stof says:

        Installing FrameworkBundle is mandatory. Otherwise, you are building your own framework on top of the components.

        The point when using the base controller is that it hides the real dependencies of the controller. When using “$this->render()”, it is not obvious that this implies the use of the templating engine.
        The base controller is mainly a way to make it easier for newcomers, by adding shortcuts. But when sharing a bundle, it is better to make the dependencies obvious (using the controller as a service comes further in this way but it loads all dependencies of a controller each time it is used, even when some actions don’t need them)

      • Kevin McBride says:

        I completely agree. Just stating that it’s wrong to extend a base controller without qualifying it with “if you’re going to be writing publicly available / distributed bundles” is actually promoting bad practice. It would imply that if a dozen of your controllers have similar business logic, that its somehow wrong to keep it DRY and extend a base class.

        We have to make sure we express that:
        1. Controllers don’t have to extend anything, which is pretty awesome.
        2. Just like with any class a developer writes, dependencies should be scrutinized (what do your “use” statements say about your class?)
        3. Using a base controller class is *good* practice when it comes to reducing duplicated logic, as long as the developer understands that those bundles could require some modification before being released publicly.

        I think the overarching issue boils down to dependency management as a whole. When is it ok to couple one class to another? Its something that good developers think about *constantly* but less experienced developers probably haven’t considered (hopefully the amount of attention placed on dependency management in Symfony2 will be an awakening for the latter :) )

        • miller says:

          Sorry if there is some confusion, I do not intend to say that extending a base controller is bad full stop, more that moving away from the provided base controller reveals more clearly the dependencies being used. For me personally, this is one of the reasons for no longer extending it and moving to more explicit managing of the dependencies.

          As @stof says using a controller as service in this way is only really a step towards better dependency management as having the different actions in the same class means that unnecessary dependencies are being loaded. For example in the viewAction no use is made of the form.factory service yet it is still injected.

          I am not intending to suggest that each controller should reimplement common code and violate DRY. Once you have reached this stage though it would probably be better to avoid extending a base class but extract the common functionality into separate services. These could then be reused more easily through composition rather than only through extension.

  3. Ryan Weaver says:

    Hi guys!

    I agree with what John, Stof and Kevin are saying and I think it would be great to add some more details to the documentation. IMO, Kevin says it the best in his 3 points, and overall, we have to remember that the controller chapter of the book will be read very largely by developers that may never need to worry about making their controller a service. That being said, highlighting the rationale behind the base controller, and the fact that you can just spin up your own (as John said) in order to not repeat yourself seems like a great idea.

    So, John – I’d be thrilled if you wanted to add a few notes in the controller chapter.

    I also think that an article like this is a great thing to have in the Symfony ecosphere because as people start to become more advanced, they need something like this to deconstruct what the base controller is really doing.

    Cheers!

    • Fabien says:

      Keep in mind that I still think that declaring controllers as services is a bad idea. Using the base controller is fine by me. Most people will use the base controller, and they will be happy with it because it saves them time.

      • Luis says:

        @fabpot you said it is a bad idea and it is ok, i just want to find out where url? are you elaborating more on the why. i am with all the other guys and it is painful to see you saying that so freely :) I made my mind already to become more advanced like john and will try to always register my controllers as services.

        • Luis says:

          I think all the idea of Dependency Management (DM) needs to be explored further with examples and all. It will promote good practices like DRY, composition, and optimization overall because as john said it can end up optimizing the loading of the services that are used, or at least one can explicitly know where the problem is. I bet all of this makes the graphic on JMSDebugging look very explicit and when debugging that is what one wants.
          Anyway, my poor understanding is trying to grasp all of this. Sorry for babbling.

  4. keymaster says:

    Richard, this is an absolutely fantastic post. Thanks.

    After reading the Symfony2 documentation, I was wondering why some third party bundles (eg. FOSUserBundle) looked nothing like what was being described.

    I was scratching my head trying to figure out why they were doing things that way.

    Thanks to this post I now understand perfectly.

    This should definitely become a cookbook article, in my opinion.

    In general, your posts are outstanding. Please keep them up.

  5. cordoval says:

    great discussion

    also for those blogging on symfony2 here use this @Miller so that the code stands out better and be more readable for symfonians:

    http://www.craftitonline.com/2011/06/code-highlighting-in-wordpress-with-symfony2-style/

    Enjoy!

  6. cordoval says:

    oh and this plugin so people can follow more discussion through comments via email notification

    http://txfx.net/wordpress-plugins/subscribe-to-comments/

  7. As some said above, I don’t really understand WHY you want a controller to be a service most of the times. Arguments are quite good but some of them seems contradictory with other principles (someone mentioned DRY). On the other hand, even Fabien said that he doesn’t like this so, “Houston, we have a problem”, :)

    I’ve been thinking a lot about that and I imagine one situation in wich you could benefit of defining controllers as services.

    Suposse you have a controller-serviced called ‘my_controller’ define like this:

    parameters:
    my_controller.class = MyBundle\Controllers\MyController

    services:
    my_controller:
    class: %my_controller.class%

    This controller just performs one simple task: shows a form, collect the data, sends an email and show a thank you page, but another developer wants to extend it and send a SMS too. The only thing this developer should do is extends this controller like:

    class MyExtendedController extends MyBundle\Controllers\MyController{

    public funcion indexAction(){

    $response = parent::indexAction();

    // Some code to send an SMS

    return $response;

    }
    }

    and change the parameter to fit the new class but anything else need to change: routing, templating, …

    (I know that events should be a better solution for this, but the developer of MyControllers forgot about it, ;) )

    Probably I am wrong as I’ve been playing with Symfony2 not for long, so I will thank any comment that would help.

  8. Jeremy says:

    Although i’m fairly new to symfony2 i find myself using the EventListener to fill a controller with properties. Although this isn’t too logical it has given me a working environment which I seem to like. It works with the following function in a listener class:

    public function onCoreController(FilterControllerEvent $event){
    $controller = $event->getController();
    $shortCuts = array(“page”, “website”, “breadcrumb”, “cache”, “em”, “request”, “router”, “user”, “post”);
    foreach ($shortCuts as $shortCut){
    if (property_exists($controller[0], $shortCut)){
    if (is_null($this->$shortCut)){
    switch ($shortCut){
    case “post”: $this->initPost(); break;
    case “em”: $this->initEntityManager(); break;
    case “user”: $this->initUser(); break;
    case “router”: $this->initRouter(); break;
    }
    }
    $controller[0]->$shortCut = $this->$shortCut;
    }
    }
    }

    This could also be used to set parameters as i dislike the whole $this->container->getParameter(). I’d rather have configuration and its defaults in the class itself.

    The controllers i create look like this:

    class MyController{
    /** @var User * */
    public $user;
    /** @var Request * */
    public $request;
    /** @var ParameterBag * */
    public $post;
    /** @var Page * */
    public $page;
    }

    Making it also possible for IDE’s to pickup the class type.

    • miller says:

      Keeping the configuration in the class makes it more difficult to reuse the class with a different configuration as you will have to change the class itself and potentially maintain different version of it.

      • Luis says:

        John, on another note, my comments can display code I just realized check here:
        http://www.craftitonline.com/2011/07/symfony2-with-jenkins/#comment-2317

        I think we should tell people how to use it perhaps so they can post nicer code into comments like this

        public function onCoreController(FilterControllerEvent $event){
            $controller = $event-&gt;getController();
            $shortCuts = array(“page”, “website”, “breadcrumb”, “cache”, “em”, “request”, “router”, “user”, “post”);
            foreach ($shortCuts as $shortCut){
                if (property_exists($controller[0], $shortCut)){
                    if (is_null($this-&gt;$shortCut)){
                        switch ($shortCut){
                            case “post”: $this-&gt;initPost(); break;
                            case “em”: $this-&gt;initEntityManager(); break;
                            case “user”: $this-&gt;initUser(); break;
                            case “router”: $this-&gt;initRouter(); break;
                        }
                    }
                    $controller[0]-&gt;$shortCut = $this-&gt;$shortCut;
                }
            }
        }
      • Luis says:

        your setup escapes badly the -> characters not sure why because mine doesn’t

      • Luis says:

        lol I guess this reply could generate a blog post titled “When event listeners go wrong” …

  9. Łukasz says:

    Hi. It doesn’t work.

    I have XML:

    and in SoapController class:

    public function __construct(FormFactoryInterface $formFactory) {
    $this->formFactory = $formFactory;
    }

    Argument 1 passed to (..)Controller::__construct() must be an instance of Symfony\Component\Form\FormFactoryInterface, none given.

    I have no idea..

    • miller says:

      Sorry Lukasz I need to sort out allowing XML to be posted in the comments so that I can see what the issue is with this.

  10. John.C says:

    Im not sure I fllow the meaning of the post. Seems like there is a great deal of overegineering going on here. What issue does this actually solve? While, I do not use Symfony, I would say that the concept of a bundle is symfony specific so why try to remove a dependency from the core of the framework? Am I missing something?

  11. consultoria de sistemas…

    [...]Symfony2: Moving Away From the Base Controller | Richard Miller[...]…

  12. Hi my family member! I wish to say that this post is awesome, nice written and come with almost all vital infos. I would like to look more posts like this .

  13. Jérémie says:

    Thank you so much for this great post!
    I have a better understanding about services.

    Un grand merci de Tahiti.
    Jérémie

  14. Bruno says:

    Symfony2 == good concepts but heavy implementations !

    let’s stick to the base controller.

  15. Mikey says:

    Wouldn’t it be cleaner if instead of having…

        public function __construct(Registry $doctrine,
                                    FormFactoryInterface $formFactory,
                                    EngineInterface $templating,
                                    Request $request,
                                    RouterInterface $router)
        {
            $this->doctrine    = $doctrine;
            $this->formFactory = $formFactory;
            $this->templating  = $templating;
            $this->request     = $request;
            $this->router      = $router;
        }
    

    You introduce a parameter object making your interface much cleaner, like so.

    file: bundle/Params/InjectorParams.php

    class InjectorParams
    {
        public $doctrine;
        public $formFactory;
        // etc
    }
    

    And then the constructor changes:

        public function __construct(InjectorParams $params)
        {
            $this->doctrine    = $params->doctrine;
            $this->formFactory = $params->formFactory;
            // etc..
        }
    

Leave a Reply

Please use [code] and [/code] around any source code/html/xml you wish to include in a comment.

Subscribe to RSS Feed Follow me on Twitter!