我一直在尝试创建一个接受参数的路由来获取单个用户,但是我一直在努力了解自己做错了什么,我被困住了。
以下是路线: 第一个作品没有任何问题:
<?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'));
}
如果你们需要更多信息,我可以将整个代码添加到代码笔中。谢谢
答案 0 :(得分: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
我还想象第一个和第二个应该工作,只有以实际正则表达式为键的那个才会受到影响,因为它具有“动态”特性。
您缺少的一件事是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);
}
通过这种简单的设置,所有参数都将传递,包括控制器名称(如果它是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
这给了我们关于零件是什么的“保证”。但这是您的代码,无论什么情况,您都可以决定放弃第一个代码。
我应该提到我没有测试以上任何代码,但是根据我的经验,这些是您可能会需要的功能。
干杯!