Как подкорректировать Router?

Добрый вечер, оцените простенький роутер и стоит ли его корректировать?

класс Router:
<?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";
		}
	}
}


Файл с роутерами:
return [
	'' => [
		'controller' => 'main',
		'action' => 'index',
	],

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


Собственно вопросы:
1. Почему (зачем) предпочитаю хранить роутеры в файлах (в базе данных), если можно автоматизировать?
2. Нужно ли корректировать код, указанный выше или и так сойдет?
3. Что лучше: загружать контроллер из роутера или загружать класс контроллер (проверки...), который будет загружать необходимый контроллер?
  • Вопрос задан
  • 180 просмотров
Решения вопроса 1
@EvgeniiR
https://github.com/EvgeniiR
private $classConfig;

Использовать типизованные параметры

$this->classConfig = new Config;

Использовать DI и интерфейсы, ниже подробнее.

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

Плохо. Почитайте про GRASP паттерны, особенно Information expert и, на будущее - Coupling/Cohesion(совсем-совсем на будущее, хотя можете глянуть докладик - тык, думаю сойдёт).
Без регулярных выражений можно обойтись.
Информация должна быть там где данные, не нужно доставать данные из какого-то объекта чтобы принять решение на основе их - попросите объект с данными принять решение.

Как вариант - вместо этого должен инжектиться (по интерфейсу) какой-нибудь ControllerInfoProvider, или RoutesProvider, с методом вроде getControllerInfo($requestData);, который вернёт экземпляр DTO вроде
/**
 * @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;
    }
}

Ну и ещё можно postControllerMiddlewares, или, с более понятным названием вроде ResposeAwareMiddlewares, потому что миддлвари это хорошо.
Ещё там аннотации для статического анализатора psalm, потому что ловить ошибки на этапе разработки запустив ./vendor/bin/psalm лучше чем ловить их после запуска кода, особенно в продакшене.

Впрочем, на самом деле это чутка оверхед для вас, но когда-нибудь разобраться с этим всем стоит.

$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;

Не нужно в Роутере глобальные переменные парсить. Пусть до роутера это дело обработает какой-нибудь RequestParser, и создаст DTO-объект запроса, можно по psr-7, можно без него, чтобы проще было. Это не критично.

По поводу параметров в контроллере - стоит обратить внимание на argument resolvers, и не заниматься этим в роутере. Вещь отличная и удобная, свои аргумент резолверы тоже пишутся просто.

echo "<b>" . ucfirst($this->params['controller']) . "Controller</b> or <b>" . $action . "</b> not found.";
        return false;

Не роутера это дело, в stdout писать. Пусть кинет исключение, а ответ пользователю сформируется уровнем выше.

$path = 'app\controllers' . $this->params['folder']

Роутер не должен определять структуру директорий. Используйте полное имя в конфиге роутов, т.е. SomeController::class.
Сваливать все контроллеры в одну папочку занятие сомнительное.

Почему (зачем) предпочитаю хранить роутеры в файлах (в базе данных), если можно автоматизировать

Как удобнее так и храните. Конфиг, база, аннотации, php массив - без разницы

Нужно ли корректировать код, указанный выше или и так сойдет?

Чем больше сделаете из описанного выше - тем лучше.

Что лучше: загружать контроллер из роутера или загружать класс контроллер (проверки...), который будет загружать необходимый контроллер?

Получили ControllerInfo, обратились в ControllersRegistry->getControllerClass(), получили инстанс контроллера.
Далее либо вызвали нужный метод, либо, если всё же сделали - попросили аргумент резолвер распарсить и инициализировать аргументы:
$args = $this->argumentsResolver->resolve($controller, $method, $request);

Вызвали миддлвари, что-то вроде этого: https://github.com/thephpleague/tactician/blob/mas... , вызвали контроллер:
$response = $controller->{$action}(... $args)
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы