Ruby on Rails – 重复路由 & 对控制器的影响

huangapple go评论60阅读模式
英文:

Ruby on Rails - Duplicate routes & impact on controllers

问题

  1. 这是正确的方法吗?还有没有更好的解决方法?
  2. 如果我采用这种方法,如何处理控制器操作?因为在一种情况下(2),我们将获得 params[:group_id],但在另一种情况下,我需要将它添加为 strong_params。是否可以使用if-else条件来处理?(如果在strong_params中没有[:group_id],则使用params[:group_id])
英文:

I am creating a Task Manager.

So, I have:

  1. A dashboard where I can do CRUD for tasks (and then I will assign them a group)
  2. A group section where I can go inside a group and do CRUD for tasks

Then my routes would look something like this:

Rails.application.routes.draw do
  devise_for :users
  namespace :api, defaults: { format: :json } do
    namespace :v1 do
      resources :groups, only: [ :index, :show, :create, :update, :destroy] do
        member do
          resources :tasks, only: [:index, :show, :create, :update, :destroy]
        end
      end
      resources :tasks, only: [:index, :show, :create, :update, :destroy]
    end
  end
end

For this:

  1. A dashboard where I can do CRUD for tasks (and then I will assign them a group)
resources :tasks, only: [:index, :show, :create, :update, :destroy]

And for this:

  1. A group section where I can go inside a group and do CRUD for tasks
resources :groups, only: [ :index, :show, :create, :update, :destroy] do
  member do
    resources :tasks, only: [:index, :show, :create, :update, :destroy]
  end
end

But I feel this is duplicating routes. I have 2 questions:

  1. Is this the right approach or would there be a better way to solve it?
  2. If I go with this, how would I approach the controller actions? Since in one situation (2) we would get the params[:group_id] but in the other I would need to add it as strong_params. Would an if-else condition work? (if there is no [:group_id] in strong_params then take the params[:group_id])

Thanks!

答案1

得分: 1

不是的。不太好。

深度嵌套的路由很快变得难以处理,因此您应该考虑使用浅层嵌套。这仅嵌套了集合路由(索引,新建,创建),而不嵌套成员路由(显示,编辑,更新,删除)。

如果任务具有唯一的ID,您应该能够在不涉及组的情况下显示,编辑,更新和删除它们。这个规则的例外情况是如果任务只在组内是唯一的(但为什么呢?)。

在API中,这尤其如此。后端不应该知道您是从前端应用程序的页面X或Y修改资源。

您可以使用 shallow: true 选项来生成浅层路由,但我可能会将其定义为:

# 这表示没有嵌套在组中的任务
resources :tasks, only: [:index, :show, :create, :update, :destroy]

resources :groups, only: [:index, :show, :create, :update, :destroy] do
  resources :tasks, only: [:index, :create]
end

如果我采用这个方法,如何处理控制器操作呢?

这里实际上有两种不同的方法。其中一种我称之为 "参数嗅探方法":

module API
  module V1
    class TasksController
      # GET /api/v1/groups/1/tasks
      # GET /api/v1/tasks
      def index
        if params[:group_id]
          render json: Group.find(params[:group_id]).tasks
        else
          render json: Task.all
        end
      end
    end
  end
end

这是最明显的解决方案,但明显违反了 单一职责原则 并增加了所有方法的循环复杂度。

另一种解决方案是将资源的嵌套表示路由到单独的控制器:

# 这表示不嵌套在组中的任务
resources :tasks, only: [:index, :show, :create, :update, :destroy]

resources :groups, only: [:index, :show, :create, :update, :destroy] do
  resources :tasks, only: [:index, :create], module: :groups
end

这将 /groups/:group_id/tasks 路由到 API::V1::Groups::TasksController

module API
  module V1
    class TasksController
      # GET /api/v1/tasks
      def index
        render json: Task.all
      end
    end
  end
end
module API
  module V1
    module Groups
      class TasksController
        before_action :set_group

        # GET /api/v1/groups/1/tasks
        def index
          render json: @group.tasks
        end

        private

        def set_group
          @group = Group.find(params[:group_id])
        end
      end
    end
  end
end

如果您想避免再嵌套一步,您也可以只使用 resources :tasks, only: [:index, :create], controller: :group_tasks

英文:

> Is this the right approach or would there be a better way to solve it?

No. Not really.

Deeply nested routes quickly become cumbersome to work with so you should consider using shallow nesting. This only nests the collection routes (index, new, create) and not the member routes (show, edit, update, destroy).

If tasks have a unique id you should be able to show, edit, update and destroy them without the group being involved. The exception to this rule is if tasks are only unique per group (but why?).

In an API this is especially true. The backend shouldn't need to know that you're modifying the resource from page X or Y in your frontend application.

You can use the shallow: true option to generate shallow routes but I would probally just define it as:

# This respresents tasks not nested in a group
resources :tasks, only: [:index, :show, :create, :update, :destroy]
 
resources :groups, only: [:index, :show, :create, :update, :destroy] do
  resources :tasks, only: [:index, :create]
end

> If I go with this, how would I approach the controller actions?

There are really two different approaches here. One I call the "param sniffing method":

module API
  module V1
    class TasksController
      # GET /api/v1/groups/1/tasks
      # GET /api/v1/tasks
      def index
        if params[:group_id]
          render json: Group.find(params[:group_id]).tasks
        else
          render json: Task.all
        end
      end
    end
  end
end

This is the most obvious solution but blatantly violates the Single Responsibity Principle and increases the cyclic complexity of all the methods.

The other solution is to route the nested representation of the resource to a separate controller:

# This respresents tasks not nested in a group
resources :tasks, only: [:index, :show, :create, :update, :destroy]
 
resources :groups, only: [ :index, :show, :create, :update, :destroy] do
  resources :tasks, only: [:index, :create], module: :groups
end

This routes the /groups/:group_id/tasks routes into API::V1::Groups::TasksController.

module API
  module V1
    class TasksController
      # GET /api/v1/tasks
      def index
        render json: Task.all
      end
    end
  end
end
module API
  module V1
    module Groups
      class TasksController
        before_action :set_group
 
        # GET /api/v1/groups/1/tasks
        def index
          render json: @group.tasks
        end

        private

        def set_group
          @group = Group.find(params[:group_id])
        end 
      end
    end
  end
end

You could also just do resources :tasks, only: [:index, :create], controller: :group_tasks if you want to avoid nesting the contants one more step.

huangapple
  • 本文由 发表于 2023年2月14日 05:54:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/75441573.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定