2014-02-27 114 views
2

就嵌套資源而言,我的應用程序對上下文變得非常敏感。如何構建控制器以匹配嵌套資源

例如,假設我的應用程序是一個圖書管理員應用程序,我可能有這樣的路線:

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 

然而,這種模式在我的應用程序重複幾次(想,如果我想有書在作者或出版商嵌套!)我的控制器可以變得非常複雜。如果我想爲每個環境提供不同的視圖,它會變得更加困難。

所以,我想有兩個書籍控制器。一個位於根名稱空間中,另一個位於庫下的名稱空間中。

這很好。我已經構建了BooksControllerLibrary::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) 

所以,有沒有一種更傳統的方式來構建嵌套資源有意義的控制器?有沒有更好的方法來構建我的路線?

UPDATE

我已經加入到as: false命名空間聲明,像這樣得到預期的結果:

resources :books 
resources :libraries do 
    namespace :library, path: false, as: false do 
    resources :books 
    end 
end 

和H-人注意的是,你可以指定每個資源的控制器。然而,由於大量路線的管理可能失控,這種感覺並不正確。

所以這兩個解決方案工作,但是有沒有更傳統的方法來解決這個問題?

更新#2

我從ActionDispatch::Routing::Mapper::Scoping它允許您直接發送到#scope參數沒有一堆其他干擾邏輯的登陸使用#defaults。我很喜歡,因爲這個而優於設定底片,我設置積極配置:

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

然而,問題仍然有效,如果我下面的好習慣......這仍然沒有感覺100%正確。

回答

1

嘗試使用範圍的模塊:

resources :books 

scope module: :libraries do 
    resources :libraries do 
    resources :books 
    end 
end 

那麼你應該有可去的索引操作(例如)這些各種各樣傭工:

books_path == [:books] 
library_books_path(@library) == [@library, :books] 

我很擔心libraries資源是否應該在範圍模塊下,但大多數情況下這是合理的。假裝你想把這個塊拿出來放在一塊寶石中(即使這不是你的計劃)。這有助於繪製每個範圍內模塊中的內容。

+0

是的,範圍仍然渾濁的路線幫手 –

+0

你確定嗎?我發誓我已經解決了類似於使用這種代碼的問題。否則我可能會誤解你的問題。 –

+0

不,你;對,我很抱歉,它增加了路徑。您必須執行'path:false'來維護相同的路線。我有一個編輯 –

1

如果要指定嵌套的路線不同的控制器,你總是可以做到這一點:

resources :books 
resources :libraries do 
    resources :books, controller: 'libraries/books' 
end 

這樣的話,你可以有兩個控制器:

  • 應用程序/控制器/ books_controller .RB
  • 應用程序/控制器/庫/ books_controller.rb

我在這種情況下通常會讓每個控制器控制器處理範圍,並將共享功能放在一個關注點上。即:

應用程序/控制器/ 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 

你的路線看起來像這個:

  • [:書籍]或books_path => /書籍
  • [@library,:書籍]或library_books_path(@library)=> /庫/:ID /書籍
+0

只是一個簡短的提示:我沒有證實我的代碼有效,但你明白了。希望這可以幫助。 – Hesham

+0

因此,如果有一種方法可以爲一組路由設置命名空間,那就好了。而不必在每個資源上指定控制器。 –

+0

是的,我同意。這種方法在路由上看起來有點奇怪,但它會生成所需的正確路由,並保留乾淨的文件夾結構。 – Hesham