Simple example on how to use injector and DI?

As a DI noob, I wonder if there’s a simple example on how to use Yii 3 DI and injector?

As an example, say I have:

class ProcessOrderCommand
{
  public function __construct(Db $db, Curl $curl) { ... }
  public function run() { ... }
}

Which code would I have to call to automatically populate the $db and $curl variables with the correct classes?

Do I have to use DbInterface rather than Db, or is that just a considered best practice?

This simple snippet seems to do the trick:

<?php

require_once("vendor/autoload.php");

use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
use Yiisoft\Injector\Injector;

class ProcessOrderCommand
{
    public function __construct(Db $db, Curl $curl) {}
}

class Db {}
class Curl {}

$config    = ContainerConfig::create();
$container = new Container($config);
$injector  = new Injector($container);
$obj       = $injector->make(ProcessOrderCommand::class);

Will try to combine this with the most basic Route snippet I can figure out, too.

Do I have to use DbInterface rather than Db , or is that just a considered best practice?

In order to answer this for your case, check the following topics:

1 Like

I think “code to an interface, not an implementation” is the one, eh? :slight_smile: On the other hand, sometimes the probability of switching the implementation is very, very low, so adding another layer of complexity might not be worth it. Not that an interface is so complicated…

PHPUnit should easily be able to do mocking of classes too, not only interfaces, IIRC.

Also might be useful:

2 Likes

Hm getting confused again. Isn’t this support to work?

Using DI 1.1 and injector 1.2 due to PHP 7.4 constraints.

<?php

require_once("vendor/autoload.php");

use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;
use Yiisoft\Injector\Injector;

$diConfig = ContainerConfig::create()->withDefinitions(['\Foo' => fn() => new stdClass()]);
$container = new Container($diConfig);
$injector  = new Injector($container);
$db = $injector->make('\Foo');

Getting error

PHP Fatal error: Uncaught ReflectionException: Class “\Foo” does not exist

This line works tho

$db = $container->get('\Foo');

Injector creates and instance of the class specified using container for getting dependencies. It’s not getting the class itself from the container.

OK, but shouldn’t the dependency resolver be able to handle scalar arguments to constructors, too?

I have diconfig.php:

return [
    Monolog\Logger::class => function() {
         // Logic to configure name and debug level
    },
    DbWrapper::class => function() {
        return new DbWrapper();
    }
];

and

$diConfigData = include("diconfig.php");
$diConfig = ContainerConfig::create()->withDefinitions($diConfigData);
$container = new Container($diConfig);
$injector  = new Injector($container);
// Breaks because of $name string argument to Logger constructor
$logger->make(Monolog\Logger::class); 

At this point I want to pass around the $injector to a Redis message task object, but it seems I have to either pass around both the container and the injector, or just create the container from scratch in the class?

Funny thing is, a DbWrapper class works fine to fetch from the injector, since it has no arguments.

So yea, what’s the deal with scalar arguments? Why would $container->get() work but not $injector->make()?

I can also do

$logger = $injector->make(Logger::class, ['name' => 'fck']);

but then the configured construction isn’t run at all!

Is injector::make needed at all if container::get can do the job?

Is injector::make needed at all if container::get can do the job?

It depends. If you need the same instance, container would do. If you need to create an instance without storing it anywhere, makeis better.

So yea, what’s the deal with scalar arguments? Why would $container->get() work but not $injector->make()?

Possible to add a test that fails in a pull request to injector package?

2 Likes