購物車model怎麼設計比較好


#1

1.把Cart存入Table, 裡面有Product_id, 這樣可直接找產品,也能避免多次Select,但缺點是使用者加入購物車以後一直未刪除或Session Expire這張表就會越來越肥
2.把Product_id存到Session, 然後建立一個CartItem的model, 裡面再寫一個find_product的method, 但缺點是五個product就會select5次


#2

不管購物的商品存在 Cache 或是 Mysql 中,如果有多個商品都只需要 select 一次才對。

Product.where("id IN(1,2,3,5)")

等同於 SQL 中

SELECT * FROM products WHERE id IN (1,2,3,5)

但是存在 SQL 需要先存在一個 CartItem 的 Table。
所以要多 sql 一次。

cart_ids = CartItem.where("user_id=?", current_user.id ).select("product_id").join(",")
products = Product.where("id IN( ? )", cart_ids)

這樣就找出來了

CartItem 的 schema

user_id => 擁有者
product_id => 對應的商品id
mount => 商品數量

有些人會喜歡多一個 Cart Model當做購物車,這時候 user_id 就換成 cart_id。
如果你只有一個購物車系統用 user_id 就 ok 了。


#3

建議改成

Product.where(id: [1, 2, 3, 5])

在 ActiveRecord 中用 array 來取代直接的 IN Query 是有好處的,在於平臺可攜性,說不定不是所有的 db 都叫做 IN (這裡是說「說不定」,因為資料庫系統不是我們設計的嘛(ry)

然後

這個部分還可以利用 includesreferences 作查詢,這樣就真的只要查一次就好了(AR 會幫我們轉 JOIN 跟 sub-query,然後把取出的資料作 cache):

cart_items = current_user.cart_items.includes(:product).references(:product) # 這裡用關聯來取購物車項目
products = cart_items.map(&:product).uniq! # Array#uniq! 用來剔除重複 Project

BTW, CartItem.where("user_id=?", current_user.id ) 這樣的寫法真的要避免,用 CartItem.where(user: current_user) 會好更多,理由同上


#4

不錯的觀點 學習了 :slight_smile:
以新手教學的角度來說,希望新手多接觸點 raw sql,畢竟大多新手是非本科,對於 sql 的認識比較深入的不多,有時可能會搞不清楚 ORM 包裝後的語法糖和真正 sql 下的效果,如果能多下點 sql 語法,會對學習很有幫助。
以實務的角度來說:抽換關聯式 db 情況較低 ( 但也不能說沒有 ) 。更多情況需要置換的指令會是 db adapter 沒有提供轉換的語法,例如大範圍的 join group 之類的。也有很多情況會用的會是 nosql 來解決特定的問題。不過以設計模式的角度來說,用一個通用的指令的確是漂亮的。
includes 應該是用兩個sql再進行組合,不確定 join 什麼時候會發生。subquery 本質上也是 query 兩次,並沒有什麼差別。與其依賴 rails 的機制幫你做,自己弄髒手有時候思考的多一點、選擇權多一點。比如說因為效能考量,我只想要用 subquery 而非 join ,如果不太確定 includes 什麼情況下會幫你下 join 什麼情況下會幫你下 subquery,這時候你就得花很多時間看程式碼。不同的版本搞不好實作的方式不一樣。所以~對於漂亮的寫法我是覺得有的話很好,沒有的話,就寫好測試之後再重構就好。


#5

其實你說的很對,可是這裡要用 Rails 的核心思維思考,就是他已經給你 ActiveRecord 這麼好的輪子了,不應該自己把胎框拔下來自己作一個上去,除非真的非必要不然不要下 raw sql (慣例優先跟 DRY 在這裡都很重要)