วันอังคาร, มีนาคม 10, 2552

Single Table Inheritance

อุตสาห์ลง mac port จนสำเร็จ ดันเจอปัญหากับ X11 ซะนี่ สุดท้ายก็ใช้ TextMate ต่อไป..
บ่นนิดหน่อยก่อนเข้าเรื่อง วันนี้ได้มีโอกาศใช้ความสามารถ Single Table Inheritance ของ Active Record ซะทีครับ หลังจากที่ใช้ก็เกิดความประทับใจสุดๆ จดต้องเอามาแชร์ให้รู้โดยทั่วกัน



จากรูป ถ้าคิดแบบซื่อๆ การสร้าง Object 3 ตัวก็ต้องมี 3 ตาราง อันแรกเก็บ Player อันที่สอง สาม เก็บ Footballer กับ Cricketer แล้ว link กลับไปที่ Player สร้างแบบนี้ Normalize สุดๆ แต่ใช้งานยากที่สุดๆ เช่นเดียวกัน ดังนั้นถ้าพิจารณาแล้ว Footballer กับ Cricketer เก็บข้อมูลไม่ต่างกันมาก น่าจะเอาสาม table มารวมเป็น table เดียวมากกว่า วิธีการงานก็ง่าย เพราะ Active Record มันเตรียมมาให้เราแล้วครับ
ขั้นแรกให้สร้าง Table Player ให้มี column ชื่อ type

create_table :players do |t|
  t.string :type t.string :name t.timestamps 
end

จากนั้นก็สร้าง model สามตัว สามไฟล์

#/app/model/player.rb 
class Player < ActiveRecord::Base 
end

#/app/model/footbller.rb 
class Footballer < Player 
end

#app/model/cricketer.rb 
class Cricketer < Player 
end
เสร็จแล้วครับ ทดสอบได้โดยเปิด ruby script/console แล้วลอง

f = Footballer.new 
f.name ="Piyapong" f.save

ในฐานข้อมูลจะแสดง type เป็น Footballer ให้อัตโนมัติ ที่นี้ลองแก้ model เพิ่มเติมอีกหน่อย

#/app/model/player.rb 
class Player < ActiveRecord::Base 
end 

#/app/model/footbller.rb 
class Footballer < Player 
  def iam 
    "Footballer" 
  end 
end

#app/model/cricketer.rb 
class Cricketer < Player 
  def iam 
    "Cricketer" 
  end 
end
ลองใส่ข้อมูลลองใน ฐานข้อมูลสัก 4-5 record ให้มี Type เป็น "Cricketer" กับ "Footballer" สลับกัน เปิด irb แล้วลอง

players = Player.find(:all) players.each do |player| 
  puts player.iam 
end 
player แต่ละคนจะรู้ว่าตัวเองเป็นอะไร โดยที่เราไม่ต้องมา if ให้เสียบรรทัด ทำให้ code แยกเป็นสัดส่วน และดูสะอาดตาเป็นยิ่งนัก

Reference: hmartinfowler

เพิ่มเติม :) ในกรณีที่เราไม่ต้องการใช้ column type ในการทำ STI เราสามารถกำหนด manual ได้ด้วยคำสั่ง

#/app/model/player.rb 
class Player < ActiveRecord::Base 
  set_inheritance_column :player_type 
end

คำสั่งนี้ยังใช้ในกรณีที่เราต้องการ column type ไปใช้ทำอย่างอื่นที่ไม่ใช่ sit ก็กำหนดให้ column นี้เป็นอย่างอื่นไปเลย

#/app/model/player.rb 
class Player < ActiveRecord::Base 
  set_inheritance_column :no_sit 
end
blog comments powered by Disqus