1

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?

Belkin
  • 199
  • 15
  • 1
    There will be only one DI instance throughout your application. You don't need two containers. – Hari K T Dec 11 '19 at 05:47
  • 1
    Can you show me how to use it in AuthenticationFlow? Where should I "set" the Filter, in AuthenticationFlow constructor or in index.php? Looks like passing the container as a parameter to AuthenticationFlow or Filter is not a good idea since I am spreading the container registration all over the place... – Belkin Dec 11 '19 at 20:34

1 Answers1

2

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.

Hari K T
  • 4,174
  • 3
  • 32
  • 51
  • 1
    That's great, thank you for explaining. Your explanation was more straightforward than documentation for me. Just one more question, I saw in the Config class you passed the Container. Is it a good practice? – Belkin Dec 12 '19 at 06:48
  • 1
    One more thing, I see in the Config class you set the needed parameters, as well as loading classes. This needs to know all the needed classes for the config file, which means the config file cannot be dynamic (to load classes on demand). How do you address this issue? – Belkin Dec 12 '19 at 07:01
  • 1
    `I saw in the Config class you passed the Container. Is it a good practice? ` You are bootstrapping container, not passing container to any other classes and doing anything from inside it. `One more thing, I see in the Config class you set the needed parameters, as well as loading classes. This needs to know all the needed classes for the config file, which means the config file cannot be dynamic` I have used Aura.Di extensively in the past. Have not came across any issues like dynamic loading classes on demand. If you have concrete examples I can look. – Hari K T Dec 12 '19 at 14:26