I have no access to container in my controller - symfony

I am trying to access a service in a Symfony Controller
$session = $this->get('session');
But I get the next error:
PHP Fatal error: Call to a member function get() on a non-object
I thought that Symfony2 had the controllers defined as services by default.
Note: this question was originally asked by Dbugger, but he removed it for no reason, while it was already answered.

Using the container in controllers
get() is only a shortcut function provided by the Symfony base Controller class to access the container.
Your controller must extend this class to use this function:
namespace Acme\ExampleBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller
{
// your actions
}
If you don't want to depend on this class (for some reasons) you can extend ContainerAware to get the container injected and use it like in the get() shortcut:
namespace Acme\ExampleBundle\Controller;
use Symfony\Component\DependencyInjection\ContainerAware;
class DefaultController extends ContainerAware
{
public function exampleAction()
{
$myService = $this->container->get('my_service');
// do something
}
}
Creating controllers on your own
Controllers are not defined as services per default, you can define them, but it's not needed to get the container. If a request is made, the routing framework determines the controller, which need to be called. Then the controller gets constructed and the container is injected via the setContainer() method.
But if you construct the controller on your own (in a test or anywhere else), you have to inject the container on your own.
$controller = new DefaultController();
$controller->setContainer($container);
// $container comes trough DI or anything else.

Related

Symfony 4 - how dependecy injection works

Using Symfony 3.4 we call services like so: $container->get('service.name')
so we make sure to give names to our services.
in Symfony 4 we inject services by class name or interface directly in the controller like so:
public function someAction(HttpClientInterface $service){
// do something here
}
so what confuses me here is that we are injecting an interface and symfony takes care of invoking the right object in the controller.
my question is: if I have 2 services that implements the same interface :
class ClassA implements InterfaceX{}
class ClassB implements InterfaceX{}
and in the controller I do this:
public function someAction(InterfaceX $service){
// do something here
}
which service gets invoked ?
Yes, services can implement the same interface and in your controller you will need to inject correct service, like this:
public function someAction(ClassA $serviceA){
// do something here
}
and
public function someOtherAction(ClassB $serviceB){
// do something here
}
If you need same service in multiple methods on the same class, use Dependency Injection on the __construct

Symfony 2.8 injecting RequestStack error

I have problem with injecting RequestStack.
In this case, if I use just Request, everything is fine:
<?php
namespace Cms\AdminBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
...
class DefaultController extends BaseController {
public function mainAction(Request $request) {
...
But if I try to use RequestStack, I get error
"Controller "Cms\AdminBundle\Controller\DefaultController::mainAction()" requires that you provide a value for the "$requestStack" argument (because there is no default value or because there is a non optional argument after this one)."
Here is the code:
namespace Cms\AdminBundle\Controller;
use Symfony\Component\HttpFoundation\RequestStack;
...
class DefaultController extends BaseController {
public function mainAction(RequestStack $requestStack) {
...
Thank you!
According to Symfony docs:
In a controller for instance, don't inject the request stack, use the Request type-hint on an action method argument instead.
RequestStack is a service but Request is a value object (not a service) not accessible from the container.
So if you need request_stack in you controller, depending on your constoller dependecies you can get it from container:
$requestStack = $this->get('request_stack');
or just inject it into controller constructor (if your controller is a service).

Symfony Call to a member function get() on null

I use Symfony components in own framework. I installed DI component and tried to get it in my BaseController but all the time I get the same error:
Call to a member function get() on null
My base controller:
abstract class BaseController implements ContainerAwareInterface
{
use ContainerAwareTrait;
...
}
And when I try to access $this->container->get('ser_id'); return call to a member function.
How to slove this?
Update:
only work if i include in my controller service container
$this->service_container = include __DIR__ .'/../../../container.php';
and use it like this:
$this->service_container->get('id');

How exactly can I define a controller as service using annotations?

This seems to be the fastest and simpliest way to use a controller as service, but I am still missing a step because it doesn't work.
Here is my code:
Controller/service:
// Test\TestBundle\Controller\TestController.php
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
/**
* #Route(service="test_service")
*/
class TestController extends Controller {
// My actions
}
Use :
// Test\TestBundle\Controller\UseController.php
// ...
public function useAction() {
$testService = $this->get('test_service');
}
When I do that, I get the error
You have requested a non-existent service "test_service".
When I check the list of services with app/console container:debug, I don't see my newly created service.
What am I missing?
From Controller as Service in SensioFrameworkExtraBundle:
The #Route annotation on a controller class can also be used to assign the controller class to a service so that the controller resolver will instantiate the controller by fetching it from the DI container instead of calling new PostController() itself:
/**
* #Route(service="my_post_controller_service")
*/
class PostController
{
// ...
}
The service attribute in the annotation is just to tell Symfony it should use the specified service, instead of instantiating the controller with the new statement. It does not register the service on its own.
Given your controller:
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
/**
* #Route(service="test_service")
*/
class TestController
{
public function myAction()
{
}
}
You need to actually register the controller as a service with the test_service id:
services:
test_service:
class: Test\TestBundle\Controller\TestController
The advantage of this approach is that you can inject your dependencies into the constructor by specifying them in the service definition, and you don't need to extend the base Controller class.
See How to define controllers as services and Controller as Service in SensioFrameworkExtraBundle.
For future folks, if you decide to use controller-as-a-service, you should better inject your services into the controller via the constructor instead of getting them through a service locator. The former is considered to be an antipattern, while the latter allows easy unit testing and is simply more verbose.
So instead of:
public function useAction() {
$testService = $this->get('test_service');
}
You should:
private $yourService;
public function __construct(YourService $yourService)
{
$this->yourService = $yourService;
}
public function useAction()
{
$this->yourService->use(...);
}
Don't create shortcuts, write solid, maintainable code.
For Symfony 3.4, we don't need to register controllers as services because they are already registered as services with the default services.yml configuration.
You just have to write this :
// current controller
public function myAction() {
$test = $this->get(SomeController::class)->someAction($param);
}

How to use monolog from the class which inherit Controller class

I am using monolog
in class DefaultController extends Controller
such as
$logger = $this->get('logger');
$logger->info('Get Started');
I can call this->get('logger') from the class which inherits Controller class.
However I want to use logger from other class such as /Entity/User.php
How can I make it?
my reference is
http://symfony.com/doc/2.0/cookbook/logging/monolog.html
In general you can access services like the logger in classes where the container is not automatically injected ( i.e. Controller and Commands extending ContainerAwareCommand ) by using Dependency Injection.
Possible injection-types are property-,setter- and constructor injection. My example will cover constructor injection. you will first need to create a service for your class.
Assuming yml-configuration an example could look like this:
services:
your_service:
class: Vendor/YourBundle/NonControllerExtendingClass
arguments: ["#logger"] # inject logger service into constructor
In my example the 'logger' service is automatically injected in the NonControllerExtendingClass if it is called as a service. Make sure you have something like this in your Vendor/YourBundle/NonControllerExtendingClass:
use Symfony\Component\HttpKernel\Log\LoggerInterface;
// ...
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
now use the logger in your method like this:
public function someAction()
{
$this->logger->info('Im here');
}
The logger will only be injected if you call your class as a Service or inject the logger manually.
// ... gets service from the container
$my_service = $this-container->get('your_service');
// ... manual injection
$logger = $this->container->get('logger');
// alternatively create a logger object yourself i.e. $logger = new Logger();
$my_service = new Vendor/YourBundle/NonControllerExtendingClass($logger);
If you want to track entity changes with the logger you should use a Doctrine Event Listener or Subscriber.
Readmore about it in the documentation chapter - How to Register Event Listeners and Subscribers.
Because of separation of concerns, entities should not have dependencies on services. Depending on your needs, it might be wise to use the logger from the controller/service or whatever that calls the entities method that you want to log.
Generally speaking, you can define classes as services and inject the logger into that service. If you are not yet familiar with the service container and dependency injection, I would strongly advise you to read this chapter of the docs.
It might be a tricky subject to grasp. However, as it is such a vital component of symfony, it will be really worth your while to try to understand this.

Resources