Automatic Routing (attempt 2)

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;
    }
}
1 Like

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.

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”
1 Like