1

我已經開始意識到Rails中的「瘦控制器」哲學,它說業務邏輯不應該在控制器中,但他們應該基本上只負責調用一些模型方法,然後決定渲染/重定向什麼。將業務邏輯推入模型(或其他地方)可以保持行爲方法的清潔(並避免在控制器的功能測試中存留長鏈ActiveRecord方法)。瘦的控制器傳遞分層/嵌套的數據來查看

我跑過的大多數情況都是這樣的:我有三個模型,Foo,BarBaz。他們每個人都有一個定義的方法或範圍(稱爲filter),將對象縮小到我要找的位置。一個瘦小的操作方法可能如下:

def index 
    @foos = Foo.filter 
    @bars = Bar.filter 
    @bazs = Baz.filter 
end 

不過,我碰到的,其視圖需要顯示更多的分層數據結構的情況。例如,Foohas_many barsBarhas_many bazs。在視圖(通用「儀表板」頁面)中,我將顯示類似這樣的內容,其中每個foobarbaz已按照某些標準過濾掉(例如,對於每個級別,我只想顯示active個) :

Foo1 - Bar1 (Baz1, Baz2) 
     Bar2 (Baz3, Baz4) 
----------------------- 
Foo2 - Bar3 (Baz5, Baz6) 
     Bar4 (Baz7, Baz8) 

爲用戶提供其需要的數據來看,我最初的想法是把一些瘋狂像這樣的控制器:

def index 
    @data = Foo.filter.each_with_object({}) do |foo, hash| 
    hash[foo] = foo.bars.filter.each_with_object({}) do |bar, hash2| 
     hash2[bar] = bar.bazs.filter 
    end 
    end 
end 

我推下來,以Foo模式,但這並不好。這看起來不像是一個複雜的數據結構,可以將其分解爲單獨的非ActiveRecord模型或類似的東西,它只需要在每個步驟中應用一個非常簡單的過濾器即可獲取一些foos及其bars及其bazs

從控制器向視圖傳遞像這樣的分層數據的最佳實踐是什麼?

+0

什麼,如果你只是過濾並急於加載模型,然後擔心在您的視圖中的演示文稿?從我所知道的情況來看,你的控制器方法只是將已經正確的結構重新打包成哈希......如果我錯過了某些東西,我會很抱歉。 –

+0

@DamienRoche:我可能會錯過一些簡單的事情,但我無法想出一種方法來加快考慮到每個步驟的範圍的相關數據。我嘗試了'Foo.filter.includes(:bars).merge(Bar.filter).all'的初始步驟,但在'default_scope'中使用的列有一個模糊的列引用錯誤。閱讀更多使它聽起來像'includes'只能在最簡單的情況下與'merge'一起使用。有什麼我失蹤? – jrdioko

+0

您可以像這樣熱切加載數據:'@ foos = Foo.filter.includes(bars:[:baz])''。 – jokklan

回答

3

你可以得到@foos這樣的:

@foos = Foo.filter.includes(:bars, :bazs).merge(Bar.filter).merge(Baz.filter).references(:bars, :bazs)

現在你的關係,過濾,並渴望加載。您想要做的其餘部分是您希望在視圖中呈現的方式。也許你會這樣做:

<% Foo.each do |foo| %> 
    <%= foo.name %> 
    <% foo.bars.each do |bar| %> 
    <%= bar.name %> 
    <% bar.bazs.each do |baz| %> 
     <%= baz.name %> 
    <% end %> 
    <% end %> 
<% end %> 

在控制器中的任何形式的散列建設是不必要的。您在視圖中使用的抽象級別是合理的。

+2

我認爲這個答案是正確的。請記住,如果你使用'@foos = Foo.filter.includes(:bars,:bazs).merge(Bar.filter).merge(Baz.filter).references(:bars,:bazs)' 需要將你的foos限制爲具有活動bazs的活動酒吧,你可以使用: 'Foo.active.includes(:bars =>:bazs).where(「bars.active」=> true).where(「 bazs.active「=> true)' – xlembouras

+0

謝謝,看起來像那樣會工作。這是最初給我不明確的列錯誤,但[添加'table_name'到我的'default_scope'](http://stackoverflow.com/questions/3318263/question-about-activerecorddefault-scope-method-and-default-ordering )解決了這個問題。我假設Rails 3會是相同的,只是沒有'references'? – jrdioko

1

這種事情被廣泛接受的最佳實踐之一,如果提取窗體對象。從代碼氣候布萊恩Helmkamp寫了一個很好的博客文章如下:

http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

見第三部分「提取表單對象」。

問題是,是的,您應該將業務邏輯從您的控制器中移出。但它不屬於你的數據模型(Activerecord模型)。您需要使用數字2的組合,「提取服務對象」和3,「提取表單對象」,以便爲您的應用程序構建良好的結構。

您還可以觀看布萊恩的一個很好的視頻在這裏解釋這些概念:http://www.youtube.com/watch?v=5yX6ADjyqyE

更多關於這些話題,它也強烈建議您觀看Confreaks會議視頻:http://www.confreaks.com/

+0

問題是「從控制器向視圖傳遞像這樣的分層數據的最佳做法是什麼?」 「層次結構」已經由foos,bars和bazs之間的關係來表達。沒有什麼可以提取到表單或服務對象中。 – Kaleidoscope

+0

啊,你是對的。我誤解了它:/ – Houen

相關問題