多个路由到同一个 Laravel 资源控制器操作

Posted

技术标签:

【中文标题】多个路由到同一个 Laravel 资源控制器操作【英文标题】:Multiple routes to same Laravel resource controller action 【发布时间】:2014-08-07 12:40:14 【问题描述】:

我喜欢在 Laravel 中使用资源控制器,因为它让我想到了数据建模。到目前为止,我已经过关了,但我现在正在开发一个具有公共前端和受保护后端(管理区域)的网站。

我创建了一个添加“admin”前缀的路由组,如下所示:

Route::group(array('before' => 'auth', 'prefix' => 'admin'), function()

    Route::resource('article', 'ArticleController');
    Route::resource('event', 'EventController');
    Route::resource('user', 'UserController');    
);

我可以使用默认的 URL 结构访问方法,即 http://example.com/admin/article/1/edit

但是,我希望在前端使用一个不同的 URL 结构,这不符合资源控制器的期望。

例如,要访问一篇文章,我想使用如下 URL:http://example.com/news/2014/06/17/some-article-slug。如果这篇文章的 ID 为 1,它应该(在后台)转到 /article/1/show

如何在 Laravel 中实现这一点?在那里,我可以对路线进行某种预处理,以将日期和 slug 与文章 ID 匹配,然后然后将其作为参数传递给我的资源控制器的 show() 方法?

【问题讨论】:

你想出答案了吗?另外,你考虑过路由过滤器吗?? @brianlmerritt 不要以为我解决了我在问题中谈到的 Laravel 4 应用程序,但对于 Laravel 5 应用程序,我已经开始使用路由参数(可能包含斜杠),然后在此基础上找到一个模型,因此“2014/06/17/some-article-slug”可以传递给我的Article 模型中的静态findBySlug() 方法,然后在斜线处拆分它,并找到一个根据日期和 slug 匹配记录。 【参考方案1】:

重新访问这个,我通过使用路由模型绑定和模式解决了它:

$year = '[12][0-9]3';
$month = '0[1-9]|1[012]';
$day = '0[1-9]|[12][0-9]|3[01]';
$slug = '[a-z0-9\-]+';

// Pattern to match date and slug, including spaces
$date_slug = sprintf('(%04d)\/(%02d)\/(%02d)\/(%s)', $year, $month, $day, $slug);

Route::pattern('article_slug', $date_slug);

// Perform the route–model binding
Route::bind('article_slug', function ($slug) 
    return Article::findByDateAndSlug($date_slug);
);

// The actual route
Route::get('news/article_slug', 'ArticleController@show');

然后根据需要将Article 模型实例注入到我的控制器操作中。

【讨论】:

【参考方案2】:

如果 Laravel 4 在路由中支持 (:all) 似乎可以轻松做到,但不幸的是 (:all) is not supported in Laravel 4。

不过,Laravel 4 允许通过正则表达式检测路由,所以我们可以使用->where('slug', '.*')

routes.php:(文件底部)

Route::get('slug', 'ArticleController@showBySlug')->where('slug', '.*');

由于 Laravel 会首先尝试匹配 routes.php 中最顶层的路由,所以我们可以安全地将通配符路由放在 routes.php 的底部,以便只有在所有其他条件都已经评估后才检查它。

ArticleController.php:

class ArticleController extends BaseController

    public function showBySlug($slug)
    
        // Slug lookup. I'm assuming the slug is an attribute in the model.
        $article_id = Article::where('slug', '=', $slug)->pluck('id');

        // This is the last route, throw standard 404 if slug is not found.
        if (!$article_id) 
            App::abort(404);
        

        // Call the controller's show() method with the found id.
        return $this->show($article_id);
    

    public function show($id)
    
        // Your resource controller's show() code goes here.
    

上面的代码假定您将整个 URI 存储为 slug。当然,您始终可以定制 showBySlug() 以支持更高级的 slug 检查。

额外:

你也可以这样做:

Route::get('category/year/slug', 'ArticleController@showBySlug')->where('slug', '.*');

你的 showBySlug() 只会有额外的参数:

public function showBySlug($category, $year, $slug)

    // code

显然你可以扩展到月和日,或其他适应。

【讨论】:

谢谢。我希望有一个解决方案,我可以重用我的资源控制器的show() 方法,而不是定义一个新方法。我可以这样做,但我觉得它不“正确”。【参考方案3】:

一个简单的解决方案是为您的要求再创建一条路线,并在那里进行处理以将其链接到主路线。所以,例如:

//routes.php 
Route::get('/arical/date/indentifier/slug', array (
'uses' => 'ArticleController@findArticle'
  ));

//ArticleContoller

public function findArticle($date,$slug)
$article = Article::where('slug','=','something')->first(); //maybe some more processing;
$article_id = $article->id;
/*
Redirect to a new route or load the view accordingly
*/ 

希望这是有用的。

【讨论】:

谢谢。我希望有一个解决方案,我可以重用我的资源控制器的show() 方法,而不是定义一个新方法。我可以这样做,但我觉得它不“正确”。

以上是关于多个路由到同一个 Laravel 资源控制器操作的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 资源路由命名前缀

多个控制器的单个 Laravel 路由

Laravel 5.7 使用某些命名空间的资源控制器路由名称是啥?

Laravel 路由到控制器动作

路由资源更新和删除无法使用除存储 laravel 8 之外的多个角色

如何在 laravel 的单个控制器中传递多个路由