iT邦幫忙

2021 iThome 鐵人賽

DAY 28
0
自我挑戰組

海邊囝仔帶阿公阿嬤一起學 Ruby On Rails 系列 第 28

Day-28 : Model 多對多

今天我們要來講多對多
什麼是多對多
舉個例子:

商店has_many商品,商品也has_many商店,然後有一個第三方的資料表來存放兩邊Model的資訊。

接下來,奉上圖解!

https://ithelp.ithome.com.tw/upload/images/20211012/20140259hY9bum9Jto.png

那我們來開始做多對多拉
1.第三方Model僅需存放兩邊(商店和商品)Model的id,若想要額外增加一些欄位也是可行的
https://ithelp.ithome.com.tw/upload/images/20211012/20140259sGzrYsfLMb.png

這邊會用到references的寫法是因為會幫我們多做幾件事情

1.自動加上索引,加快查詢的速度
2.自動幫Model加上belongs_to

2.執行rails db:migrate
https://ithelp.ithome.com.tw/upload/images/20211012/20140259dl7FvFu1Va.png

3.讓我們來編輯planet這個檔案
https://ithelp.ithome.com.tw/upload/images/20211012/20140259YO3PffuNoc.png
上面有說到,因為我們使用了references的寫法,所以Ruby on Rails 會自動幫我們補上這兩行

 belongs_to :store
 belongs_to :product
  1. 回到Store Model,加上下面這兩行
    https://ithelp.ithome.com.tw/upload/images/20211012/20140259xYBMzmJdZk.png

5.Product Model加上這兩行
https://ithelp.ithome.com.tw/upload/images/20211012/20140259cUPHgBfkND.png

Planet Model同時belongs_to Store 以及 Product這兩個Model

Store 與 Product這兩個Model也都has_many Planet

關係圖解如下:
https://ithelp.ithome.com.tw/upload/images/20211012/20140259nJTpA6clXL.png

Store跟Product都是透過through Planet的紀錄,也就是第三方,才會知道商店與商品之間的關係

接下來,進入 rails c看看
1.先取任意兩間商店 => store1和 store2

> store1 = Store.find(1)
  Store Load (0.3ms)  SELECT "stores".* FROM "stores" WHERE "stores"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
 => #<Store id: 1, title: "良心商店", tel: nil, address: nil, user_id: 1, created_at: "2021-10-10 09:...
> store2 = Store.find(2)
  Store Load (0.3ms)  SELECT "stores".* FROM "stores" WHERE "stores"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
 => #<Store id: 2, title: "三眼怪行星", tel: nil, address: nil, user_id: 2, created_at: "2021-10-10 0...

2.隨意取出2件商品 => product1和 product2

> product1 = Product.find(1)
  Product Load (0.1ms)  SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
 => #<Product id: 1, name: "吃吃喝喝商店", description: nil, price: 0.2e3, is_available: nil, store_i...
> product2 = Product.find(2)
  Product Load (0.3ms)  SELECT "products".* FROM "products" WHERE "products"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
 => #<Product id: 2, name: "我想要飛", description: nil, price: 0.1e4, is_available: nil, store_id: 1...

3.把product1, product2丟給store2,把product1, product2丟給store1

> store2.products = [product1, product2]
  Product Load (0.1ms)  SELECT "products".* FROM "products" INNER JOIN "planets" ON "products"."id" = "planets"."product_id" WHERE "planets"."store_id" = ?  [["store_id", 2]]
  TRANSACTION (0.0ms)  begin transaction
  Planet Create (0.5ms)  INSERT INTO "planets" ("store_id", "product_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["store_id", 2], ["product_id", 1], ["created_at", "2021-10-12 15:24:01.126751"], ["updated_at", "2021-10-12 15:24:01.126751"]]
  Planet Create (0.1ms)  INSERT INTO "planets" ("store_id", "product_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["store_id", 2], ["product_id", 2], ["created_at", "2021-10-12 15:24:01.128415"], ["updated_at", "2021-10-12 15:24:01.128415"]]
  TRANSACTION (0.5ms)  commit transaction
 => [#<Product id: 1, name: "吃吃喝喝商店", description: nil, price: 0.2e3, is_available: nil, store_...
 > store1.products = [product1, product2]
  Product Load (0.4ms)  SELECT "products".* FROM "products" INNER JOIN "planets" ON "products"."id" = "planets"."product_id" WHERE "planets"."store_id" = ?  [["store_id", 1]]
  TRANSACTION (0.1ms)  begin transaction
  Planet Create (0.5ms)  INSERT INTO "planets" ("store_id", "product_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["store_id", 1], ["product_id", 1], ["created_at", "2021-10-12 15:24:12.087711"], ["updated_at", "2021-10-12 15:24:12.087711"]]
  Planet Create (0.1ms)  INSERT INTO "planets" ("store_id", "product_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["store_id", 1], ["product_id", 2], ["created_at", "2021-10-12 15:24:12.089616"], ["updated_at", "2021-10-12 15:24:12.089616"]]
  TRANSACTION (1.0ms)  commit transaction
 => [#<Product id: 1, name: "吃吃喝喝商店", description: nil, price: 0.2e3, is_available: nil, store_...
  1. 查詢看看,有幾間商店在賣product1:
> product1.stores.count
   (0.5ms)  SELECT COUNT(*) FROM "stores" INNER JOIN "planets" ON "stores"."id" = "planets"."store_id" WHERE "planets"."product_id" = ?  [["product_id", 1]]
 => 2
> product2.stores.count
   (0.5ms)  SELECT COUNT(*) FROM "stores" INNER JOIN "planets" ON "stores"."id" = "planets"."store_id" WHERE "planets"."product_id" = ?  [["product_id", 2]]
 => 2

因為在store1和store2都有販售product1與product2,因此為兩家,我們看一下上面的SQL語法,我們就可以得知,他不再對Store與Product要資料,而是與第三方planet進行查詢的動作!

與前面的差別在於,

他是跟第三方要資料!!!

今天分享到這,每天進步一點點/images/emoticon/emoticon23.gif

參考資料:為你自己學Ruby on Rails


上一篇
Day-27 : Model 一對多
下一篇
Day-29 : Model驗證
系列文
海邊囝仔帶阿公阿嬤一起學 Ruby On Rails 30

尚未有邦友留言

立即登入留言