Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dependency Injection in Slim Framework - passing Container into your own classes

Tags:

php

slim

I've already commented on this thread but it seems to be dead so I'm opening a new one: Dependency Injection Slim Framework 3

The post above explains how pass Slims Container to a class you've written yourself.

However, the OP has asked if it's possible to get Slim to Dependency Inject ALL their classes.

I'm also interested in knowing if there's a way to do this since it seems to be anything but DRY if you have to pass the container to every class that you want to use it.

As an example, if I want to use one of Slim's functions (such as doing a redirect, in one of my own classes) I cannot use this as per the documentation:

$res->withStatus(302)->withHeader('Location', 'your-new-uri');

Because $res (the response object) is not within the scope of my class, unless I inject/pass it.

The problem with this is, if I have say 100 classes, do I have to pass (or inject) the container 100 times? That seems really, really tedious.

In frameworks like CakePHP you can use the 'AppController' to globally do stuff like this, i.e. define things once, and make it available in ALL your classes. Does Slim not provide this functionality? If not, that's a serious drawback, IMO.


Edit - I'm adding this from one of the comments I've made to try and explain the issue further:

If you look at the First Application Tutorial - http://slimframework.com/docs/tutorial/first-app.html - they are adding a PDO database connection to the container.

Let's say I have 100 separate classes in a sub-directory (the example has a ../classes/ directory) and autoload them in index.php using spl_autoload_register(). The container is NOT available in any of those classes.

If I had to pass something 100 separate times, each time I use one of my classes, just to get a PDO connection (and that's just one example) then that makes the code very repetetive, i.e. not DRY.

like image 239
Andy Avatar asked Oct 21 '16 11:10

Andy


2 Answers

Slim comes with Pimple by default. Some developers argue (and I tend to agree with them) that Pimple is not a dependency injection container, but a service locator, since it doesn't resolve dependencies on its own, you need to register them.

Slim 3 works with any dependency manager that implements Container interop interface, which PHP-DI does.

Go for this package. This is what I'm using for my projects and it's simply amazing, because of autowiring. To put it simply, PHP-DI reads constructor of the class and understands what needs to be injected, son you don't have to register dependencies as you would with Pimple.

Sometimes I think (hope?) that PHP-DI will replace Pimple as Slim's default DI container, because it's simply more advanced.

Here's how you do with Pimple:

<?php
namespace Controllers;

class UsersController
{
    // Inject Container in controller (which is bad, actually)
    public function __construct(ContainerInterface $container)
    {
        // grab instance from container
        $this->repository = $container['userRepository'];
    }

    // Handler of a route
    public function getAllUsers($request, $response)
    {
        $user = $this->repository->getAllUsers();
        return $response->withJson($users);
    }
}

Here's the same controller with PHP-DI:

<?php
namespace Controllers;

class UsersController
{
    // Declare your dependencies in constructor:
    // PHP-DI will find the classes and inject them automatically
    public function __construct(UserRepository $repository)
    {
        $this->repository = $repository;
    }

    // Handler of a route
    public function getAllUsers($request, $response)
    {
        $user = $this->repository->getAllUsers();
        return $response->withJson($users);
    }
}

The problem with this is, if I have say 100 classes, do I have to pass (or inject) the container 100 times? That seems really, really tedious.

If you use Slim bundled with PHP-DI, the problem is solved autmatically with autowiring. :)

like image 129
Georgy Ivanov Avatar answered Sep 27 '22 20:09

Georgy Ivanov


The simplest way to do this is like so:

index.php

$app->get('/mytest', '\TestController:mytest');

TestController.php

class TestController {

    protected $ci;

    public function __construct(Slim\Container $ci) {
        //var_dump($ci);
        $this->ci = $ci;
    }

    public function mytest() {
        $sql = ''; // e.g. SQL query
        $stmt = $this->ci->db->prepare($sql);
    }
}

I'm not sure if this is the "correct" way of doing it, but what happens is that the constructor of TestController receives the container as the first argument. This is mentioned in their documentation: http://www.slimframework.com/docs/objects/router.html#container-resolution

So when you're using a function like TestController::mytest() it has access to anything in the container, such as the PDO Database instance you set up in index.php (if following their First Application example tutorial).

As I say, I'm not sure if that's the "right" way of doing it, but it works.

If you uncomment the var_dump($ci) line in you'll see the Slim Container object.

If anyone has any feedback on this please comment as I'd be interested in knowing.

like image 42
John Avatar answered Sep 27 '22 21:09

John