#Railsのポリモーフィック関連
はじめに
今回はポリモーフィック関連についてです。
はて、ポリモーフィックとは?の方ように、ポリモーフィックについて簡単に説明して、実際に実装してみたいと思います。
##ポリモーフィズムとは##
Polymorphism
、意味は多様性。オブジェクティブ指向の3大要素にもありますね(ポリモーフィズム)。
一言でいうと、オブジェクトの実態を気にせず、メソッドを呼び出し、そのオブジェクトごとに振舞ってもらうことです。
豚さん、鴨さん、犬さんがいたとして、3人の鳴き声を聞きたいときに、
「ブヒブヒって言ってください」
「クワクワと叫んでください」
「ワンワンと鳴いてください」
とそれぞれの振る舞いごとに命令をいうのではなく、3匹共に
「鳴いてください。」
と命令した方が楽だし、いちいち命令先の動物達のことを考えて命令しませんよね。
プログラミングでも同じで、いちいちオブジェクトの実態のことを考えてコードを書いていたらコードは汚くなるし、大変です。鳴き声を出力するメソッド(命令)は全部同じ名前で使いたいのです。鳴き声についてはオブジェクトごとに勝手に変わってくれればいいのです。
さてポリモーフィズムはオブジェクティブ指向の話なので、まあこんなところで切り上げます。
今回はポリモーフィック関連
です。
##ポリモーフィック関連##
ポリモーフィック関連とは上記のポリモーフィズムの思想の元、モデルを関連づけることです。
例えば、Userモデル
とCompanyモデル
の二つがPostモデル
を共有している場合、
記事の所有者が企業なのか、個人ユーザーなのかの2通りあります。
ポリモーフィックを使わない場合、
class User < ApplicationRecord
has_many :posts
end
class Company < ApplicationRecord
has_many :posts
end
class Post < ApplicationRecord
belongs_to :user
belongs_to :campany
end
上記3つのコードで一応、関連はつけられてます。しかし、問題が2つあります。
1、記事の投稿者の情報を取得する場合、Userなのか、Companyなのか確認しなければなりません。
2、投稿するモデルが増えた場合、Postモデルにカラムを追加して関連づけなければならない。
これらを解決するためにポリモーフィック関連を使って、オブジェクトの実態のしがらみから解放されましょう。
##ポリモーフィック関連を実装してみる##
実装は簡単です。
class CreatePost < ActiveRecord::Migration[5.1]
def change
create_table :posts do |t|
t.string :name
t.references :postable, polymorphic: true, index: true
t.timestamps
end
end
end
前回学んだreference
を用いて参照するためのキーをカラムに追加します。そのときにオプションでpolymorphic
をtrueにするだけです。これでpostable
を使って関連した投稿者を取得できます。
これスキーマ見ればわかるんですけど、自動でpostable_typeってのが追加されてるんですよね。ここで投稿者のクラスを判断してるみたいです。
create_table "posts", force: :cascade do |t|
t.string "name"
t.string "postable_type"
t.integer "postable_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["postable_type", "postable_id"], name: "index_posts_on_postable_type_and_postable_id"
end
あとは、各々のモデルで関連づけるだけです。
class Post < ApplicationRecord
belongs_to :postable, polymorphic: true
end
class User < ApplicationRecord
has_many :posts, as: :postable
end
class Company < ApplicationRecord
has_many :posts, as: :postable
end
これで@post.postable
で投稿者がどっちのクラスか気にせず呼び出せます。
さて、ポリモーフィズムにのっとるなら、共通のメソッドの定義しましょう。
class User < ApplicationRecord
has_many :posts, as: :postable
def contributor_name
"#{last_name} #{first_name}"
end
end
class company < ApplicationRecord
has_many :posts, as: :postable
def contributor_name
name
end
end
これで記事の投稿者の名前出力はオブジェクトの実態を気にせず全部共通で@post.postable.contributor_name
で出力されます。しかも個人ユーザーか、企業かで名前の表記の振る舞いも各々に応じたものになります。
これが簡単なポリモーフィックの例です。
##まとめ
最後のメソッドの定義は必須な仕様にはなってないみたい。そこを実装するかはコーダーの腕ってとこなのかね。オブジェクティブ指向は考え方みたいなものだから、難しいね。そちらも深めていきたい。
今回は参考記事がワシなに書けばいいんだろうってくらい良記事だったのぜひ。
##参考にしたの
Railsのポリモーフィック関連とはなんなのか
https://qiita.com/itkrt2y/items/32ad1512fce1bf90c20b
【Rails】ActiveRecord:単一テーブル継承(sti)とポリモーフィック関連を未だにぱっと思い出せないのでまとめ。
https://shirusu-ni-tarazu.hatenablog.jp/entry/2012/11/04/173742
Active Record の関連付け (アソシエーション)
https://railsguides.jp/association_basics.html#%E3%83%9D%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%95%E3%82%A3%E3%83%83%E3%82%AF%E9%96%A2%E9%80%A3%E4%BB%98%E3%81%91