iT邦幫忙

2022 iThome 鐵人賽

DAY 24
1
Modern Web

一探紅寶石的神秘面紗 - Ruby 及 Rails入門介紹 系列系列 第 24

Day 24 - 進入Rails世界必須懂:Model 關聯性 (N:N)

  • 分享至 

  • xImage
  •  

多對多 N:N

關於多對多繼續用飲料店的例子舉例說明,可以想像我們很多間飲料店品牌,有很多飲料品項是相同的或不同,就會有需要有一張第三方表單來記錄:

  • 一間飲料店可以有很多種飲料
    • store has many drinks
  • 同一種飲料可以被很多間不同的飲料店販賣
    • drink has many stores

我們可以透過這張表單來知道,哪間店有賣哪些飲料,哪些飲料有被哪些店家販售。
他們之間的關係可以透過下面這張圖片看到,虛線的 has many 表示他們並不是真的直接有一對多的關聯,而是需要透過第三方表格來建立多對多關聯。

我們可以建立第三方的表格 drink_invest 來記錄飲料和店家之間的關係
rails g model drink_invest store:references drink:references

加上 reference 或是 belongs_to 都可以讓這個表格自動建立出對應的 store_id 及 drink_id 欄位,並且可以:

  • 自動加上索引(index),加快查詢
  • 自動幫 Model 加上 belongs_to

需要建立這樣的第三方表格來建立關聯,是屬於多對多的關聯
drink 和 store 之間沒有直接 has_many 的關聯, 但可以使用 has_many through 去透過 drink_invest 建立多對多的關聯。

接著可以把相關的 model 建立好關聯

store.rb

class Store < ApplicationRecord
  has_many :drink_invests
  has_many :drinks, through: :drink_invests 
end

drink.rb

class Drink < ApplicationRecord
  has_many :drink_invests
  has_many :stores, through: :drink_invests 
end

進入 rails console 試試看

先建立好我們需要的資料,店家、飲料、還有店家販售的飲料有哪些。

s1 = Store.create(name:"五石蘭", tel:"031234567", address:"新竹縣", owner_id:1)
s2 = Store.create(name:"渴不渴", tel:"03323232", address:"台北市", owner_id:1)

d1 = Drink.create(name: "紅茶", price: 30)
d2 = Drink.create(name: "綠茶", price: 30)
d3 = Drink.create(name: "珍珠奶茶", price: 60)

# 五石蘭 有賣 紅茶跟綠茶
s1.drinks = [d1, d2]
# 渴不渴 有賣 綠茶跟珍珠奶茶
s2.drinks = [d2, d3]

查詢相關的資訊之後會注意到sql語法的變化,開始透過第三方表格進行查詢。

# 有販售 d1 綠茶有幾家
d1.stores.count
   (0.6ms)  SELECT COUNT(*) FROM "stores" INNER JOIN "drink_invests" ON "stores"."id" = "drink_invests"."store_id" WHERE "drink_invests"."drink_id" = ?  [["drink_id", 1]]
 => 1 

# 有販售 d2 紅茶有幾家
d2.stores.count
   (0.5ms)  SELECT COUNT(*) FROM "stores" INNER JOIN "drink_invests" ON "stores"."id" = "drink_invests"."store_id" WHERE "drink_invests"."drink_id" = ?  [["drink_id", 2]]
 => 2 
 
 # 有販售 d3 奶茶有幾家
 d3.stores.count
   (0.4ms)  SELECT COUNT(*) FROM "stores" INNER JOIN "drink_invests" ON "stores"."id" = "drink_invests"."store_id" WHERE "drink_invests"."drink_id" = ?  [["drink_id", 3]]
 => 1 

可以發現,這些查詢的 SQL 語法中,都已經不是直接跟 Store 或 Drink 要資料,而是向 drink_invest 進行查詢。例如第二項查詢在 s1 跟 s2 都有 d2 這個商品,所以是 2 家。

「多對多」關連,在使用上就跟一般的「一對多」很像,但實際上的資訊都是記錄在第三方資料表裡。


參考資料:

  1. 為你自己學 Ruby on Rails

上一篇
Day 23 - 進入Rails世界必須懂:Model 關聯性 (1:N)
下一篇
Day 25 - 享受 Rails 的神兵利器:Gem (套件)
系列文
一探紅寶石的神秘面紗 - Ruby 及 Rails入門介紹 系列30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言