URL-маршрутизация, содержащая параметры - PullRequest
0 голосов
/ 04 февраля 2019

Я пытался создать маршрут, который принимает параметры для получения одного пользователя, но я пытаюсь понять, что я делаю неправильно, я застрял.

Вот маршруты: 1-й работаетбез каких-либо проблем:

<?php
$router->get('users', 'UsersController@index');
$router->get('users/about', 'UsersController@test');
$router->get('users/:id', 'UsersController@show');

Вот мой класс маршрутизатора, я сопоставляю URL и использую preg_replace, чтобы я мог получить идентификатор динамически

<?php

namespace App\Core;

class Router
{
    /**
     * All registered routes.
     *
     * @var array
     */
    public $routes = [
        'GET' => [],
        'POST' => []
    ];

    /**
     * Load a user's routes file.
     *
     * @param string $file
     */
    public static function load($file)
    {
        $router = new static;

        require $file;

        return $router;
    }

    /**
     * Register a GET route.
     *
     * @param string $uri
     * @param string $controller
     */
    public function get($uri, $controller)
    {
        $this->routes['GET'][$uri] = $controller;
    }

    /**
     * Register a POST route.
     *
     * @param string $uri
     * @param string $controller
     */
    public function post($uri, $controller)
    {
        $this->routes['POST'][$uri] = $controller;
    }


    /**
     * Load the requested URI's associated controller method.
     *
     * @param string $uri
     * @param string $requestType
     */
    public function direct($uri, $requestType)
    {
        $matches = [];

        foreach ($this->routes[$requestType] as $regex => $controller) {

            $pattern = "@^" . preg_replace('/\\\:[a-zA-Z0-9\_\-]+/', '([a-zA-Z0-9\-\_]+)', preg_quote($regex)) . "$@D";

            if ( preg_match($pattern, $uri, $matches ) ) {

                print_r($matches[0]);


                return $this->callAction(
                    ...explode('@', $this->routes[$requestType][$uri])
                );
            }
        }

        throw new Exception('No route defined for this URI.');
    }

    /**
     * Load and call the relevant controller action.
     *
     * @param string $controller
     * @param string $action
     */
    protected function callAction($controller, $action)
    {

        $controller = "App\\Controllers\\{$controller}";
        $controller = new $controller;

        if (! method_exists($controller, $action)) {
            throw new Exception(
                "{$controller} does not respond to the {$action} action."
            );
        }

        return $controller->$action();
    }
}

А в контроллере пользователя я простоесть функция, которая получает идентификатор и показывает мне пользователя на основе $ id

/**
 * Show selected user.
 */

public function show($id)

{
$id = array_slice(explode('/', rtrim($_SERVER['REQUEST_URI'], '/')), -1)[0];

$user = App::get('database')->get('users', [
    'id' => $id
]);

return view('user', compact('user'));
}

Если вам, ребята, нужно больше информации, я могу добавить весь код в кодовое перо.Спасибо

1 Ответ

0 голосов
/ 04 февраля 2019

В этом разделе (метод direct)

explode('@', $this->routes[$requestType][$uri])

Это должно быть

explode('@', $this->routes[$requestType][$regex])

Или просто (и предпочтительно):

explode('@', $controller)

какURI (для третьего) выглядит примерно так:

users/10
users/20

И фактический ключ: users/:id, который также является значением $regex (очевидно)

Код (Только для тестирования):

$routes = [
    'GET' => [
        'users'=>'UsersController@index',
        'users/about'=>'UsersController@test',
        'users/:id'=>'UsersController@show'
    ],
    'POST' => []
];

$requestType = 'GET';

$uri = 'users/10';

foreach ($routes[$requestType] as $regex => $controller) {
    $pattern = "@^" . preg_replace('/\\\:[a-zA-Z0-9\_\-]+/', '([a-zA-Z0-9\-\_]+)', preg_quote($regex)) . "$@D";

    if ( preg_match($pattern, $uri, $matches ) ) {

        print_r($matches[0]);
        echo "\n";
        print_r($routes[$requestType][$uri]);
        echo "\n";
        print_r($routes[$requestType][$regex]);
    }
}

Вывод:

  #$matches[0]
  users/10
  #with $uri as the key - $routes[$requestType][$uri]
  <b>Notice</b>:  Undefined index: users/10 in <b>[...][...]</b> on line <b>27</b><br />
  #with $regex as the key - $routes[$requestType][$regex]
  UsersController@show

Песочница

Также я думаю, что первый и второй должны работать, толькотот, с действительным регулярным выражением в качестве ключа, будет затронут из-за его «динамического» характера.

Другие вещи

Одна вещь, которую вам не хватает, это аргументы из URL, возьмите третий пример (users/10), как вы передаете этот идентификатор (10) в свой контроллер?Кроме того, если бы это был я, я бы нарушил зависимость, которая у вас есть в этой строке $controller = "App\\Controllers\\{$controller}";, так как она ограничивает вас использованием только классов пространства имен App\\Controllers\....

Итак, чтобы исправить это, измените структуру данных наудалите этот знак @.Поэтому вместо этого:

 $router->get('users', 'UsersController@index');

Сделайте это следующим образом:

 #Obj::class returns the fully qualified class name (includes namespace)
 # PHP 5.6+ I think?
 $router->get('users', [UsersController::class,'index']);

Что на самом деле упростит ваш код и даст вам возможность делать такие вещи (проще и гибче):

   $router->get('users', function(){
        //do something simple
   });
   #or
   $router->get('users', 'somefunction');
   #or (drop in plugins outside of your normal controller folder)
   $router->get('users', 'Plugins/Users/Controllers/User);

Таким образом, мы должны внести небольшие изменения:

public function direct($uri, $requestType)
{
    $matches = [];

    foreach ($this->routes[$requestType] as $regex => $controller) {

        $pattern = "@^" . preg_replace('/\\\:[a-zA-Z0-9\_\-]+/', '([a-zA-Z0-9\-\_]+)', preg_quote($regex)) . "$@D";

        if ( preg_match($pattern, $uri, $matches ) ) {
            //Simplify the code here and also pass the uri as an array
            return $this->callAction($controller, explode('/', $uri));
        }
    }

    throw new Exception('No route defined for this URI.');
}

protected function callAction($controller, array $args=[])
{
    //you can check types here but all callables work with call_user_func & call_user_func_array

    //you may be able to just check !is_callable($controller) for them all if you don't need the granularity

    if(is_array($controller)){
        //[object, method]
        //[class name, method]
         if(!class_exists($controller[0]) || !method_exists($controller[0], $controller[1])){ 
            //the router has a direct interface to the end user
            //because of this it must handle requests to bad URLs and such
            //direct to 404 page, for example something like this
            //you can and should "log" the errors, but don't show them
            // ---- return $this->error404();
         }  
    }else if(is_object($controller) && !is_callable($controller)){
         //closure or magic method __invoke
          // ---- return $this->error404();
    }else if( !function_exists($controller) ){
         //standard functions
          // ---- return $this->error404();
    }

    return call_user_func_array($action, $args);
}

При этой простой настройке передаются все аргументы, включая имя контроллера, если он является частью URL.Например, использование третьего маршрута со значением этого users/10 вызовет

  $UsersController->show('users', '10');

Может быть сложно удалить это, не вставляя «метод» в путь маршрута: например,

  $router->get('users/about', 'UsersController@test');

Нет способа «узнать», важны ли «пользователи» для метода «теста».Теперь, если они совпадают:

  $router->get('test/about', 'UsersController@test');

Вы можете удалить его.Обычно я видел этот шаблон в URL-адресах

   www.yoursite.com/controller/method/...args

, который дает нам своего рода "грантополучатель" в отношении того, какие части.Но это ваш код, вы можете просто решить, что можете отказаться от первого, несмотря ни на что ...

Я должен отметить, что я не тестировал ни один из приведенных выше кодов, но, исходя из моего опыта, эти функции вывероятно, захочет в какой-то момент.

Ура!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...