我一直在试图创建一个接受参数,以获得单个用户的路线,但我努力理解我在做什么错了,我卡住了。
下面是路线:一日一工作没有任何问题:
<?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'));
}
如果你们需要更多的相关信息,我可以添加整个代码到代码笔。谢谢
在本节中(方法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);
}
有了这个简单的设置所有的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这些都是你可能会希望在某些时候的特点。
干杯!