iT邦幫忙

2022 iThome 鐵人賽

DAY 28
0
Modern Web

Ruby新手村的礦工日記系列 第 28

[ Day 28 ] Rails : Model 的關聯性(多對多)

  • 分享至 

  • xImage
  •  

前幾章已經介紹了一對ㄧ、一對多了,但現在有個小問題,一件商品可能會在很多間商店被賣,多間商店可能會賣同件商品,但我不能確定這件商品在哪間商店有被販賣。
舉例來說:一個限量麵包可能在 A、C 商店有賣,但是 B 商店沒有賣;這時,我們就需要一個表格去紀錄商品跟商店的資訊,有了這個表格我們就可以知道這些限量麵包究竟在哪裡有被販賣,以及每個商店有賣這些限量麵包。

多對多

這個概念就是多對多關聯,通常會需要一個第三方資料表來記錄這兩邊的 Model 的資訊。
先用一張圖來第三方資料表的架構:
第三方表格
透過上面這張圖,可以發現原本 Store has_many Product 跟 Product belongs_to Store 的關聯性已經不見了,取而代之的是用 WareHouse 記錄這兩邊的資訊。

建立model

我們先建立一個 WareHouse Model :

$ rails g model WareHouse store:references product:references

當加上 referencebelongs_to 後,會使這個資料表自動建立出對應的 store_idproduct_id 欄位,還會做以下的事情:

  • 自動加上索引(index),加快查詢速度。
  • 自動幫 Model 加上 belongs_to
class WareHouse < ApplicationRecord
  belongs_to :store
  belongs_to :product
end

接著我們要加上 has_manythrough

# 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_keystore_id 的欄位。
作法如下:

  1. 先建一個 Migration 檔
$ rails g migration RemoveStoreForeignKeyFromProduct 
  1. 移除 foreign_keystore_id
class RemoveStoreForeignKeyFromProduct < ActiveRecord::Migration[6.1]
  def change
    remove_foreign_key :products, :stores
    remove_column :products, :store_id
  end
end
  1. 記得 rails db:migrate

進到 console

首先我們先建立 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]

store1.products

store2.products << product4

store2.products
這邊可以觀察一下 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 進行查詢。

多對多關聯跟一對多關聯使用上相似,但多對多關聯的資訊會紀錄在第三方資料表中。

參考資料:

  1. 為自己學 Ruby on Rails

上一篇
[ Day 27 ] Rails : Model 的關聯性(一對多)
下一篇
[ Day 29 ] Rails : 進資料庫先過資料驗證 (Validation) 這關!
系列文
Ruby新手村的礦工日記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言