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