I'm trying to build a form for a like button. This like model is polymorphic to different types of Models (comments / posts / etc.) and belongs to a certain user.
When this user is viewing a blog item for instance, I want to show a like button below the post. I've set-up my routes in a way that the like routes are always nested inside the polymorphic object for which they are destined:
So for posts for instance:
#routes.rb
resources :posts do
resources :likes, only: [:create, :destroy]
end
So a post link would look like /posts/:post_id/likes/
(method: Post)
In the controller I create a new Like object, assign it to a user and save it. This works perfectly.
The problem is when I'm trying to create delete form. I really have no clue how to create that, I know the link should be /posts/:post_id/like/:id
(method: Delete), but configuring it this way results in an error.
I think the form could also be refactored, but I have no clue on how making forms for these kind of 'complex' relationships.
#shared/_like_button.html.haml
- if not @post.is_liked_by current_user
= form_for(@post.likes.build, url: post_likes_path(@post)) do |f|
= f.submit
- else
= form_for(@post.likes.find_by(user_id: current_user.id), url: post_like_path(@post), html: {method: :delete}) do |f|
= f.submit
I think the main problem is that post_like_path(@post)
doesn't get rendered correctly because I'm not aware of the :id
of the like. So I keep getting an ActionController::UrlGenerationError
in PostsController#show
error when trying to build the link.
This should work:
= form_for([@post, @post.likes.find_by(user_id: current_user.id)], html: {method: :delete}) do |f|
url: post_like_path(@post)
in your code expects a second argument (the like
object). That's what throws the error.
But you don't need it actualy if you put the nested resources as an array in the first argument of the form_for
helper.
If the record passed to form_for is a resource, i.e. it corresponds to a set of RESTful routes, e.g. defined using the resources method in config/routes.rb. In this case Rails will simply infer the appropriate URL from the record itself. (source: http://apidock.com/rails/ActionView/Helpers/FormHelper/form_for)
You can pass an array of resources if your resource is nested within another resource.
Now... You probably want to re-use this code for your other polymorphic models as well. You can do this by passing @post
, or @comment
to your partial like this:
= render :partial => 'like_button', locals: {likable: @post}
and refactor your partial like this:
= form_for([likable, likable.likes.find_by(user_id: current_user.id)], html: { method: :delete}) do |form|