Kearney Taaffe Kearney Taaffe - 7 months ago 246
PHP Question

Dependency Injection Slim Framework 3

I'm using Slim Framework 3 to create an API. The app structure is: MVCP (Model, View, Controller, Providers).

Is it possible to have Slim Dependency Inject all my classes?

I'm using composer to autoload all my dependencies.

My directory structure looks like this:

/app
- controllers/
- Models/
- services/
index.php
/vendor
composer.json


Here's my
composer.json
file.

{
"require": {
"slim/slim": "^3.3",
"monolog/monolog": "^1.19"
},
"autoload" : {
"psr-4" : {
"Controllers\\" : "app/controllers/",
"Services\\" : "app/services/",
"Models\\" : "app/models/"
}
}
}


Here's my
index.php
file. Again, the dependencies are being auto injected by composer

<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;

require '../vendor/autoload.php';

$container = new \Slim\Container;
$app = new \Slim\App($container);

$app->get('/test/{name}', '\Controllers\PeopleController:getEveryone');

$app->run();


My controller looks like this

<?php #controllers/PeopleController.php

namespace Controllers;

use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;


class PeopleController
{
protected $peopleService;

protected $ci;
protected $request;
protected $response;

public function __construct(Container $ci, PeopleService $peopleService)
{
$this->peopleService = $peopleService;
$this->ci = $ci;
}

public function getEveryone($request, $response)
{
die($request->getAttribute('name'));

return $this->peopleService->getAllPeoples();
}
}


My PeopleService file looks like this:

<?php

namespace Services;

use Model\PeopleModel;
use Model\AddressModel;
use Model\AutoModel;


class PeopleService
{
protected $peopleModel;
protected $autoModel;
protected $addressModel;

public function __construct(PeopleModel $peopleModel, AddressModel $addressModel, AutoModel $autoModel)
{
$this->addressModel = $addressModel;
$this->autoModel = $autoModel;
$this->peopleModel = $peopleModel;
}

public function getAllPeopleInfo()
{
$address = $this->addressModel->getAddress();
$auto = $this->autoModel->getAutoMake();
$person = $this->peopleModel->getPeople();

return [
$person[1], $address[1], $auto[1]
];
}
}


Models/AddressModels.php


<?php

namespace Model;

class AddressModel
{

public function __construct()
{
// do stuff
}

public function getAddress()
{
return [
1 => '123 Maple Street',
];
}
}


Models/AutoModel.php


namespace Model;

class AutoModel
{

public function __construct()
{
// do stuff
}

public function getAutoMake()
{
return [
1 => 'Honda'
];
}
}


Models/PeopleModel.php


<?php
namespace Model;

class PeopleModel
{

public function __construct()
{
// do stuff
}

public function getPeople()
{
return [
1 => 'Bob'
];
}

}


ERROR
I'm getting the following error now:

PHP Catchable fatal error: Argument 2 passed to Controllers\PeopleController::__construct() must be an instance of Services\PeopleService, none given, called in /var/www/vendor/slim/slim/Slim/CallableResolver.php on line 64 and defined in /var/www/app/controllers/PeopleController.php on line 21


THE QUESTION
How do I dependency inject all my classes? Is there a way to automagically tell Slim's DI Container to do it?

Answer

When you reference a class in the route callable, Slim will ask the DIC for it. If the DIC doesn't have a registration for that class name, then it will instantiate the class itself, passing the container as the only argument to the class.

Hence, to inject the correct dependencies for your controller, you just have to create your own DIC factory:

$container = $app->getContainer();
$container['\Controllers\PeopleController'] = function ($c) {
    $peopleService = $c->get('\Services\PeopleService);
    return new Controllers\PeopleController($c, $peopleService);
};

Of course, you now need a DIC factory for the PeopleService:

$container['\Services\PeopleService'] = function ($c) {
    $peopleModel = new Models\PeopleModel;
    $addressModel = new Models\AddressModel;
    $autoModel = new Models\AutoModel;
    return new Services\PeopleService($peopleModel, $addressModel, $autoModel);
};

(if PeopleModel, AddressModel or AutoModel had dependencies, then you would create DIC factories for those too)

Comments