Polymorphic comments, how to render when comment validation is not performed?

I have created an application with multiple models (say A, B) that are politically related to the comment model. When you view the page associated with controller A, show the action, the comments associated with object A are displayed as a form to create a new object. It all works and looks like Ryan Bates' 15 minute blog posted on the rails website. However, if I add a confirmation to prevent the user from submitting an empty comment, I'm not sure how. Here's what I have in my comment controller:

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

      

View partial comment and form (with 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'

      

I can figure out how to deal with validation errors. When validation errors are fired, it doesn't seem to call f.error_messages. Also, when the render is triggered, the user is taken to a page with the following url: a / 2 / comments when I would like to do a / 2.

newest solution:

def create
  subject = ""
  if !@comment.save
    subject = "?subject=#{@comment.subject}"
  end
  redirect_to @back + subject
end

      

Then, in controller view A:

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

      

It works, but it feels ugly ...

+2


source to share


1 answer


It's hard to wrap your head around yourself because you don't know which object you are going to receive in the comment controller.

It's much easier if it's not a polymorphic relationship. Before we understand how to do this, we need to understand the best way to make a single version.

I should note that this assumes you have your resources / routes defined correctly:

map.resources: posts ,: has_many => [: comments] map.resources: pages ,: has_many => [: comments]

Let's say that we have a lot of comments on a simple Post example. Here's an example of this:

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

      

Now we want to use this in a polymorphic relationship, so we need to set up a few things. Let's say we have Pages and Posts that have comments. Here's an example of this:

From you Pages and Pages Pages Pages:



<%= render 'comments/new' %>

      

In the message controller:

before_filter :fetch_post

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

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

      

This simplifies your form: <% 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 here either. They will created in the controller.)
<% end %>

      

In your comments, the controller:

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

      

+3


source







All Articles