H
H
Human2020-02-10 01:20:28
PHP
Human, 2020-02-10 01:20:28

How to correct Router?

Good evening, evaluate a simple router and is it worth adjusting it?

Router class:

<?php

namespace app\system;

class Router
{
  private $classConfig;
  
  function __construct()
  {
    $this->classConfig = new Config; // Класс для работы с конфигурацией
  }

  public function loadRoutes()
  {
    $arr = $this->classConfig->getConfigs('routes'); // Вывод роутеров (Ниже пример)
    if ($arr) {
      foreach ($arr as $key => $value) {
            	$route = '#^' . $key . '$#';
            	$this->routes[$route] = $value;
        	}
    }

    $url = trim($_SERVER['REQUEST_URI'], '/');
        foreach ($this->routes as $route => $params) {
            if (preg_match($route, $url, $matches)) {
                $this->params = $params;
                if (isset($this->params['folder'])) {
                	$this->params['folder'] = '\\' . ucfirst($this->params['folder']);
                }
                return true;
            }
        }
       	return false;
  }

  public function runRouter()
  {
    if ($this->loadRoutes()) {
      $path = 'app\controllers' . $this->params['folder'] . '\\' . ucfirst($this->params['controller']) . 'Controller';
      $action = $this->params['action'] . 'Action';
      if (class_exists($path) and method_exists($path, $action)) {
        $controller = new $path($this->params);
        $controller->$action();
      } else {
        echo "<b>" . ucfirst($this->params['controller']) . "Controller</b> or <b>" . $action . "</b> not found.";
        return false;
      }
    } else {
      echo "404";
    }
  }
}


Router file:
return [
  '' => [
    'controller' => 'main',
    'action' => 'index',
  ],

  'admin' => [
    'folder' => 'admin',
    'controller' => 'admin',
    'action' => 'index',
  ],
];


Actually questions:
1. Why (why) do I prefer to store routers in files (in a database), if it can be automated?
2. Do I need to correct the code above or will it work?
3. Which is better: load the controller from the router or load the controller class (checks...) that will load the required controller?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
E
Evgeny Romashkan, 2020-02-10
@rafxxxa

private $classConfig;

Use Typed Parameters
$this->classConfig = new Config;

Use DI and interfaces, see below for more details.
$arr = $this->classConfig->getConfigs('routes'); // Вывод роутеров (Ниже пример)
    if ($arr) {
      foreach ($arr as $key => $value) {
            	$route = '#^' . $key . '$#';
            	$this->routes[$route] = $value;
        	}
    }

Badly. Read about GRASP patterns, especially Information expert and, for the future - Coupling / Cohesion (quite, completely for the future, although you can look at the report - tyk , I think it will do).
You can do without regular expressions.
Information should be where the data is, no need to get the data from some object to make a decision based on it - ask the data object to make a decision.
Alternatively, instead, some ControllerInfoProvider, or RoutesProvider, should be injected (via the interface), with a method like getControllerInfo($requestData);, which will return a DTO instance like
/**
 * @psalm-immutable
 */
class ControllerInfo {
    public string $controllerClass;

    public string $controllerMethod;

    /**
     * @var string[]
     * @psalm-var class-string[]
     */
    public array $beforeControllerMiddlewares;

    /**
     * @param string[] $beforeControllerMiddlewares
     * @psalm-param class-string[] $beforeControllerMiddlewares
     */
    public function __construct(string $controllerClass, string $controllerMethod, array $beforeControllerMiddlewares = [])
    {
        $this->controllerClass = $controllerClass;
        $this->controllerMethod = $controllerMethod;
        $this->beforeControllerMiddlewares = $beforeControllerMiddlewares;
    }
}

Well, you can also postControllerMiddlewares, or, with a more understandable name like ResposeAwareMiddlewares, because middleware is good .
There are also annotations for the psalm static analyzer , because catching errors at the development stage by running ./vendor/bin/psalmis better than catching them after running the code, especially in production.
However, in fact, this is a bit of an overhead for you, but someday it’s all worth dealing with.
$url = trim($_SERVER['REQUEST_URI'], '/');
foreach ($this->routes as $route => $params) {
if (preg_match($route, $url, $matches)) {
$this->params = $params;
if (isset($this->params['folder'])) {
$this->params['folder'] = '\\' . ucfirst($this->params['folder']);
}
return true;
}
}
return false;

No need to parse global variables in the Router. Let some RequestParser process this matter before the router, and create a DTO request object, you can use psr-7 , you can without it, to make it easier. It's not critical.
As for the parameters in the controller, you should pay attention to argument resolvers , and don't do it in the router. The thing is excellent and convenient, its argument resolvers are also written simply.
echo "<b>" . ucfirst($this->params['controller']) . "Controller</b> or <b>" . $action . "</b> not found.";
        return false;

It's not the router's business, write to stdout. Let it throw an exception, and the response to the user will be generated by the level above.
$path = 'app\controllers' . $this->params['folder']

The router should not define the directory structure. Use the fully qualified name in the routes config, i.e. SomeController::class.
Dumping all the controllers into one folder is a dubious occupation.
Why (why) do I prefer to store routers in files (in a database), if I can automate

Keep it as convenient as possible. Config, base, annotations, php array - no difference
Do I need to adjust the code above or will it work?

The more you do from the above, the better.
Which is better: load the controller from the router or load the controller class (checks...) that will load the required controller?

Got ControllerInfo, turned to ControllersRegistry->getControllerClass(), got the controller instance.
Next, they either called the desired method, or, if they did, they asked the resolver argument to parse and initialize the arguments:
$args = $this->argumentsResolver->resolve($controller, $method, $request);

Called the middleware, something like this: https://github.com/thephpleague/tactician/blob/mas... , called the controller:
$response = $controller->{$action}(... $args)

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question