Web development with Yii3 Packages: Part 2 – DI Container

Hi! I hope you are well and following this series. In the first part of the series, we learned on how you can use Yii3 database abstraction layer package, in a non-Yii application. Today, we will look at the Dependency Injection Container, another Yii3 package that you can use with your own application or non-Yii framework. I will assume that you know the basics of dependency injection. If not, I suggest you check a lot of resources on the topic. If you have specific question on the topic, feel free to ask in our channels or other Yii channels.

As usual, let us start by installing required packages.

(i) INSTALL PACKAGES
Installing DI Container is as easy as running in the terminal composer require yiisoft/di and wait it to finish. Straight forward thing!

(ii) SETTING UP
Once done, you need to configure it. Basically you create array of definitions, each defining how container should instantiate class, when needed. For example to instantiate cache object and use it with our container, we just need to provide simple array as one below. Note that you can add more settings depending on what the kind of class you are instantiating provides you in terms of options/configurations.

<?php
$definitions = [
    CacheInterface::class => [
        'class' => ArrayCache::class,
    ]
];

That is all a definition requires. Some class are complex in terms of dependencies as we will see in a moment, but a lot others are quiet simple. Now that we have defined our cache to be ArrayCache (You can use file caching or many others provided by Yii3), it is time to define the database connection. Remember the goal: Using container to pull out our objects instead of doing it manually. Here is how we define the database and its dependencies:

<?php
$definitions = [
    CacheInterface::class => [
        'class' => ArrayCache::class,
    ],
    ConnectionInterface::class => [
        'class' => Connection::class,
        '__construct()' => [
            'driver' => new Driver(
                $params['yiisoft/db-mysql']['dsn'],
                $params['yiisoft/db-mysql']['username'],
                $params['yiisoft/db-mysql']['password'],
            ),
        ],
    ],
];

The definitions should be familiar by now except the ‘__construct()’ part. If you look at Connection class parent (AbstractPdoConnection) you will find that it defines constructor like this: public function __construct(PdoDriverInterface $driver, SchemaCache $schemaCache). The container will pass instance of Driver instance to first argument as provided while auto instantiating and passing cache object when creating the connection instance. This is the beauty of DI Container: You define dependency once and it will make instances for us. If we had something else that depended on both database connection and cache, we would define it and container will pass dependencies for us.

Now the full code for container.php should be:

<?php

declare(strict_types=1);

use Psr\SimpleCache\CacheInterface;
use Yiisoft\Cache\ArrayCache;
use Yiisoft\Db\Connection\ConnectionInterface;
use Yiisoft\Db\Mysql\Connection;
use Yiisoft\Db\Mysql\Driver;
use Yiisoft\Di\Container;
use Yiisoft\Di\ContainerConfig;

require_once('../vendor/autoload.php');
$params = require_once('config/db.php');

//Create DI container
$definitions = [
    CacheInterface::class => [
        'class' => ArrayCache::class,
    ],
    ConnectionInterface::class => [
        'class' => Connection::class,
        '__construct()' => [
            'driver' => new Driver(
                $params['yiisoft/db-mysql']['dsn'],
                $params['yiisoft/db-mysql']['username'],
                $params['yiisoft/db-mysql']['password'],
            ),
        ],
    ],
];

$container = new Container(
    ContainerConfig::create()
        ->withDefinitions($definitions)
);

One more thing to explain is the use of params. Instead of hardcoding them on container, we are pulling them from a configuration file, under config/db.php.

<?php

declare(strict_types=1);

use Yiisoft\Db\Mysql\Dsn;

return [
    'yiisoft/db-mysql' => [
        'dsn' => (new Dsn('mysql', '127.0.0.1', 'students', '3306', ['charset' => 'utf8mb4']))->asString(),
        'username' => 'root',
        'password' => '123456',
    ],
];

Last piece of cake in setting up is creating actual container. As you have already seen it in the code above, it is as easy as just creating Container, passing instance of Container configurations which requires our definitions. Here it is.

$container = new Container(
    ContainerConfig::create()
        ->withDefinitions($definitions)
);

(iii) TYING ALL TOGETHER
Now that we have our container setup, we can easily use it. In index.php, we just includes container.php and easily query connection instance from our container. The container should make for us an instance we can use for our task. Here is the code:

<?php
use Yiisoft\Db\Connection\ConnectionInterface;
require_once('includes/container.php');

$db = $container->get(ConnectionInterface::class);
//Pull students as usual

The rest of the code is not affected. Note how easy it is to get the connection instance. The same change goes into the new-student.php file. The code should work fine, if you did exactly as described above.

(iv) CLOSING REMARKS
Now for someone who have never used dependency injection containers might ask, what is the use of all these trouble, only to achieve what was previously achieved without all these juggling? A lot! For this simple code it might not be much, but imagine a situation where you are using libraries written by third-party or other developers. Normally you would had to create each instance yourself and provide it when it is needed. And if another library depends on another library which depends on another two library that depends on a library, it would be your task to resolve them all and provide them.

But with DI Container, it does the resolution and instantiation for you. Life is made easier by proper use of DI container!

We have barely scratched the surface of what yiisoft/di package provides. There is an extensive documentation on dependency injection in the Yii3 Guide, Please check it out.

Also Yii have a lot of other packages you can use in your PHP application. Conside checking Yiisoft github. Follow Yii Framework handle for releases and other official announcements and Yii Updates for regular Yii updates. If you didn’t know, Yii have quiet a presence in social media space, make sure to visit the page outlining official channels here

Please let me know if you have any comments on our X account @hosannahtech

Until next part of this series,
Enjoy New Year 2024!

Leave a Reply

Your email address will not be published. Required fields are marked *