1
votes

I am trying to use the Symfony Dependancy Injection component to make my PHP classes easier to unit test. However I am getting stuck when the class that I want has constructor arguments that are determined at runtime. The class must be instantiated with the arguments to make sense. So I don't want to use setter injection. I have a feeling that I will not be able to use Dependancy Injection for this situation and need to reorganize the way I am trying to do this, but I am not sure what I need to do...

I read that I should use abstract factory if I have runtime arguments, but my class will also have the $service argument which is not a runtime argument and I would like to inject this so that when I am running tests, I can inject a mock service.

If Abstract factory is the way to go I would like to know how to use Abstract Factory with DI so that I can inject my service.

Otherwise if not Abstract Factory, what are my other options?

class Foo
{
     public function myMethod()
     {
         $container = new ContainerBuilder();
         $loader = new PhpFileLoader($container, new FileLocator(__DIR__));
         $loader->load('services.php'); //Load the config
         $container->compile();

         //I don't know how to get a Bar because it has other runtime arguments.
         $bar = $container->get('bar');
         //...
    }
}

class Bar
{
    private $service;

    public function __construct($userId, $date, $service)
    {
        $this->service = $service
        //...
    }
}

service.php (DI container config):

$container->register('dao', 'Dao');

$container->register('service', 'Service')
        ->addArgument(new Reference('dao'));


$container->register('bar', 'Bar')
        ->addArgument(new Reference('service'));
1

1 Answers

3
votes

You could create a factory like that:

class BarFactory 
{
    private $service;

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

    public function createBar($userId, $date)
    {
        return new Bar($userId, $date, $service);
    }
}

This way you could still pass the service via your dependency injection container.