Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony2 passing data between bundles & controllers

this is more of a 'best practice' question than an actual problem:

I'm working on a project in Symfony 2 and I've built a bundle to handle all of my webservices. Basically one controller takes some JSON data, sends it off to another controller to check it matches a described format, then off to another controller to handle the database calls and eventually back to the initial controller to return a JSON response.

I'm sending data between controllers by doing something like this:

$controller = new \Acme\myBundle\Controller\DefaultController;
$response = $controller->function();

This works correctly, but I keep coming across one problem. In the controller I pass data to I need to instantiate AppKernel and call the boot function for any Symfony function to work. This to me seems a little bit silly which leads me to believe I may be doing this all wrong.

Any suggestions or tips appreciated!

EDIT/UPDATE Thank you all for the comments. I have set up my controllers as services, the services are working but I am still needing to manually boot/instantiate the kernel when I call a service from inside a service.

#config.yml
# API Services
services:
service.handler:
    class: Acme\APIBundle\Controller\ServicesController
    arguments:
        container: "@service_container"

service.definitions:
    class: Acme\APIBundle\Controller\DefinitionController
    arguments:
        container: "@service_container"

From another bundles controller I can then call a function from either of those services without problem:

 $test = $this->get("service.handler")->testFunction();
 $test2 = $this->get("service.definitions")->anotherTestFunction();

Where I DO have a problem is if I call a function inside of one service, then try to call another service from inside that service I get the following PHP error

Fatal error: Call to a member function get() on a non-object in /vendor/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php on line 188

I can avoid that error by using this function and calling it rather than using $this

    public function bootKernel(){
//boot kernel
    $controller = new \Acme\myBundle\Controller\DefaultController; 
    $kernel = new \AppKernel('dev', true);$kernel->loadClassCache(); 
    $kernel->boot(); 
    $controller->setContainer($kernel->getContainer());
    return($controller);    
}

I guess my solution 'works' but it certainly doesn't seem an efficient way to do things.

EDIT 2: If I stick this at the top of the class then modify the service call it seems to work... Still not sure if this is the best way to do things.

    protected $container;

public function __construct($container) {
    $this->container= $container;
}
like image 711
greg Avatar asked Mar 03 '12 00:03

greg


1 Answers

I'd go down the controller-as-a-service route with the above. Say you have to call 3 methods as part of your processing. Let's call them foo(), bar() and something(). Each of these methods are in separate controllers:

namespace Acme\DemoBundle\Controller;

class FooController
{
    public function __construct($container, ...)
    {
        $this->container = $container;
        // ... deal with any more arguments etc here
    }

    public function foo($params)
    {
        // ...
        return $x;
    }

    protected function get($service)
    {
        return $this->container->get($service);
    }
}

Ditto for the bar() and something() methods, each in their own controller. Then, add them to your application as a service. Eg in your config.yml (other methods available):

services:
    my.foo.service:
        class: Acme\FooBundle\Controller\FooController
        arguments:
            container: "@service_container"

See the Symfony docs for more details about how you can construct this entry, including injecting any dependencies such as an entity manager or similar. Now, you can get an instance of this from the container:

public function yourMainAction()
{
    // ...

    $newData = $this->get("my.foo.service")->fooAction($someData);

    // ...

    return new Response(json_encode($newData), ...);
}

Likewise again for BarController and SomethingController. The advantage now is that this service can be made available at any point in your application (whether via the container as above, or as an injected service), across bundles, without needing to instantiate the class manually yourself and provide any dependencies.

For more information about the container etc, the Symfony docs have a good section on it (referenced in the link above).

Edit: Adjusted the code example to include details of passing in arguments into the service. Also added a convenience get() method to retrieve services from the container.

like image 123
richsage Avatar answered Dec 04 '22 07:12

richsage