Ruby on rails 如何构造控制器以匹配嵌套资源

Ruby on rails 如何构造控制器以匹配嵌套资源,ruby-on-rails,routing,Ruby On Rails,Routing,就嵌套资源而言,我的应用程序对上下文变得非常敏感 例如,假设我的应用程序是图书管理员应用程序,我可能有如下路径: resources :books resources :libraries do resources :books end 我希望这样,如果您访问/books,您会看到与您相关的书籍,即登录用户。不管是你借出的书,还是你喜欢的书,或者你拥有的什么 但是当您访问/libraries/:id/books时,您应该会看到与该库相关的书籍 现在,在控制器中很容易做到: def inde

就嵌套资源而言,我的应用程序对上下文变得非常敏感

例如,假设我的应用程序是图书管理员应用程序,我可能有如下路径:

resources :books
resources :libraries do
  resources :books
end
我希望这样,如果您访问
/books
,您会看到与您相关的书籍,即登录用户。不管是你借出的书,还是你喜欢的书,或者你拥有的什么

但是当您访问
/libraries/:id/books
时,您应该会看到与该库相关的书籍

现在,在控制器中很容易做到:

def index
  if params[:library_id]
    @library = Library.find(params[:library_id])
    @books = @library.books
  else
    @books = current_user.books
  end
end
然而,这种模式在我的应用程序中重复了好几次(想想我是否想让书籍嵌套在作者或出版商之下!)我的控制器可能会变得非常复杂。如果我想对每个上下文有不同的观点,那就更难了

所以,我想要两本书。一个位于根命名空间中,另一个位于库下的命名空间中

那很好。我已经构建了
BooksController
Library::BooksController
。然而,我在设置合理的路线时遇到了问题

我首先认为可以指定名称空间:

resources :books
resources :libraries do
  namespace :library do
    resources :books
  end
end
但这打破了现有的路线,看起来像
/libraries/:id/library/books

因此,我尝试将
path:false
传递到名称空间,这会修复路由,但会使命名路由和多态路由变得非常麻烦

我希望为了访问
/libraries/123/books
,我可以:

link_to "See books", [@library, :books] # -or- 
link_to "See books", library_books_path(@library)
但是,由于我们添加了名称空间,现在路由非常庞大:

link_to "See books", [@library, :library, :books] # -or- 
link_to "See books", library_library_books_path(@library)
那么,有没有更传统的方法来构造对嵌套资源有意义的控制器?有没有更好的方法来构建我的路线

更新

通过将
as:false
添加到名称空间声明中,我得到了预期的结果,如下所示:

resources :books
resources :libraries do
  namespace :library, path: false, as: false do
    resources :books
  end
end
H-man指出,您可以在每个资源上指定一个控制器。然而,这感觉并不正确,因为大量路线的管理可能会失控

所以这两种解决方案都有效,但有没有更传统的方法来解决这个问题

更新#2

我使用了
ActionDispatch::Routing::Mapper::Scoping
中的
#defaults
,它允许您直接将参数发送到
#scope
,而不需要其他一些干扰逻辑。我更喜欢这一点,因为我设置的是积极配置,而不是消极配置:

resources :books
resources :libraries do
  defaults module: :libraries
    resources :books
  end
end

然而,问题仍然存在,如果我遵循良好的惯例。。。这仍然感觉不是100%正确。

如果要为嵌套路由指定不同的控制器,始终可以执行以下操作:

resources :books
resources :libraries do
  resources :books, controller: 'libraries/books'
end
这样,您可以有两个控制器:

  • app/controllers/books\u controller.rb
  • app/controllers/libraries/books\u controller.rb
在这种情况下,我通常会让每个控制器处理作用域,并将共享功能放在关注点中。i、 e:

app/controllers/books\u controller.rb

class BooksController < ApplicationController
  include BooksControllerConcern

private
  def book_scope
    current_user.books
  end
end
class Libraries::BooksController < ApplicationController
  include BooksControllerConcern

private
  def book_scope
    Book.find_by(library_id: params[:library_id])
  end
end
您的路线如下所示:

resources :books
resources :libraries do
  resources :books
end
  • [:books]或books\u path=>/books
  • [@library,:books]或library\u books\u路径(@library)=>/libraries/:id/books

尝试使用作用域模块:

resources :books

scope module: :libraries do
  resources :libraries do
    resources :books
  end
end
然后,您应该有这些类型的帮助程序来访问索引操作(例如):


我对
资源是否应该在作用域模块下感到困惑,但大多数时候这样做是有意义的。假装你想把那块石头拿出来放进宝石里(即使这不是你的计划)。这有助于划清每个作用域模块中的界限。

请注意:我还没有验证我的代码是否有效,但你明白了。希望这能有所帮助。不过,如果有一种方法可以为一组路由设置名称空间,那就太好了。而不是必须在每个资源上指定控制器。是的,我同意。这种方法在路由中看起来有点奇怪,但它生成了您想要的正确路由,并保留了一个干净的文件夹结构。是的,作用域仍然会使路由帮助程序变得混乱,您确定吗?我发誓我已经用这种代码解决了一个类似于你的问题。或者我可能误解了你的问题;你是对的,对不起,这会添加到路径中。您必须执行
path:false
来维护相同的路由。我马上就要编辑了事实上,你比我想象的更正确。对指定的模块使用
#scope
,实际上正是我所希望的。然而,仅仅声明
scope:libraries
就把东西推到了垃圾堆里!弄清楚如何组织控制器已经困扰了我很长一段时间,所以我很高兴能和别人分享我的解决方案。摇滚乐。
books_path == [:books]
library_books_path(@library) == [@library, :books]