klein / klein.php

A fast & flexible router
MIT License
2.66k stars 290 forks source link

More concise, Laravel-style Controller@method callbacks #400

Closed nikrowell closed 5 years ago

nikrowell commented 5 years ago

Thanks for all work on this @Rican7 ! I'm interested in adding support for Laravel-style 'Controller@method' route callbacks. Something like this:

$klein->get('/users', 'UsersController@index');
$klein->get('/users/[i:id]', 'UsersController@show');

... which could be implemented by adding the following to setCallback in Route.php:

if (is_string($callback) && strpos($callback, '@') !== false) {

    $parts = explode('@', $callback);
    $class = $parts[0];
    $method = $parts[1];

    if (!class_exists($class)) {
        throw new InvalidArgumentException('Controller class '. $class .' does not exist');
    }
    if (!method_exists($class, $method)) {
        throw new InvalidArgumentException('Controller method '. $method .' does not exist');
    }

    $callback = function($req, $res, $ser, $app) use ($class, $method) {
        $controller = new $class($req, $res, $ser, $app);
        $controller->$method($req, $res);
    };
}

I know there is support for static controller methods, but this technique also opens up some interesting possibilities for using a base Controller class, adding middleware etc. I'd be happy to submit this as a Pull Request with tests etc if this is something you'd be open to adding to Klien.php. It doesn't look like this project is getting much activity in terms of recent commits, but I think it's delightful to use on small projects and would love to see it continue to be maintained.

nikrowell commented 5 years ago

Or for a slightly more sophisticated approach that detects route params and passes those into the method call, replace the $callback closure above with this:

$reflection = new ReflectionMethod($class, $method);
$params = (array) $reflection->getParameters()[0];
$params = $req->paramsNamed()->all(array_values($params));
$controller = new $class($req, $res, $ser, $app);
$reflection->invokeArgs($controller, $params);
rekcuFniarB commented 5 years ago

I already use similar style in a Klein.php based project:

$router->respond('GET', '/', 'main->index');

Example. Implementation is here (done by extending Klein class).

nikrowell commented 5 years ago

@rekcuFniarB thanks for sharing!