16

I have project and inside this project, I have big own service which is huge and complicated with own dependencies etc... And I wanna create for this service with purpose to use my service in controllers like:

$myService = $this->container->get('service_from_my_domain');

My question - is how inside my facade I can get access to container to service's dependencies. I know only 1 way - is to inject dependency into service in yaml config.
But is there another way to do it? Like:

$dependency = Container::getInstance()->get('my_dependency_service');

I've found this answer but using global variable feels like back in time...

PS: I don't want to inject dependency through yaml config (not constructor injection nor setter injection) because I don't need IoC () here.

cn007b
  • 16,596
  • 7
  • 59
  • 74
  • I've found lot of question with the same name, but this one about another problem, hence please pay attention before mark this question as a duplicate. – cn007b Aug 30 '17 at 16:21
  • I don't understand why do you even need the container if you don't want to use IoC and DI ? – Ramy Nasr Aug 30 '17 at 17:27
  • Container::getInstance() is using a global. In fact, if you want to globally access the container then you pretty much need to use a global by definition. Call it a facade like Laravel does if it makes you feel better but it is still a global. – Cerad Aug 30 '17 at 17:42
  • @RamyNasr I need container because I have to work with other services... – cn007b Aug 30 '17 at 19:33
  • This is quite not recommended. but you can inject the container in this one god service you want to use by using `'@service_container'` in `service.yml` – Ramy Nasr Aug 30 '17 at 19:43
  • @RamyNasr But I don't want to inject service. Injection means you can pass instance1 or instance2 as far as this instance implement injection interface, but I need use certain class in my service without replacing opportunity. – cn007b Aug 30 '17 at 19:51
  • @RamyNasr DI is IoC but I need high-level behavior depend on low-level behavior. I don't need fake IoC which is described here https://app.pluralsight.com/player?course=inversion-of-control&author=john-sonmez&name=inversion-of-control&clip=4&mode=live – cn007b Aug 30 '17 at 19:57
  • IoC does NOT necessarily mean that services can be interchangeable. You can easily enforce an specific class or interface when implementing your services `public function __construct(verySpecificServiceToBeInjected $service) {}`. Either I can't understand what you are saying, or your understanding of what IoC and DI is totally different from mine. – Ramy Nasr Sep 05 '17 at 17:27
  • @RamyNasr When you use `verySpecificServiceToBeInjected` you very far from IoC because no inversion is here... Your top-level behavior depends on low-level `verySpecificServiceToBeInjected` behavior... – cn007b Sep 05 '17 at 19:05
  • @VladimirKovpak isn't this what you said you want to do? Anyway, seems to be a lot of confusion. I will let you figure out what is best for you. Thanks :) – Ramy Nasr Sep 05 '17 at 19:31
  • @RamyNasr Yes, it is exactly what I'm trying to find but without ussing `__construct(verySpecificServiceToBeInjected $service)` without this pseudo IoC... – cn007b Sep 05 '17 at 20:12
  • The example I gave was not IoC at all. It is just a way of making the dependency visible up front. getting a service that you depend on, in the middle of the code, might mean that your dependency is buried deep and other developers might not see it right away. – Ramy Nasr Sep 06 '17 at 18:14

3 Answers3

7

can you do like this

services:
     kernel.listener.acme_listener:
          class: Acme\AcmeBundle\EventListener\AcmeListener
          arguments:
                - @service_container
          tags:
                - { name: kernel.event_listener, event: kernel.controller, method: onKernelController }

your Listener

use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;

class AcmeListener
 {
    /**
     * @var Container
     */
     private $container;

    /**
     * Constructor
     *
     * @param Container $container
     */
   public function __construct(Container $container)
    {
        $this->container = $container;
    }

   public function onKernelController(FilterControllerEvent $event)
    {
        $this->container->...
    }
 }
Robert
  • 3,373
  • 1
  • 18
  • 34
5

If you really want to have a fun and do code escapades, you could do something like this...

Create a Facade class, that must be initialized when the app starts. So, in app.php, just after the line $kernel = new AppKernel('prod', false); do the Facade initialization:

$kernel->boot();
$container = $kernel->getContainer();
\MyBundle\Facade::init($container);

And, here is a code for the Facade class:

<?php

namespace MyBundle;


use Symfony\Component\DependencyInjection\ContainerInterface;

class Facade
{
    /**
     * self|null
     */
    private static $instance = null;

    /**
     * ContainerInterface
     */
    private static $myContainer;

    /**
     * @param ContainerInterface $container
     */
    private function __construct(ContainerInterface $container)
    {
        self::$myContainer = $container;
    }

    /**
     * @param string $serviceId
     *
     * @return object
     * @throws \Exception
     */
    public static function create($serviceId)
    {
        if (null === self::$instance) {
            throw new \Exception("Facade is not instantiated");
        }

        return self::$myContainer->get($serviceId);
    }

    /**
     * @param ContainerInterface $container
     *
     * @return null|Facade
     */
    public static function init(ContainerInterface $container)
    {
        if (null === self::$instance) {
            self::$instance = new self($container);
        }

        return self::$instance;
    }
}

And, wherever you need some service, you create it this way:

$service = \MyBundle\Facade::create('my_dependency_service');


But, if you ask me - I would create a Facade service, that would have a container injected in the constructor. And you would have some method for service creation (Facade::create($serviceId)), that would ask the container for given service ID.

Matko Đipalo
  • 1,676
  • 1
  • 11
  • 23
1

I've built a console app using Symfony 5.

Injecting a service in Symfony 5 is as simples as __construct(ServiceClass $service_name) ( type-hinting ) and everything is a service. But in one case I could not dependency inject at the constructor because the dependency service is conditioned to the parameters given at the command line.

So this is how I injected the needed service on the fly.

<?php

namespace App\Command;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use App\Kernel;

class MyCommand extends Command {
    protected static $defaultName = 'app:my-command';
    private $container;

    public function __construct(Kernel $kernel) {
        $this->container = $kernel->getContainer();
        //...
        parent::__construct();
    }

    protected function configure() {
        // ...
    }

    protected function execute(InputInterface $input, OutputInterface $output) {
        //...
        // read the parameters given in the cmd and decide what class is
        // gona be injected.
        // $service_name = "App\\My\\Namespace\\ServiceClassName"
        $service = $this->container->get($service_name);
        $service->doSomething();

        return Command::SUCCESS;
    }
}
Francisco Luz
  • 2,775
  • 2
  • 25
  • 35