简体   繁体   English

Laravel 8 路由到控制器。 SEO友好的URL结构

[英]Laravel 8 routes to controllers. SEO friendly URL structure

I am trying to figure out how to achieve a specific URL structure in a Laravel 8 project and the necessary route to achieve this.我试图弄清楚如何在 Laravel 8 项目中实现特定的 URL 结构以及实现这一目标的必要途径。 What I want is:我想要的是:

// 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

This is because we don't want the strings “directory” or “blog” contained in the url for every listing or blog post.这是因为我们不希望每个列表或博客文章的 url 中都包含字符串“directory”或“blog”。 Search engine results work better without it.没有它,搜索引擎结果会更好。

So far I am using a catch-all route {any} at the bottom of the web.php routes file to “catch all” routes that get that far.到目前为止,我在 web.php 路由文件的底部使用了一个包罗万象的路由 {any} 来“捕获所有”到达那一步的路由。 I then manipulate the string provided by the path to get the ID and single character token from the end of the urls.然后我操作路径提供的字符串以从 url 的末尾获取 ID 和单个字符标记。 I then have these 2 variables but can figure out how to pass these onto the right controllers!然后我有这两个变量,但可以弄清楚如何将它们传递给正确的控制器!

Or am I being really dumb and there is a much better way of achieving this?还是我真的很笨,有更好的方法来实现这一目标?

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;
    }

});

I would split the path into 2 variables ( $slug and $id ) and directly pass it to the controller.我会将路径拆分为 2 个变量( $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+)']);

And in your controllers在你的控制器中

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

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

I can see two ways of achieving this result:我可以看到实现这一结果的两种方法:

Create an intermediate controller创建一个中间控制器

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

Then in your CheckPathController you do all the checks and your call the proper controller action:然后在您的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);
}

Complex regex复杂的正则表达式

see: https://laravel.com/docs/8.x/routing#parameters-regular-expression-constraints见: https : //laravel.com/docs/8.x/routing#parameters-regular-expression-constraints

I'm not a regexp master, this should be a basic was to match any {word}-{word}-...-p{id}.html pattern but it will break in case of unexpected chars我不是正则表达式大师,这应该是一个基本的匹配任何{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$']);

Note that in this case, you controller will receive the pull $path string, so you will need to extract the id there.请注意,在这种情况下,您的控制器将收到 pull $path字符串,因此您需要在那里提取 id。

You can match the slug using regex您可以使用正则表达式匹配 slug

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

Repeated with pp重复

Then when you pickup your $site in your controller method you can use regex to grab the site.然后,当您在控制器方法中获取 $site 时,您可以使用正则表达式来获取站点。

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

OR或者

This will give your controller method d{number} or p{number}这会给你的控制器方法 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('App\Http\Controllers\Application\\' . $controllerName);

    return $controller->callAction('methodName', $params = ['code' => $code]);
})->where(['site' => '.*(-(d|p)(.*?)\.).*']);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM