Vladimir Hraban
Vladimir Hraban

Reputation: 3581

Symfony 2 controller loses container when forwarded to another controller

I have a controller that forwards the whole request to another controller. The first controller is Version1Controller, and $this->controller is not null, giving me access to services. It forwards the request to GetUserTrialsController defined in services as controller.get_user_trials. In the second controller $this->container is null. How come symfony loses it half way through? Is there any way to pass the container around?

Below is the code to demonstrate the behaviour

services.yml

services:
    controller.get_user_trials:
        class: AppBundle\Controller\GetUserTrialsController

Version1Controller.php

<?php
namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

class Version1Controller extends Controller
{
    public function indexAction(Request $request)
    {
        var_dump("V1Controller " . json_encode(is_null($this->container)));
        return $this->forward("controller.get_user_trials:indexAction");
    }
}

GetUserTrials.php

<?php
namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;

class GetUserTrialsController extends Controller
{
    public function indexAction(Request $request)
    {
        var_dump("GetUserTrials " . json_encode(is_null($this->container)));
        return "abc";
    }
}

The output:

string(18) "V1Controller false"
string(13) "GetUserTrials true"
abc

Upvotes: 2

Views: 71

Answers (1)

Cerad
Cerad

Reputation: 48865

Just to amplify a bit on what @qooplmao has said. It is important to understand that there are two distinct ways to instantiate a controller.

The most common way is is by specifying a three part controller string. Something like MyBundle:MyController:my. When a string like this is encountered the routing system determines the controller class, instantiates a new object and then calls setContainer to inject the container.

You can however define a controller as a service by using a string with only two parts. When you do this the routing system simply pulls the object from the container with no additional processing. It's entirely possible that your controller does not need the container at all so there is no point in injecting it.

In your case, just add a call to setContainer in your service definition.

services:
  controller.get_user_trials:
    class: AppBundle\Controller\GetUserTrialsController
    calls: [[setContainer, ['@service_container']]]

And all will be well. Or don't define your controller as a service and just use:

$this->forward("AppBundle:GetUserTrials:index");

I happen to like defining controllers as services but the standard approach works fine for most cases.

Upvotes: 3

Related Questions