2009-09-06 111 views
2

我創建了一個應用程序,該應用程序具有多個與評論模型呈多態關聯的模型(稱爲A,B)。當查看與A控制器關聯的頁面時,顯示動作,與A對象關聯的註釋將以創建新對象的形式顯示。所有這些工作都與Ryan Bates在rails網站上發佈的15分鐘博客類似。但是,如果我添加驗證以確保用戶不提交空白評論,我不確定如何呈現該評論。下面是我在我的評論控制器:多態註釋,當註釋驗證失敗時如何渲染?

before_filter :load_resources, :only => [:create] 
def create 
    if @comment.save 
    redirect_to @back 
    else 
    render @action 
    end 
end 

private 

def load_resources 
    @comment = Comment.new(params[:comment]) 
    case @comment.commentable_type 
    when 'A' 
    @a = A.find(params[:a_id] 
    @comments = @a.comments 
    @back = a_url(@comment.commentable_id) 
    @resource = @a 
    @action = 'as/show' 
    when 'B' 
    ... 
    end 
end 

查看局部徵求意見和形式(使用Haml的):

=render :partial => 'comments/comment', :collection => @comments 

%h3 Leave a comment: 
-form_for [@resource, Comment.new] do |f| 
    =f.error_messages 
    =f.hidden_field :commentable_type, :value => params[:controller].singularize.titleize 
    =f.hidden_field :commentable_id, :value => params[:id] 
    =f.hidden_field :editor_id, :value => @current_user.id 
    =f.hidden_field :creator_id, :value => @current_user.id 
%fieldset 
    =f.label :subject, 'Subject', :class => 'block' 
    =f.text_field :subject, :class => 'block' 
    =f.label :text, 'Comment', :class => 'block' 
    =f.text_area :text, :class => 'block' 
    .clear_thick 
=f.submit 'Submit', :id => 'submit' 

我似乎可以找出是如何處理驗證錯誤。當驗證錯誤被觸發時,它似乎不會觸發f.error_messages。此外,當渲染被觸發時,它會將用戶帶到具有以下url的頁面:a/2/comments,當我希望渲染一個/ 2時。

最新的解決方案:

def create 
    subject = "" 
    if [email protected] 
    subject = "?subject=#{@comment.subject}" 
    end 
    redirect_to @back + subject 
end 

然後在控制器顯示行動:

if params.has_key?('subject') 
    @comment = Comment.create(:subject => params[:subject]) 
else 
    @comment = Comment.new 
end 

這工作,但感覺那種難看......

回答

3

這是一種難以包裹因爲你不知道你將會在評論控制器中接收什麼樣的對象。

當它不是一個多態關係時就簡單多了。在我們理解如何做到這一點之前,我們需要了解做單一版本的最佳方式。

我要指出,這是假定你有你的資源/路線正確定義:

map.resources:帖子:的has_many => [:評論] map.resources:頁面:的has_many => [ :評論]

比方說,我們有一個簡單的例子,一個職位有很多評論。這裏是一個這樣做的示例:

class CommentsController < ApplicationController 
    before_filter => :fetch_post 

    def create 
    @comment = @post.comments.new(params[:comment]) 

    if @comment.save 
     success_message_here 
     redirect post_path(@post) 
    else 
     error_message_here 
     redirect_to post_path(@post) 
    end 
    end 

    protected 
    def fetch_post 
     @post = Post.find(params[:post_id]) 
    end 
end 

現在我們想要在多態關係中使用它,所以我們必須設置幾件事情。假設我們有現在有評論的網頁和帖子。下面是這樣的一個樣本的方式:

從您的文章和網頁頁面中顯示:

<%= render 'comments/new' %> 

在posts控制器:

before_filter :fetch_post 

    def show 
     @comment = @commentable.comments.build 
    end 

    protected 
     def fetch_post 
     @post = @commentable = Post.find(params[:id]) 
     end 

這將設置你的形式很簡單: <%error_messsages_for:comment%>

<% form_for [ @commentable, @comment ] do |f| %> 
    #Your form fields here (DO NOT include commentable_type and or commentable_id also don't include editor and creator id's here either. They will created in the controller.) 
<% end %> 

在你身上[R評論控制器:

def create 
    @commentable = find_commentable 
    # Not sure what the relationship between the base parent and the creator and editor are so I'm going to merge in params in a hacky way 
    @comment = @commentable.comments.build(params[:comment]).merge({:creator => current_user, :editor => current_user}) 

    if @comment.save 
    success message here 
    redirect_to url_for(@commentable) 
    else 
    failure message here 
    render :controller => @commentable.class.downcase.pluralize, :action => :show 
    end 
end 

    protected 
    def find_commentable 
     params.each do |name, value| 
     if name =~ /(.+)_id$/ 
      return $1.classify.constantize.find(value) 
     end 
     end 
     nil 
    end 
+0

很不錯的解決方案。出於好奇,爲什麼不直接在表單中包含創作者和editor_id?在我看來,這是降低控制器複雜度的簡單方法。有了這個說法,視圖應該比控制器更清潔嗎? – LDK 2009-09-20 00:20:02

+0

賽維LDK, 一般規則是你不應該信任用戶輸入。即使有隱藏字段,惡意用戶也可以將任何想要的ID注入表單中,然後將其傳遞,然後模擬另一個用戶。 通過刪除該選項,您可以使應用程序更安全。 – Carlos 2009-09-21 20:27:38