でも、関連の貼り方までは結構サンプルコードが転がっているんですが、データ保存部分まで含めたものがあまりないような気がしたので、どうするのがいいかなと悩みつつ書いてみた。
has_oneの場合
has_one
のサンプルとして、備考のモデルで作ってみた。コメントは結構複数の場合があるけど、備考はたいてい 1 つしか持たないだろう。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 |
note
属性で値を直接やりとりしたかったのでこんな風に。Notable を include すればもう備考書き放題。
has_manyの場合
こんどは定番のタグをサンプルにしてみた。
関連テーブルをポリモーフィック関連にするわけですな。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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 | |
雑感
*
* 関連を定義してくれる module を作っておくと後が便利だと思う。
*
*
xxx_type
と xxx_id
は Rails に任せる。自分では処理しない。* 関連を定義してくれる module を作っておくと後が便利だと思う。
*
t.references :taggable, polymorphic: true
とするサンプルが多いけど、null が入ることは設計上ありえないんだから null: false
を付けておいたほうがいいと思う。*
save_note
では保存のみ実行しているけど、note_store.note
が nil の場合はデータサイズ的なことを考えると note_store.destroy
としてもいいと思う。(集約されるテーブルなので)
まあサンプルなのでシンプルにした。