S
S
slip312019-07-23 17:54:36
PHP
slip31, 2019-07-23 17:54:36

How to use Di container?

Just yesterday I was introduced to the DI container and the documentation seems to be clear and there are a lot of examples, but it doesn’t reach me.
There is

class App {
    
    private $companyService;

    public function __construct() {
        
        $container = new Container;

        $container->add('CompanyCreateService', CompanyCreateService::class)
                ->addArgument(CompanySqlRepository::class);
        $container->add(CompanySqlRepository::class)
                ->addArgument(DbConnection::class)
                ->addArgument(Hydrator::class);
        $container->add(CompanySqlRepository::class);
        $container->add(DbConnection::class);
        $container->add(Hydrator::class);
        $container->add(AdminController::class);
        $container->add('AdminController', AdminController::class)->addArgument('CompanyCreateService');
        $this->companyService = $container->get('AdminController');
        $this->actionIndex();
    }

    public function actionIndex() {
        $controller = new AdminController();
    }

Here I have registered a container and want to run a controller
class AdminController {

    public $companyService;
    
    public function __construct(CompanyServiceInterface $companyService) {
        $this->companyService = $companyService;
        $this->actionCreate();         
    }

    public function actionCreate() {
        $dto = [...]
        $this->companyService->createCompany($dto);
        echo 'saved';

From the controller AppI run AdminControllerwithout parameters.
And I get an error
Fatal error: Uncaught ArgumentCountError: Too few arguments to function
, which is logical, since I do not pass any parameter to the constructor. But at the same time, the data is still saved to the database as it should.
Question1 - do you still need to pass a parameter to the constructor of the called controller or can you just call it without parameters?
Question 2 - where do you need to do it ? In the controller itself, it’s somehow wrong, probably - because then it’s still a dependency. Where to do it? $container->get('что-нибудь');

Answer the question

In order to leave comments, you need to log in

2 answer(s)
W
Wentixon, 2019-07-23
@Wentixon

I recommend reading a book about the pattern or looking at this site , in particular strategy patterns. Then I think everything will become much clearer, since now you even have the wrong questions, but in general it’s better not to get into such jungle without experience.
In short, then:
We have a container, in which we register classes (services) when the application starts. When registering, we associate an abstraction (interface) and an implementation (concrete class), and basically tell our application HOW to create a concrete object. Then we just do it and get the finished object

// Получаем объект интерфейса из любой точки приложения
$container->get(MyInterface::class);

That is, for this approach to work, we need somewhere (before launching the application) to tell us how to create this interface, and this is what we do using the container
$container->register(MyInterface::class, function () {
  return new MyConcreteClass('какие то параметры');
});

Firstly, the controller is an ordinary class, and secondly, the principle of DI is that your classes do not depend on specific implementations and explicitly DEPEND on ABSTRACTIONS. This is the most important thing to understand. Answering the question - you need to take services directly from the container FOR services, that is, in a service provider and above
Well, by the way, modern frameworks are smart enough and create the classes necessary for the constructor on their own, if they are quite simple, more complex services still need to be registered.
Here is a shorter example I wrote that explains everything in principle, if it is not clear, ask
interface CartRepository
{
    public function add(CartItem $item);
    public function getAll();
}

class SessionCartRepository implements CartRepository
{
    ...
}

class DbCartRepository implements CartRepository
{
    ...
}

class Cart
{
    public function __construct(CartRepository $repository) 
    {
        ...
    }

    public function getTotal()
    {
        ...
    }
}

// Service provider
$container->register(CartRepository::class, function ($container) {
    if ($user = $container->get('auth')->getUser()) {
        $repository = new DbCartRepository($container->get('db'), $user->id)
    } else {
        $repository = new SessionCartRepository();
    }

    return $repository;
});

$container->register(Cart::class, function ($container) {
    return new Cart($container->get(CartRepository::class));
});

D
dmitriy, 2019-07-23
@dmitriylanets

here is a good solution https://container.thephpleague.com/3.x/auto-wiring/

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question