Laravel 8路控制器.SEO友好的URL结构



我正试图弄清楚如何在Laravel 8项目中实现特定的URL结构,以及实现这一点的必要途径。我想要的是:

// Example urls to listings in the business directory.
// These urls should be routed to the directory controller.
www.domain-name.com/example-business-name-d1.html
www.domain-name.com/example-business-name-d15.html
www.domain-name.com/example-business-name-d100.html
www.domain-name.com/example-business-name-d123.html
www.domain-name.com/example-business-name-d432.html
// Example urls to articles/posts in the blog.
// These urls should be routed to the posts controller.
www.domain-name.com/example-post-name-p5.html
www.domain-name.com/example-post-name-p11.html
www.domain-name.com/example-post-name-p120.html
www.domain-name.com/example-post-name-p290.html
www.domain-name.com/example-post-name-p747.html
// We want to avoid the more traditional:
www.domain-name.com/directory/example-business-name-1.html
www.domain-name.com/blog/example-post-name-5.html

这是因为我们不希望每个列表或博客文章的url中都包含字符串"directory"或"blog"。没有它,搜索引擎的结果会更好。

到目前为止,我正在使用web.php路由文件底部的一个catch-all路由{any}来"捕捉所有"到达那一步的路由。然后,我操作路径提供的字符串,从url的末尾获取ID和单字符令牌。然后我有这两个变量,但可以找出如何将它们传递给正确的控制器!

还是我真的很笨,有更好的方法来实现这一点?

Route::get('{any}', function($any = null){
// Break up the url into seperate parts.
$pieces = explode("-", $any);
$pieces = array_reverse($pieces);
$piece =  $pieces[0];
// Remove the .html
$piece = substr($piece, 0, -5);
// Get the two parts of the identifier.
$id = substr($piece, 1);
$token = substr($piece, 0, 1);
// Call correct controller based on the token.
switch ($token) {
case "d":
// HERE I WANT TO PASS THE ID ON TO THE SHOW ACTION OF THE DIRECTORY CONTROLLER 
break;
case "p":
// HERE I WANT TO PASS THE ID ON TO THE SHOW ACTION OF THE POSTS CONTROLLER 
break;
default:
return abort(404);
break;
}
});

我会将路径拆分为两个变量($slug$id(,并直接将其传递给控制器。

Route::get('{slug}-d{id}.html', 'DirectoryController@show')
->where(['slug' => '([a-z-]+)', 'id' => '(d+)']);
Route::get('{slug}-p{id}.html', 'PostController@show')
->where(['slug' => '([a-z-]+)', 'id' => '(d+)']);

在你的控制器

class DirectoryController
{
public function show(string $slug, int $id) {}
}
class PostController
{
public function show(string $slug, int $id) {}
}

我可以看到实现这一结果的两种方法:

创建一个中间控制器

Route::get('{path}', 'CheckPathController@redirect')

然后在您的CheckPathController中,您进行所有检查并调用正确的控制器操作:

public function redirect(Request $request, $path) {
// Your checks on $path, extract $id and content type
if($isPost) {
$controller = resolve(PostController::class);
return $controller->show($request, $id);
}
if($isBusiness) {
$controller = resolve(BusinessController::class);
return $controller->show($request, $id);
}
// No matches, error 404
abort(404);
}

复杂正则表达式

请参阅:https://laravel.com/docs/8.x/routing#parameters-正则表达式约束

我不是regexp大师,这应该是一个基本的匹配任何{word}-{word}-...-p{id}.html模式,但它会在出现意外字符的情况下中断

Route::get('{path}', 'PostController::show')
->where(['path' => '([w]*-)*p[0-9]+.html$']);
Route::get('{path}', 'BusinessController::show')
->where(['path' => '([w]*-)*d[0-9]+.html$']);

请注意,在这种情况下,控制器将接收pull$path字符串,因此需要在那里提取id。

您可以使用regex 匹配段塞

Route::get('/{any}', 'YourController@methodName')->where(['any' => '.*(-d(.*?).).*']);

p重复

然后,当您在控制器方法中获取$站点时,您可以使用regex来获取该站点。

public function methodName($site)
{
preg_match('/.*(-(d(.*?)).).*/', $site, $parts); //or something similar, $parts[2] will have what you want
}

这将给您的控制器方法d{number}或p{number}

Route::get('/{site}', function($site) {
$code = preg_match('/.*(-(d(.*?)|p(.*?)).).*/', $site, $parts) ? $parts[2] : null;
$controllerName = 'ControllerA';
if(isset($code) && !is_null($code) && Str::contains($code, 'p')) {
$controllerName = 'ControllerB';
}
$controller = app()->make('AppHttpControllersApplication\' . $controllerName);
return $controller->callAction('methodName', $params = ['code' => $code]);
})->where(['site' => '.*(-(d|p)(.*?).).*']);

最新更新