Help us understand the problem. What is going on with this article?

Railsのポリモーフィック関連 初心者→中級者へのSTEP10/25

More than 1 year has passed since last update.

Railsのポリモーフィック関連

はじめに
今回はポリモーフィック関連についてです。
はて、ポリモーフィックとは?の方ように、ポリモーフィックについて簡単に説明して、実際に実装してみたいと思います。

ポリモーフィズムとは

Polymorphism、意味は多様性。オブジェクティブ指向の3大要素にもありますね(ポリモーフィズム)。
一言でいうと、オブジェクトの実態を気にせず、メソッドを呼び出し、そのオブジェクトごとに振舞ってもらうことです。
豚さん、鴨さん、犬さんがいたとして、3人の鳴き声を聞きたいときに、

「ブヒブヒって言ってください」
「クワクワと叫んでください」
「ワンワンと鳴いてください」

とそれぞれの振る舞いごとに命令をいうのではなく、3匹共に

「鳴いてください。」

と命令した方が楽だし、いちいち命令先の動物達のことを考えて命令しませんよね。
プログラミングでも同じで、いちいちオブジェクトの実態のことを考えてコードを書いていたらコードは汚くなるし、大変です。鳴き声を出力するメソッド(命令)は全部同じ名前で使いたいのです。鳴き声についてはオブジェクトごとに勝手に変わってくれればいいのです。
さてポリモーフィズムはオブジェクティブ指向の話なので、まあこんなところで切り上げます。

今回はポリモーフィック関連です。

ポリモーフィック関連

ポリモーフィック関連とは上記のポリモーフィズムの思想の元、モデルを関連づけることです。
例えば、UserモデルCompanyモデルの二つがPostモデルを共有している場合、
記事の所有者が企業なのか、個人ユーザーなのかの2通りあります。
ポリモーフィックを使わない場合、

User.rb
class User < ApplicationRecord
  has_many :posts
end
Combany.rb
class Company < ApplicationRecord
  has_many :posts
end
Post.rb
class Post < ApplicationRecord
  belongs_to :user
  belongs_to :campany
end

上記3つのコードで一応、関連はつけられてます。しかし、問題が2つあります。

1、記事の投稿者の情報を取得する場合、Userなのか、Companyなのか確認しなければなりません。
2、投稿するモデルが増えた場合、Postモデルにカラムを追加して関連づけなければならない。

これらを解決するためにポリモーフィック関連を使って、オブジェクトの実態のしがらみから解放されましょう。

ポリモーフィック関連を実装してみる

実装は簡単です。

create_post.rb
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ってのが追加されてるんですよね。ここで投稿者のクラスを判断してるみたいです。

schema.rb
  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

kamohicokamo
未熟なバックエンドエンジニア。かもが好き。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした