URL包含路由参数

问题描述 投票:-1回答:1

我一直在试图创建一个接受参数,以获得单个用户的路线,但我努力理解我在做什么错了,我卡住了。

下面是路线:一日一工作没有任何问题:

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

这里是我的路由器类,我相匹配的网址,并使用了preg_replace,所以我可以动态获取ID

<?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,并显示我的用户基础上的$ 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'));
}

如果你们需要更多的相关信息,我可以添加整个代码到代码笔。谢谢

php url-routing
1个回答
0
投票

在本节中(方法direct

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

这应该是

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

或简单地(和优选的):

explode('@', $controller)

作为URI(为的3号)是这样的:

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

Sandbox

此外,我想象中的第一和第二个应该工作,只有一个与实际正则表达式为重点,由于它的“动态”自然会受到影响。

Other Stuffs

你缺少的一件事是从URL参数,从第三个例子(users/10)你怎么传递ID(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);
}

有了这个简单的设置所有的ARG游戏通过包括如果它是URL的一部分控制器的名称。对于为例,使用这个users/10值第三路线将调用

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

可以证明具有挑战性的,以去除不烘烤该“方法”到路由路径:例如

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

有没有办法“知道”,如果“用户”是“测试”的方法很重要。现在,如果他们匹配:

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

你可以将其删除。通常,我已经看到了网址,这种模式

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

这给了我们一种“受让人”的以部分是什么。但它是你的代码,你可能只是决定你可以放弃第一个不管是什么...

我要提到我没有测试任何上述代码的,但根据我的experiance这些都是你可能会希望在某些时候的特点。

干杯!

© www.soinside.com 2019 - 2024. All rights reserved.