Search code examples
phpdependency-injectionauraphp

Do I need to define a container inside a class for dependency injection?


I am learning Auraphp Di, and I want to write sample code. Suppose I have these files:

public/index.php:

use Aura\Di\ContainerBuilder;
use MyPackage\Component\Authentication\AuthenticateFlow;

require_once dirname(__DIR__) . '/vendor/autoload.php';

$builder = new ContainerBuilder();
$di = $builder->newInstance();

$di->set('authenticateFlow', $di->lazyNew(AuthenticateFlow::class));

$authenticateFlow = $di->get('authenticateFlow');

$authenticateFlow->showName('Belkin');

/src/Components/Authentication/AuthenticationFlow.php:

namespace MyPackage\Components\Authentication;

class AuthenticationFlow
{
    public function showName($name)
    {
        echo $name;
    }
}

This is working fine. Now suppose I have another class (/src/Components/Authentication/Filter.php), which has a method called filterInput:

namespace MyPackage\Components\Authentication;

class Filter
{
    public function filterInput($input)
    {
        return htmlspecialchars($input);
    }
}

How can I inject Filter to AuthenticationFlow, to use filterInput() method? I wanna have something like this in AuthenticationFlow::showName():

echo $this->filter->filterInput($name);

I am aware that I need to inject Filter class in AuthenticationFlow constructor, but I don't know if I can use the container built in the index.php or not. If I need to create another container in AuthenticationFlow, how index.php would be aware of it?


Solution

  • Your application need to make use of the di container heavily in-order to inject the necessary dependencies. This is not the case of Aura.

    Let us step back and look what you would do if you don't use a container.

    In-order to make use of Filter object inside AuthenticationFlow, you need to inject the Filter object either via constructor or a setter method. In the example below I am making use of constructor injection.

    class AuthenticationFlow
    {
        protected $filter;
    
        public function __construct(Filter $filter)
        {
            $this->filter = $filter;
        }
    
        public function showName($name)
        {
            return $this->filter->filterInput($name);
        }
    }
    

    So you will create an object of AuthenticationFlow as below.

    $auth = new AuthenticationFlow(new Filter);
    

    In the case of Aura.Di, you may do something like

    $object = $di->newInstance(AuthenticateFlow::class);
    

    If auto resolution is turned off, you need to define dependencies as below

    $di->params[AuthenticateFlow::class]['filter'] = $di->lazyNew(Filter::class);
    

    This will not be true, in an application. You may need AuthenticateFlow on a different HelloController::class.

    Class HelloController
    {
        protected $auth;
    
        public function __construct(AuthenticationFlow $auth)
        {
            $this->auth = $auth;
        }
    
        public function execute()
        {
            // Do something 
        }
    }
    

    So in that case, HelloController::class need to be instantiated via the di itself. Else the dependencies will not be injected automatically.

    $object = $di->newInstance(HelloController::class);
    

    You can extend the Aura\Di\ContainerConfig and define services in multiple classes.

    Example :

    namespace YourVendor;
    
    use Aura\Di\Container;
    use Aura\Di\ContainerConfig;
    class Config extends ContainerConfig
    {
        public function define(Container $di)
        {
            $di->set(HelloController::class, $di->lazyNew(HelloController::class));
            $di->params[HelloController::class]['auth'] = $di->lazyNew(AuthenticateFlow::class);
            $di->params[AuthenticateFlow::class]['filter'] = $di->lazyNew(Filter::class);
        }
    
        public function modify(Container $di)
        {
            // You can get the service and modify if needed
            // $auth = $di->get('authenticateFlow');
        }
    }
    

    Now your index.php will look like,

    require_once dirname(__DIR__) . '/vendor/autoload.php';
    
    $builder = new ContainerBuilder();
    $di = $container_builder->newConfiguredInstance([
        'YourVendor\Config',    
    ]);
    
    $hello = $di->newInstance(HelloController::class);
    $hello->execute();
    

    As I mentioned in previous answer, I recommend you go through the docs first. It will really help you in long run.