chucky
chucky

Reputation: 11

FastRoute: Pass route prefix to handler

For example, lets say I have this route.

<?php declare(strict_types = 1);

$dispatcher = FastRoute\cachedDispatcher(function(FastRoute\RouteCollector $router) {

    $router->addRoute('GET', '/{slug}', ['App\Controllers\SomeController', 'someMethod']);

}, [ 'cacheFile' => ROOT . '/storage/cache/route.cache', 'cacheDisabled' => true, ]);

Here is how I handle routes, and call the controller and its method.

switch ($routeInfo[0]) {
    case FastRoute\Dispatcher::NOT_FOUND:
        echo '404 Not Found';
        break;
    case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
        $allowedMethods = $routeInfo[1];
        echo '405 Method Not Allowed';
        break;
    case FastRoute\Dispatcher::FOUND:
        $controller = $dice->create($routeInfo[1][0]);
        echo $controller->{$routeInfo[1][1]}($routeInfo[2]);
        break;
}

How do I pass {slug} to the controller method? It doesn't mention anything about it in its documentation, and no information about it can be found via a google search.

Upvotes: 1

Views: 2852

Answers (1)

user7941334
user7941334

Reputation:

I didn't work with DICE until now, though I looked in its implementation in order to present the first option to you. I hope it will work. If not, feel free to read the DICE documentation/code regarding the call rule and the create method.

Note: The title should be something like "FastRoute: Pass route parameters to handler", or "FastRoute: Pass route arguments to handler", because a prefix is defined as the route part which is prepended to each route pattern inside of a route group.

Option 1: Use the call rule of the DI container (DICE):

This is, of course, the recommended way, since the DI container automatically injects the method arguments. Which could be more than the ones read from the route!

Note: controller method =: "action".

See (in DICE docs):

Route:

$router->addRoute('GET', '/{userName}[/{userId:\d+}]', ['UserController', 'list']);

Note: if you have optional route parts, then you have to define the corresponding action parameters as optional.

Dispatching request by FastRoute:

case FastRoute\Dispatcher::FOUND:
    $controllerName = $routeInfo[1][0]; // "UserController"
    $action = $routeInfo[1][1]; // "list" action
    $parameters = $routeInfo[2]; // Action parameters list (e.g. route parameters list)

    $rule['call'] = [ // Define the method to be called and the parameters to be passed to the further created controller.
        [$action, $parameters],
    ];

    $dice->addRule($controllerName, $rule);

    $controller = $dice->create($controllerName); // UserController instance

    break;

Action in UserController:

public function list($userName, $userId = NULL) {
    return 'User name = ' . $userName . ', User id = ' . $userId ?? 'N/A';
}

Option 2: Call the action (without DICE), separately passing all route parameters to it:

Route:

The same.

Dispatching request by FastRoute:

case FastRoute\Dispatcher::FOUND:
    $controllerName = $routeInfo[1][0]; // "UserController"
    $action = $routeInfo[1][1]; // "list" action
    $parameters = $routeInfo[2]; // Action parameters list (e.g. route parameters list)

    $controller = $dice->create($controllerName); // UserController instance

    call_user_func_array(
        [$controller, $action] // callable
        , $parameters
    );

    break;

Action in UserController:

public function list($userName, $userId = NULL) {
    return 'User name = ' . $userName . ', User id = ' . $userId ?? 'N/A';
}

Option 3: Call the action (without DICE), passing an instance of a Request class:

Assign the route parameters list to a Request instance (See PSR-7), as attribute, and pass the instance as action argument.

Route:

The same.

DI container definitions:

// Share a Request instance.
$dice->addRule('Request', ['shared' => true]);

Dispatching request by FastRoute:

case FastRoute\Dispatcher::FOUND:
    $controllerName = $routeInfo[1][0]; // "UserController"
    $action = $routeInfo[1][1]; // "list" action
    $parameters = $routeInfo[2]; // Action parameters list (e.g. route parameters list)

    // Create Request instance.
    $request = $dice->create('Request');

    // Assign the route parameters list to the Request instance.
    $request->setAttribute('parameters') = $parameters

    $controller = $dice->create($controllerName); // UserController instance

    call_user_func_array(
        [$controller, $action] // callable
        , [$request]
    );

    break;

Action in UserController:

public function list(ServerRequestInterface $request) {
    $userName = $request->getAttribute('parameters')['userName'];
    $userId = $request->getAttribute('parameters')['userId'] ?? 'N/A';

    return 'User name = ' . $userName . ', User id = ' . $userId ?? 'N/A';
}

Upvotes: 1

Related Questions