Automatic Routing (attempt 2)


#1

Just to check if I have something worth using & sharing :slight_smile:
All routing goes to the corresponding controllers which reside in Modules.
Modules
– Front
---- MainController
---- AnotherFrontController
– Admin
---- AdminController
---- UserController

So if you call /admin/user/list

if will auto-magically go to the edit function within the UserController

routes.php:

<?php
/**
 * All the routes to their corresponding controllers
 *
 * The first param is the start of the url
 * Example:
 * every url starting with /admin will be handled by the configured controller
 *
 */
use MyApp\Common\Helpers\RouteResolver;

$app->any('[/{args:.*}]', function ($request, $response, $args) use($app)
{
    $obRouteResolver = new RouteResolver($this, $request, $response, $args);
	return $obRouteResolver->call($this, $request, $response, $args);
});

RouteResolver.php:

<?php

namespace MyApp\Common\Helpers;

/**
 * Class routeResolver
 * creates dynamic routing functionality
 * routes url to the correct controller->action($args)
 */
class RouteResolver
{
    /**
     * Call a given class / method
     *
     * @param string $container
     * @param string $request
     * @param string $response
     * @param array $args
     *
     * @return \Psr\Http\Message\ResponseInterface
     * @throws \Exception
     */
    public function call($container, $request, $response, $args)
    {
        $args = explode('/', $args['args']);

        $reflection = $this->getControllerClass($args);

        $method = $this->getMethod($reflection, $args);
        if (!$method)
        {
            throw new \Exception('Method ' . $method->getName() . ' not found in ' . $reflection->getName());
        }

        $params['args'] = $args;

        $oClass = $reflection->newInstanceArgs([$container, $request, $response]);

        $ret = $method->invokeArgs($oClass, $params);

        if ($ret instanceof ResponseInterface) {
            $response = $ret;
        } elseif (is_string($ret) || is_numeric($ret)) {
            $response->write($ret);
        }

        return $response;
    }

    /**
     * Get the correct controller class
     *
     * @param $nameSpace
     * @param $route
     * @param $defaultRoute
     * @param $args
     *
     * @return \ReflectionClass
     */
    private function getControllerClass(&$args)
    {
        $reflection = false;

        // test if argument 1 is a controller
        if (@$args[1])
        {
            $reflection = $this->getReflection($args, 1);
        }

        // test if argument 0 is a controller
        if (@$args[0] && !$reflection) {
            $reflection = $this->getReflection($args, 0);
        }

        // get default controller
        if (!$reflection) {
            $reflection = $this->getReflection($args);
        }

        return $reflection;
    }

    /**
     * Try to load the reflection class of the controller
     *
     * @param $args
     * @param int $argNr
     *
     * @return bool|\ReflectionClass
     */
    private function getReflection(&$args, $argNr = -1)
    {
        if ($argNr == 1) {
            $domain = ucfirst($args[0]);
            $controller = ucfirst($args[1]);
        } elseif ($argNr == 0) {
            $domain     = ucfirst($args[0]);
            $controller = ucfirst($args[0]);
        }
        else {
            $domain = 'Frontend';
            $controller = 'Frontend';
        }

        $nameSpace = 'MyApp\Modules\\';
        $route = $domain . '\Controllers\\' . $controller . 'Controller';

        $fullControllerName = $nameSpace . $route;
        //var_dump($fullControllerName);

        try {
            $reflection = new \ReflectionClass($fullControllerName);

            for ($i = 0; $i <= $argNr; $i++){
                unset($args[$i]);
            }
            $args = array_values($args);
        } catch (\ReflectionException $e) {
            $reflection = false;
        }

        return $reflection;
    }

    /**
     * Try to load the method of the reflection class of the controller
     *
     * @param $reflection
     * @param $args
     *
     * @return bool|\ReflectionMethod
     */
    private function getMethod($reflection, &$args)
    {
        $func = false;
        if ($args) {
            $func = $args[0];
            unset($args[0]);
            $args = array_values($args);
        }

        if (!$func) {
            $func = 'index';    // default action in a controller
        }

        try {
            $method = $reflection->getMethod($func);
        } catch (\ReflectionException $e) {
            $method = false;
        }

        return $method;
    }
}

Dynamic Routing
#2

This might just be overcomplicating the system. This is how Codeigniter does it and this is one of the reason people moved away from it.


#3

For people who want to experiment with it, here a little more of what is does:

/something

  • this is not an existing module (the Modules/Something/Controllers/SomethingController does not exist)
    so it decides it’s a function of the default controller
    So in the Modules/Frontend/Controllers/FrontendController there should be a function called “somewhere”

/admin

  • this is an existing module (the Modules/Admin/Controllers/AdminController does exist)
    but no further argument was found, so the standard argument is "index"
    So in the Modules/Admin/Controllers/AdminController there should be a function called “index”

/admin/something

  • this is not an existing module (the Modules/Admin/Controllers/SomethingController does not exist)
    so it decides it’s a function of the AdminController (because that one does exist)
    So in the Modules/Admin/Controllers/AdminController there should be a function called “somewhere”

/admin/user

  • this is an existing module (the Modules/Admin/Controllers/UserController does exist)
    so it decides it’s a function of the UserController (because that one does exist)
    but no further argument was found, so the standard argument is "index"
    So in the Modules/Admin/Controllers/UserController there should be a function called “index”

/admin/user/list

  • this is an existing module (the Modules/Admin/Controllers/UserController does exist)
    so it decides it’s a function of the UserController (because that one does exist)
    the argument is "list"
    So in the Modules/Admin/Controllers/UserController there should be a function called “list”

/admin/user/edit/12

  • this is an existing module (the Modules/Admin/Controllers/UserController does exist)
    so it decides it’s a function of the UserController (because that one does exist)
    the argument is “edit” and 12
    So in the Modules/Admin/Controllers/UserController there should be a function called “edit” which receives the argument “12”