就嵌套资源而言,我的应用对上下文变得非常敏感。
例如,假设我的应用程序是图书管理员应用程序,我可能会有这样的路由:
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
我已经开始使用来自#defaults
的{{1}},它允许您直接将参数发送到ActionDispatch::Routing::Mapper::Scoping
,而不需要其他一些干扰逻辑。我更喜欢这个,因为我没有设置底片,而是设置正面配置:
#scope
然而,如果我遵循良好的惯例,问题仍然存在......这仍然感觉不是100%正确。
答案 0 :(得分:1)
如果要为嵌套路径指定不同的控制器,可以始终执行以下操作:
resources :books
resources :libraries do
resources :books, controller: 'libraries/books'
end
这样,您可以拥有两个控制器:
在这种情况下我通常做的是让每个控制器控制器处理范围,并将共享功能放在一个问题中。即:
应用程序/控制器/ books_controller.rb
class BooksController < ApplicationController
include BooksControllerConcern
private
def book_scope
current_user.books
end
end
应用程序/控制器/库/ books_controller.rb
class Libraries::BooksController < ApplicationController
include BooksControllerConcern
private
def book_scope
Book.find_by(library_id: params[:library_id])
end
end
关注:
module BooksControllerConcern
extend ActiveSupport::Concern
def index
@books = book_scope.page(params[:page])
end
# .. other shared actions
end
您的路线将如下所示:
答案 1 :(得分:1)
尝试使用范围模块:
resources :books
scope module: :libraries do
resources :libraries do
resources :books
end
end
然后你应该有这些类型的帮助程序来获取索引操作(例如):
books_path == [:books]
library_books_path(@library) == [@library, :books]
我对libraries
资源是否应该位于作用域模块下感到遗憾,但大多数情况下这样做是有意义的。假装你想要把那个块拿出来并稍后放入一个宝石(即使那不是你的计划)。这有助于绘制每个范围模块中的内容。