#はじめに
インターン先でSTIとポリモーフィックを利用したコメント機能を作成したときに
自分が求めてる情報が出てこなかったのと
ハマったことがいくつかあるのでメモ代わりに残していこうと思います
ポリモーフィックやSTIについては
Railsのポリモーフィック関連とはなんなのか
みんなRailsのSTIを誤解してないか!?
などがとても丁寧でわかりやすく参考になります
今回は設計やコード中心なので概念などはそちらで理解していただければと思います
私も未熟なところがたくさんあるので、指摘等あれば教えていただけると嬉しいです
##設計について
今回の設計はざっとこんな感じです。
(いろいろ省いて主要部分だけの実装を行っていきます)
1つのcommentsテーブルに2種類のテーブルがSTIで継承されており
taskかprojectのどちらかが紐づくような設計となっております!
常にprojectとtaskに紐づくcommentを作るわけではないので
projectとtaskからのびるcommentへのリレーションは点線で記述しました
(正しい書き方があるのかわからなかったです)
##実装
早速実装に入っていきます!
###テーブルの作成
commentsとtasksとprojectsテーブルの作成を行っていきます
commentsテーブルにはSTIのためのtypeを作成し
ポリモーフィックのためのcommentable_typeとcommentable_idという〇〇_typeと〇〇_idというカラムも作ります
$ rails g model Comment content:string type:string commentable_type:string commentable_id:integer
$ rails g model Task title:string content:string
$ rails g model Project name:string
###モデルの設定
モデルのディレクトリ構造は写真のようにcommentをtaskとprojectが継承するように作成します
こうすることによりSTIを行うことができます
次はモデルにポリモーフィックなどの設定を行っていきます
class Comment < ApplicationRecord
belongs_to :commentable, polymorphic: true
end
class Project < ApplicationRecord
has_many :comments, class_name: 'Comment::Project', as: :commentable, dependent: :destroy
end
class Task < ApplicationRecord
has_many :comments, class_name: 'Comment::Task', as: :commentable, dependent: :destroy
end
commentを継承しているprojectとtaskは
class Comment::Task < Comment
end
class Comment::Project < Comment
end
と記述することにより、commentを継承してくれます
モデルに記述することは以上です
###ポリモーフィックとSTIができているかの確認
この時点でrails consoleで以下のコマンドたちを打ってみてください!
pry(main)> Comment::Task.new
=> #<Comment::Task:0x00007fbf75f1e668 id: nil, type: "Comment::Task", commentable_type: nil, commentable_id: nil>
pry(main)> @task = Task.create(title: "test", content: "test")
pry(main)> @task.comments.build
=> #<Comment::Task:0x00007fbf7b0ba418 id: nil, type: "Comment::Task", commentable_type: "Task", commentable_id: 4, content: nil, created_at: nil, updated_at: nil>
typeには継承しているmodelが入っていおり
commentable_typeにはcommentに紐づくmodelが入り
commentable_idにはcommentに紐づくidが入っております
こうすることにより、commentという1つのモデルに2つのうちどちらか1つのモデルに紐づけることができます
この3つのカラムが作成できていたら、ポリモーフィックとSTIができています
###ルーティングとcontrollerの設定
Rails.application.routes.draw do
resources :tasks do
resources :comments, only: [:create], controller: 'task/comments'
end
resources :projects do
resources :comments, only: [:create], controller: 'project/comments'
end
end
###タスクのコメント機能の作成
ここからはタスクのコメント機能について作成していきます
まずはcontrollerです
@comment = Comment::Task.new
Comment::Taskのインスタンスを作成してtasksの詳細ページに投稿画面を作成します
<%= form_for @comment, url: task_comments_path(task_id: @task.id) do |f| %>
<%= f.text_area :content %>
<%= hidden_field_tag :type, @comment.type %>
<%= f.submit 'コメントを投稿する' %>
<% end %>
タスクを継承しているコメントControllerでcreateを行っていきます
def create
@comment = @task.comments.build(comment_params)
@comment.save
redirect_to task_path(@task)
end
private
def comment_params
params.require(params[:type].underscore.gsub('/', '_').to_sym).permit(:content)
end
パラメーターの受け取り方はStrong Parametersを動的に設定を参考にしました
実はこれだけでもうコメントの作成はできております!
##終わりに
STIやポリモーフィック、最初全然理解は出来なかったですが、実際に手を動かすことで少し理解できたような気がします。
今はtaskとprojectにしか紐付かないのですが、もっといろんなモデルに紐づくものだと
紐づくために必要な外部キーを増やさなくて良かったり
新しくコメントに関するテーブルを作らなくていいので便利だと感じました。
でもなぜそういう使い方をしている記事がヒットしなかったんだろうという疑問が残りました。
最後まで見ていただきありがとうございました!
ご指摘あればどしどしおまちしております〜!