【问题标题】:laravel filter group routes with same namelaravel 过滤组同名路由
【发布时间】:2025-12-18 00:00:03
【问题描述】:

我在我的 laravel 4 项目中遇到了这个烦人的问题,该项目有三种类型的用户,即学生、教师和版主(我使用 Entrust 作为角色管理解决方案)。

他们每个人都可以浏览相同的路线,但根据用户类型,应该调用另一个方法。所以我的 route.php 文件的结构是这样的:

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


Route::group(array('before' => 'teacher'), function(){
        Route::get('/tasks',array('as'=>'tasks','uses'=>'TasksController@tasksAsTeacher'));
        Route::get('/task/{id}',array('as'=>'showTask','uses'=>'TasksController@showTaskAsTeacher'));



});
Route::group(array('before' => 'moderator'), function(){
        Route::get('/tasks',array('as'=>'tasks','uses'=>'TasksController@tasksAsModerator'));
        Route::get('/task/{id}',array('as'=>'showTask','uses'=>'TasksController@showTaskAsModerator'));

});     
Route::group(array('before' => 'student'), function(){

        Route::get('/tasks',array('as'=>'tasks','uses'=>'TasksController@tasksAsStudent'));
        Route::get('/task/{id}',array('as'=>'showTask','uses'=>'TasksController@showTaskAsStudent'));




});

});

但是,使用教师或版主帐户浏览这些路线总是返回 404 错误。我发现这是因为在另外两个过滤器组中重新定义了路由。

因此,如果我将教师用户重定向到“showTask”,laravel 会将任务作为学生的路由返回,因为这是最后一次重新定义“showTask”路由,我会收到 404 错误。

我现在的问题是:处理此错误的最佳方法是什么?

我希望这不会太混乱。提前致谢!

【问题讨论】:

  • 这些过滤器在哪里?这可能是 Entrust 问题,而不是 laravel 的问题
  • @SteveBauman in app/filters.php

标签: php laravel laravel-4 laravel-routing


【解决方案1】:

将辅助路由组移出主身份验证组,然后使用管道命令在每个组之前运行身份验证,例如

Route::group(array('before' => 'auth|teacher'), function(){
Route::group(array('before' => 'auth|moderator'), function(){

【讨论】:

    【解决方案2】:

    我不确定您的方法是否是完成此任务的好方法。我认为两次定义相同的路线不是一个好习惯,但我不知道这是不是真的。

    解决此问题的一种方法可能是您只定义两个路由,并让控制器根据用户的角色决定执行哪个操作。这不是您的问题的直接解决方案,而是另一种处理让不同角色执行不同控制器操作的问题的方法。

    Route::group(array('before' => 'auth'), function(){
    
         Route::get('/tasks',array('as'=>'tasks','uses'=>'TasksController@tasks'));
         Route::get('/task/{id}',array('as'=>'showTask','uses'=>'TasksController@showTask'));
    
    });
    

    然后在您的 TasksController 中,将方法设置为 tasks 和 showTask 之类的东西

    class TasksController extends BaseController {
      public function tasks() {
        if(Entrust::hasRole('teacher')) {
          return $this->tasksAsTeacher();
        } else if(Entrust::hasRole('moderator')) {
          return $this->tasksAsModerator();
        } else if(Entrust::hasRole('student')) {
          return $this->tasksAsStudent();
        }
      }
    
      public function showTask($id) {
        if(Entrust::hasRole('teacher')) {
          return $this->showTaskAsTeacher($id);
        } else if(Entrust::hasRole('moderator')) {
          return $this->showTaskAsModerator($id);
        } else if(Entrust::hasRole('student')) {
          return $this->showTaskAsStudent($id);
        }
      }
    }
    

    这只是另一种方法,使您的路线更清晰,并将逻辑放入控制器中。

    【讨论】:

    • 我曾经这样做过,但我也不确定最好的方法是什么
    • @vrijdrogenaam 我同意,这绝对是一个解决方案,而不是一个完整的解决方案。每次为每个控制器功能检查用户角色将是非常多余的。如果您决定添加/更改/删除用户角色怎么办?您必须检查每个控制器和功能并对其进行修改。
    • @SteveBauman 确实,但是面对这个问题的一般专业方法是什么?我想这种根据用户角色传递不同视图是网站中的常用技术,不是吗?
    • @vrijdrogenaam 我会说拥有相同的视图,但根据角色将不同的数据传递给视图会更有意义,除非每个角色的数据都完全不同。如果您有不同的角色来控制对同一表上不同数据的访问,那么您绝对应该使用相同的视图,但获取该指定角色的数据。你有没有看过我的回答,看看是否适合你的需要?
    • 我有,而且它有效,但我宁愿寻找“最佳”方式。视图确实完全不同,传递了不同的数据等
    【解决方案3】:

    从@Matthias S 的回答中,这行得通吗?不要使用委托过滤器,而是像这样检查路由的权限:

    //routes.php
     if(Entrust::hasRole('teacher')) {
         Route::get('/tasks',array('as'=>'tasks','uses'=>'TasksController@tasksAsTeacher'));
         Route::get('/task/{id}',array('as'=>'showTask','uses'=>'TasksController@showTaskAsTeacher'));
     }
    

    重复不同的角色

    编辑:此外,如果您将用户的角色存储在会话中,则可以使用如下自动路由:

    //routes.php
     if(Entrust::hasRole(Session::get('role'))) {
         Route::get('/tasks',array('as'=>'tasks','uses'=>'TasksController@tasksAs'.Session::get('role')));
         Route::get('/task/{id}',array('as'=>'showTask','uses'=>'TasksController@showTaskAs'.Session::get('role')));
     }
    

    这样,一旦为角色添加了正确的控制器函数,您就可以根据需要添加任意数量的角色。

    编辑#2:

    或者我猜更好

    //routes.php - UPDATED, verify role inside controller instead of defining routes based on role
         Route::get('/tasks',array('as'=>'tasks','uses'=>'TasksController@tasks'));
         Route::get('/task/{id}',array('as'=>'showTask','uses'=>'TasksController@showTask'));
    
    
    //TasksController.php
    public function __construct(){
        if(!Session::get('role')){ //Make sure the user has a role assigned
            return Redirect::to('login'); // Redirect to login or permission screen if not
        }
    }
    
    public function tasks(){
        if(Entrust::hasRole(Session::get('role')){
            $tasks = Tasks::where('role_id', '=', $role->id); // Get tasks based on role
            View::make('tasks.index')->with('tasks', $tasks);
        } else{
            // Show permissions error for user
        }
    }
    
    public function showTask($task_id){
        if(Entrust::hasRole(Session::get('role')){
            $task = Tasks::where('role_id', '=', $role->id)->where('id', '=', $task_id)->first();
            View::make('tasks.view')->with('task', $task);
        }
    }
    

    【讨论】:

    • 这部分工作,但这里的问题是,当用户注销时,没有 if 语句为真,这些路由都没有设置,调用不存在的路由时出现错误在例如导航栏
    • 啊是的,这是真的,从来没有想过...我会更新我的“编辑#2”答案
    • 但另一个建议是,访客用户的视图不应该与登录用户不同吗? IMO,访客用户不应该能够看到已登录用户的导航,除非他们有权查看它
    • 你是对的,但这实际上是一个“愚蠢”的事情:在导航栏中有一个 if 语句来检查用户是否登录。如果不是,则会显示登录链接。如果是,则返回路由。
    • 您是否尝试过为访客用户和登录用户使用不同的布局文件?我使用两种布局(一种名为 auth.blade.php,另一种名为 main.blade.php 用于登录用户)。我认为这是一个很好的做法,因为用户只有两种状态(注销/登录),您只需要维护两个文件。如果您在控制器中指定布局文件而不是视图本身,则更容易实现,如下所示:laravel.com/docs/templates#controller-layouts