thephpleague / route

Fast PSR-7 based routing and dispatch component including PSR-15 middleware, built on top of FastRoute.
http://route.thephpleague.com
MIT License
651 stars 126 forks source link

Tip: Dependency Injection in controller method #285

Closed LuanMaik closed 3 years ago

LuanMaik commented 4 years ago

I create a class that allow use dependency injection in controller method.

Implementation based in ApplicationStrategy:

namespace MyApp\Modules\Application\Route\Strategy;
use League\Route\Route;
use League\Route\Strategy\ApplicationStrategy as LeagueApplicationStrategy;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

class ApplicationStrategy extends LeagueApplicationStrategy
{
    /**
     * {@inheritdoc}
     * @throws \ReflectionException
     */
    public function invokeRouteCallable(Route $route, ServerRequestInterface $request): ResponseInterface
    {
        $controller = $route->getCallable($this->getContainer());

        $reflection = new \ReflectionMethod($controller[0], $controller[1]);
        $methodParams = $reflection->getParameters();

        // Initialize the array of resolved method dependencies
        $resolvedParams = [
            $request,
            $route->getVars(),
        ];

        // Remove two first params from array of the method dependencies to resolution
        // because the two first it's already resolved and defined in $resolvedParams initialization
        $methodParams = array_slice($methodParams, 2);

        // Resolve the dependencies
        foreach ($methodParams as $methodParam) {
            if ($methodParam->getClass()) {
                $resolvedParams[] = $this->getContainer()->get($methodParam->getClass()->getName());
            } else {
                $resolvedParams[] = $this->getContainer()->get($methodParam->getName());
            }

        }

        // Call the method
        $response = $controller(...$resolvedParams);

        $response = $this->applyDefaultResponseHeaders($response);

        return $response;
    }
}

In definition of route strategy, replace this:

$strategy = (new League\Route\Strategy\ApplicationStrategy())->setContainer($container); // default implementation
$router   = (new League\Route\Router)->setStrategy($strategy);

to this:

$strategy = (new MyApp\Modules\Application\Route\Strategy\ApplicationStrategy())->setContainer($container); // custom implementation
$router   = (new League\Route\Router)->setStrategy($strategy);

Example route:

$router->map('GET', '/user/{iduser}', [\App\Modules\Auth\Controllers\UserController::class, 'find']);

Example controller:

class UserController
{
    protected $userService;

    nction __construct(UserService $userService)
    {
        $this->userService = $userService;
    }

    /**
     * Your method MUST start with the request object and route params array in beginning, 
     * after that, you are free to use the dependency inject as you wish.
     */
    public function find(ServerRequestInterface $request, array $routeParams, UserService $IcanInjectUserServiceHereToo)
    {
        // ...
    }
}