2014/07/22

Railsにてポリモーフィック関連のサンプルコード

Rails のポリモーフィック関連って便利ですよね。
でも、関連の貼り方までは結構サンプルコードが転がっているんですが、データ保存部分まで含めたものがあまりないような気がしたので、どうするのがいいかなと悩みつつ書いてみた。

has_oneの場合

has_one のサンプルとして、備考のモデルで作ってみた。
コメントは結構複数の場合があるけど、備考はたいてい 1 つしか持たないだろう。
# xxx_create_notes.rb
class CreateNotes< ActiveRecord::Migration
def change
create_table :notes do |t|
t.references :notable, polymorphic: true, null: false
t.text :note
end
add_index :notes, [:notable_type, :notable_id], unique: true
end
end
# note.rb
class Note < ActiveRecord::Base
belongs_to :notable, polymorphic: true
end
# notable.rb
module Notable
extend ActiveSupport::Concern
included do
after_save :save_note
has_one :note_store, as: :notable
end
def note
note_store.try(:note)
end
def note=(note)
build_note_store unless note_store
note_store.note = note
end
private
def save_note
note_store.try(:save)
end
end
view raw notable.rb hosted with ❤ by GitHub
1つしかオブジェクトを持たないので、note 属性で値を直接やりとりしたかったのでこんな風に。
Notable を include すればもう備考書き放題。

has_manyの場合

こんどは定番のタグをサンプルにしてみた。
# xxx_create_tags.rb
class CreateTags < ActiveRecord::Migration
def change
create_table :tags do |t|
t.string :name, null: false
end
add_index :tags, :name, unique: true
end
end
# xxx_create_taggings.rb
class CreateTaggings < ActiveRecord::Migration
def change
create_table :taggings do |t|
t.references :taggable, polymorphic: true, null: false
t.integer :tag_id, null: false
end
add_index :taggings, :tag_id
add_index :taggings, [:taggable_type, :taggable_id, :tag_id], unique: true, name: 'taggings_uk'
end
end
# tag.rb
class Tag < ActiveRecord::Base
unloadable
has_many :taggings, dependent: :destroy
end
# tagging.rb
class Tagging < ActiveRecord::Base
unloadable
belongs_to :tag
belongs_to :taggable, polymorphic: true
end
# taggable.rb
module Taggable
extend ActiveSupport::Concern
included do
has_many :taggings, as: :taggable
has_many :tags, through: :taggings
end
def add_tag(name)
return if tags.where(name: name).exists?
tag = Tag.where(name: name).first || Tag.new(name: name)
tags << tag
end
def remove_tag(name)
tag = Tag.where(name: name).first
tags.delete(tag) if tag && tags.include?(tag)
end
end
view raw taggable.rb hosted with ❤ by GitHub
関連テーブルをポリモーフィック関連にするわけですな。

雑感

* xxx_typexxx_id は Rails に任せる。自分では処理しない。
* 関連を定義してくれる module を作っておくと後が便利だと思う。
* t.references :taggable, polymorphic: true とするサンプルが多いけど、null が入ることは設計上ありえないんだから null: false を付けておいたほうがいいと思う。
* save_note では保存のみ実行しているけど、note_store.note が nil の場合はデータサイズ的なことを考えると note_store.destroy としてもいいと思う。(集約されるテーブルなので) まあサンプルなのでシンプルにした。

0 件のコメント :

コメントを投稿