關於多對多繼續用飲料店的例子舉例說明,可以想像我們很多間飲料店品牌,有很多飲料品項是相同的或不同,就會有需要有一張第三方表單來記錄:
我們可以透過這張表單來知道,哪間店有賣哪些飲料,哪些飲料有被哪些店家販售。
他們之間的關係可以透過下面這張圖片看到,虛線的 has many 表示他們並不是真的直接有一對多的關聯,而是需要透過第三方表格來建立多對多關聯。
我們可以建立第三方的表格 drink_invest 來記錄飲料和店家之間的關係rails g model drink_invest store:references drink:references
加上 reference
或是 belongs_to
都可以讓這個表格自動建立出對應的 store_id 及 drink_id 欄位,並且可以:
需要建立這樣的第三方表格來建立關聯,是屬於多對多的關聯
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
先建立好我們需要的資料,店家、飲料、還有店家販售的飲料有哪些。
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 家。
「多對多」關連,在使用上就跟一般的「一對多」很像,但實際上的資訊都是記錄在第三方資料表裡。
參考資料: