Как работает маршрутизация MVC?


Итак, я начал изучать MVC (реальный MVC, а не фреймворк MVC) немного более углубленно, и я пытаюсь разработать небольшой фреймворк. Я работаю, читая другие фреймворки, такие как Symphony и Zend, наблюдая, как они выполняют свою работу, и пытаюсь реализовать ее самостоятельно.

Местом, где я застрял, была система маршрутизации URL-адресов:

<?php
namespace Application\Common;

class RouteBuilder {

    public function create($name, $parameters) {
        $route           = new Route($name);
        $route->resource = array_keys($parameters)[0];
        $route->defaults = $parameters["defaults"];
        $notation        = $parameters["notation"];
        $notation = preg_replace("/\[(.*)\]/", "(:?$1)?", $notation);
        foreach ($parameters["conditions"] as $param => $condition) {
            $notation = \str_replace($param, $condition, $notation);
        }

        $notation = preg_replace("/:([a-z]+)/i", "(?P<$1>[^/.,;?\n]+)", $notation);

        //@TODO: Continue pattern replacement!!
    }
}
/* How a single entry looks like
 * "main": {
    "notation": "/:action",
    "defaults": {
        "resource"  :   "Authentication",
    },
    "conditions":   {
        ":action"   :   "(login)|(register)"
    }
},

 */

Я просто не могу как следует разобраться в этом. Каков рабочий процесс приложения отсюда?

Шаблон сгенерирован, вероятно, объект Route, который будет храниться под объектом Request или что-то в этом роде, тогда что? Как это работает?

P.S. Ищу реальный, хорошо объясненный ответ здесь. Я действительно хочу понять эту тему. Я был бы признателен, если бы кто-нибудь нашел время написать по-настоящему подробный ответ.

Author: PeeHaa, 2012-09-14

2 answers

Класс Router (или Dispatcher, как некоторые его назвали бы) проверяет URL-адрес HTTP-запроса и пытается сопоставить его составляющие компоненты с конкретным Controller и методом (т.е. действием или командой), определенным в этом контроллере. Он также передает аргументы нужному методу Controller, если таковые имеются в URL-адресе.

Стандартный URL-адрес: Формат строки запроса

В HTTP-сервере Apache, без использования mod_rewrite, URL в HTTP-запросе вероятно, будет в формате строки запроса:

http://localhost/index.php?route=news/economics/param1/param2

Переписанный URL-адрес: Желаемый формат

URL-адрес после перезаписи веб-сервером, как правило, выглядит следующим образом:

http://localhost/news/economics/param1/param2

Без перезаписи URL-адреса вам понадобится класс, который разрешает параметр route в жале запроса из стандартного URL-адреса. Это первое, что может сделать класс Routing. В этом случае он разрешает param следующим образом:

  • контроллер = новостной контроллер
  • метод = экономика()
  • параметры: [парам1, парам2]

Если все пойдет хорошо, произойдет нечто подобное:

$controller = new NewsController();
$controller->economics([param1, param2])

Класс Router создает экземпляр запрошенного конкретного дочернего элемента Controller, вызывает запрошенный метод из экземпляра контроллера и передает методу контроллера его аргументы (если таковые имеются).

Теперь класс, который вы показываете, разрешает запрошенный "маршрут" к нужному контроллеру/действию. Так, например, URL-адрес ниже:

http://localhost/index.php?route=news/economics/param1/param2

... является английским URL-адресом. Отсюда и слово новости в строке запроса. Предположим, вы хотите, чтобы URL-адрес работал на голландском языке.

http://localhost/index.php?route=nieuws/economie/param1/param2

Это означало бы, что конкретный Controller будет называться nieuwsController.php, но его не существует. Вот где ваш пример вступает в игру: класс RouteBuilder.

1) Ваш класс Router должен сначала проверить, существует ли конкретный Controller, который он может создать (используя имя, указанное в URL, плюс слово "Контроллер"). Если контроллер найден, проверьте наличие запрошенного метода (действие).

2) Если Router не может найти и загрузить необходимый PHP во время выполнения (рекомендуется использовать автозагрузчик ) для создания экземпляра конкретного дочернего элемента Controller, он должен затем проверить массив (обычно находящийся в другом имени класса Route), чтобы увидеть, соответствует ли запрошенный URL , используя регулярные выражения, любому из элементов, содержащихся внутри. Основной скелет следует класс Route.

Примечание: .*? = Ноль или более любого символа, без захвата.

class Route
{
    private $routes = array(
        array(
            'url' => 'nieuws/economie/.*?', // regular expression.
            'controller' => 'news',
            'action' => 'economie'
        ),
        array(
            'url' => 'weerbericht/locatie/.*?', // regular expression.
            'controller' => 'weather',
            'action' => 'location'
        )
    );

    public function __contstruct()
    {

    }

    public function getRoutes()
    {
        return $this->routes;
    }
}

Зачем использовать регулярное выражение? Маловероятно, что после второго прямого слэша в URL-адресе будет выполнено надежное сопоставление данных.

/controller/method/param1/param2/..., где param[x] может быть чем угодно!

Предупреждение: Рекомендуется изменить разделитель шаблонов регулярных выражений по умолчанию ('/'), если целевые данные содержат разделитель шаблонов (в в этом случае косая черта '/'. Почти любой недопустимый символ URL-адреса был бы отличным выбором.

Метод класса Router будет перебирать массив Route::routes, чтобы проверить, соответствует ли регулярное выражение целевому URL-адресу и string значение, связанное с индексом 2-го уровня url. Если найдено совпадение, Router затем знает, какой конкретный Controller создать экземпляр и последующий метод для вызова. Аргументы будут переданы методу по мере необходимости.

Всегда будьте осторожны с крайними случаями, такими как URL-адреса, представляющие следующее.

`/`   // Should take you to the home page / HomeController by default
`''`  // Should take you to the home page / HomeController by default
`/gibberish&^&*^&*%#&(*$%&*#`   // Reject
 27
Author: w00, 2018-07-18 23:59:24

Класс маршрутизатора из моей платформы. Код рассказывает историю:

class Router
{
  const default_action = 'index';
  const default_controller = 'index';

  protected $request = array();

  public function __construct( $url )
  {
    $this->SetRoute( $url ? $url : self::default_controller );
  }

  /*
  *  The magic gets transforms $router->action into $router->GetAction();
  */
  public function __get( $name )
  {
    if( method_exists( $this, 'Get' . $name ))
      return $this->{'Get' . $name}();
    else
      return null;
  }

  public function SetRoute( $route )
  {
    $route = rtrim( $route, '/' );
    $this->request = explode( '/', $route );
  }

  private function GetAction()
  {
    if( isset( $this->request[1] ))
      return $this->request[1];
    else
      return self::default_action;
  }

  private function GetParams()
  {
    if( count( $this->request ) > 2 )
      return array_slice ( $this->request, 2 );
    else
      return array();
  }

  private function GetPost()
  {
    return $_SERVER['REQUEST_METHOD'] == 'POST';
  }

  private function GetController()
  {
    if( isset( $this->request[0] ))
      return $this->request[0];
    else
      return self::default_controller;
  }

  private function GetRequest()
  {
    return $this->request;
  }
 3
Author: JvdBerg, 2012-09-15 11:51:25