user29559775
user29559775

Reputation: 11

undefined method `primary_key' for String:Class

Using Signed Global IDs with Polymorphic Select Fields in Rails Forms: My models:

class Comment < ApplicationRecord
  belongs_to :commentable, polymorphic: true
end

class Post < ApplicationRecord
  has_many :comments, as: :commentable
end

class Photo < ApplicationRecord
  has_many :comments, as: :commentable
end

My controllers:

class CommentsController < ApplicationController
  def new
    @comment = Comment.new
  end
  def create
    @comment = Comment.new(comment_params)#errors this location...
  end

def comment_params
    params.require(:comment).permit(:commentable)
  end

end

I have in table commets (commetable_type,_id)

Upvotes: 0

Views: 59

Answers (1)

max
max

Reputation: 102250

From the purely technical standpoint your code doesn't work as the association=(associate) setter method expects a model instance and not a string.

Comment.new(commentable: Post.find(1))

This is different than the association_id= setter also generated by belongs_to that you would normally used with a form. But that one will not automatically resolve a global id either so I don't understand how this is supposed to work.

While you could have the controller use different commentables depending on the passed form inputs and resolve the global id explicitly it's not really the best approach as you're making a nested resource implicit from a RESTful standpoint.

You're also not handling the case where the parent id is invalid at all.

I would really just treat this as a normal nested resource and make the connection part of the URL.

resources :photos do
  resources :comments, only: :create, module: :photos
end

resources :posts do
  resources :comments, only: :create, module: :posts
end 
class CommentsController < ApplicationController

  def create
    @comment = commentable.comments.new(comment_params)
    # @todo you probably want to assign the comment to the current 
    # user unless they are all anonymous 
    if @comment.save
      redirect_to @commentable
    else 
      # render the view where the form is embedded.
    end
  end

  # permit the actual content of the comment instead
  def comment_params
    params.require(:comment)
          .permit(:text, :foo, :bar, :baz)
  end
end
module Posts
  # Handles POST /posts/1/comments
  class CommentsController < ::CommentsController
    def commentable
      @commentable ||= Post.find(params[:post_id])
    end
  end
end
module Photos
  # Handles POST /photos/1/comments
  class CommentsController < ::CommentsController
    def commentable
      @commentable ||= Photo.find(params[:photo_id])
    end
  end
end
<%= form_with model: [commentable, comment] do |form| %>
  # ...
<% end %>

While this can seem unessicary it makes debugging way easier as you can see from POST /photos/1/comments exactly what is supposed to be happening and it also gives you a natural way to handle the complexity that always rears it's ugly head sooner or later.

The duplication can be reduced later if you have a large amount of commentable classes.

Upvotes: 2

Related Questions