前幾章已經介紹了一對ㄧ、一對多了,但現在有個小問題,一件商品可能會在很多間商店被賣,多間商店可能會賣同件商品,但我不能確定這件商品在哪間商店有被販賣。
舉例來說:一個限量麵包可能在 A、C 商店有賣,但是 B 商店沒有賣;這時,我們就需要一個表格去紀錄商品跟商店的資訊,有了這個表格我們就可以知道這些限量麵包究竟在哪裡有被販賣,以及每個商店有賣這些限量麵包。
這個概念就是多對多關聯,通常會需要一個第三方資料表來記錄這兩邊的 Model 的資訊。
先用一張圖來第三方資料表的架構:
透過上面這張圖,可以發現原本 Store has_many
Product 跟 Product belongs_to
Store 的關聯性已經不見了,取而代之的是用 WareHouse 記錄這兩邊的資訊。
我們先建立一個 WareHouse Model :
$ rails g model WareHouse store:references product:references
當加上 reference
或 belongs_to
後,會使這個資料表自動建立出對應的 store_id
跟 product_id
欄位,還會做以下的事情:
belongs_to
。class WareHouse < ApplicationRecord
belongs_to :store
belongs_to :product
end
接著我們要加上 has_many
及 through
:
# store.rb
class Store < ApplicationRecord
belongs_to :owner
has_many :ware_houses
has_many :products, through: :ware_houses
end
# product.rb
class Product < ApplicationRecord
has_many :ware_houses
has_many :stores, through: :ware_houses
end
小提醒:
如果是從一對一開始做的朋友們,在這章請記得移除 products
中的 foreign_key
及 store_id
的欄位。
作法如下:
$ rails g migration RemoveStoreForeignKeyFromProduct
foreign_key
及 store_id
class RemoveStoreForeignKeyFromProduct < ActiveRecord::Migration[6.1]
def change
remove_foreign_key :products, :stores
remove_column :products, :store_id
end
end
rails db:migrate
首先我們先建立 store1 跟 store2:
store1 = Store.create(title: "超夯麵包店", address: "台北")
store2 = Store.create(title: "貓貓集散地", address: "衡陽路")
接著建立一些商品:
product1 = Product.new(name: "菠蘿麵包", price: 50)
product2 = Product.new(name: "檸檬塔", price: 70)
product3 = Product.new(name: "提拉米蘇", price: 150)
product4 = Product.create(name:"貓貓都可以吃的麵包", price: 200)
接下來,我們就可以將這些商品放在指定店家來販賣:
store1.products = [product1,product2,product3,product4]
store2.products << product4
這邊可以觀察一下 SQL 語法中,他將兩者的關聯分別存到 WareHouse 中的 store_id
r及 product_id
。
最後,我們來看一下有幾間店在賣 product4
:
>> product4.stores.count
(0.8ms) SELECT COUNT(*) FROM "stores" INNER JOIN "ware_houses" ON "stores"."id" = "ware_houses"."store_id" WHERE "ware_houses"."product_id" = ? [["product_id", 4]]
=> 2
我們再一次看 SQL 語法,這些操作都不是直接跟 Store 或 Product 要資料了,而是向 WareHouse 進行查詢。
多對多關聯跟一對多關聯使用上相似,但多對多關聯的資訊會紀錄在第三方資料表中。
參考資料: