ROR學(xué)習(xí)筆記(41)——簡陋的購物車功能

這兩天雖然游戲玩了一會查近,但好歹還是寫了點東西,都是邊看資料邊寫挤忙,所以顯得很混亂霜威,本來不想記下來,又怕以后忘記册烈,所以還是記下來好了戈泼。。。大猛。源碼還是在:
https://github.com/kamionayuki/shop
已經(jīng)有了products表了扭倾,所以只需要創(chuàng)建一個carts表就OK了,又因為每一個用戶有且只有一個購物車挽绩,所以carts表除了id列膛壹,就只有一列user_id。而User這個模型又devise生成(具體不就多說了)這樣唉堪,就有了products和carts兩張表模聋。購物車的話,應(yīng)該是多對多關(guān)系唠亚,一個cart可以有多個prodcuct撬槽,而一個product也可以存在多個cart中。所以最開始想到了用 has_and_belongs_to_many 來實現(xiàn) 多對多的模型關(guān)聯(lián)趾撵。所以創(chuàng)建了一張表:carts_products 只有兩列:cart_id 和 product_id然后就可以用關(guān)聯(lián)生成的方法cart.products.size來查詢cart中有多少product侄柔。也可以通過代碼來排重,查詢有多少種product占调,甚至可以查詢每一種product有多少個暂题。
到現(xiàn)在,一切看起來很順利究珊。薪者。。剿涮。但是有兩個隱患

  1. 如果這樣關(guān)聯(lián)的話言津,carts_products這張表,就會很大很大取试。如果一個cart中只有一個product悬槽,但這個product有1000個,那么瞬浓,carts_products表里就會有1000行數(shù)據(jù)初婆,明顯不科學(xué)。猿棉。磅叛。
  2. 沒有辦法去一件一件的刪除cart中的product。如果一個product有10件萨赁,用delete和destroy都是直接把這10件都刪除掉了弊琴。也不科學(xué)。杖爽。敲董。
    發(fā)愁了紫皇,想到了增加product_number這一列來記錄某一類product有多少個。但是怎么去操作呢臣缀?只能找資料了坝橡。。精置〖瓶埽看了一下,似乎 has_many 好像可以滿足脂倦。于是
rails g model CartRelationship cart_id:integer product_id:integer product_number:integer

生成一個中間模型番宁,同時用carts_relationships 這張表來做中間表進行操作。model中的代碼如下:
app/models/cart.rb

has_many :cart_relationships
has_many :products, through: :cart_relationships

app/models/product.rb

has_many :cart_relationships
has_many :carts, through: :cart_relationships

app/models/cart_relationship.rb

belongs_to :cart
belongs_to :product

這樣就關(guān)聯(lián)好了赖阻。但還是一樣蝶押,沒有辦法去一件一件的刪除cart中的product。并且用cart.products << @product的時候火欧,也不會對product_number這一列進行操作棋电。所以只能自己在cart.rb中寫add和delete的方法了
具體代碼如下:

def delete_from_cart(product)
    result = select_cart_relationship(product)
    if 1 == result.product_number
      self.products.destroy(product)
    else
      result.product_number -= 1
      result.save
    end
end

def add_to_cart(product)        
    if self.products.exists?(product) 
      result = select_cart_relationship(product)     
      result.product_number += 1      
    else
      self.products << product
      result = select_cart_relationship(product)
      result.product_number = 1
    end
    result.save
end

def select_cart_relationship(product)
    query = "select * from cart_relationships where cart_id = %d && product_id = %d" % [self.id, product.id]
    temp = CartRelationship.find_by_sql(query).first
end

其中的關(guān)鍵在 select_cart_relationship 這個方法。通過find_by_sql的方法進行查詢苇侵,得到一個數(shù)組赶盔,然后取第一個元素。
它返回的值是一個CartRelationship的實例榆浓,這個實例有id,cart_id,product_id以及product_number的屬性于未。
因此,我們就可以對Product_number進行更新和保存陡鹃。邏輯直接看代碼就OK了烘浦。

至此,我們可以通過cart.products來得到cart中擁有的product的種類
然后通過 select_cart_relationship(product).product_number這個方法來得到某一類product有多少個萍鲸。所以我們也可以用以下的代碼來得到這個cart中所有的product的個數(shù)

@cart.products.inject(0) do |result, p|
    result += @cart.select_cart_raletionship(p).product_number 
end

但是看后臺數(shù)據(jù)庫的執(zhí)行闷叉,如果cart中有100類product,那么就會執(zhí)行100次查詢猿推,雖然不知道性能會怎么樣片习,但看起來太嚇人了,所以又開始查資料蹬叭,最后用下面的方法實現(xiàn)了:

def product_count
    sql = ActiveRecord::Base.connection() 
    query = "select sum(product_number) sum from cart_relationships group by cart_id having cart_id=%d" % self.id
    sql.exec_query(query).sum["sum"]
end

具體里的的邏輯不太了解,但從表面上來看:是先實例化了一個數(shù)據(jù)庫鏈接的實例
然后通過這個實例調(diào)用exec_query來直接執(zhí)行mysql語句状知。
tips:可以先把mysql語句在數(shù)據(jù)庫中執(zhí)行一下秽五,看是否正確
這樣每查詢一次cart中所有的product的個數(shù),只需要執(zhí)行一次查詢就OK了饥悴。但由于是邊看資料邊寫的坦喘,所以每調(diào)用一次product_count方法盲再,就需要實例一個數(shù)據(jù)庫鏈接的實例,可能也是蠻耗資源的瓣铣?總之答朋,數(shù)據(jù)庫操作是實現(xiàn)了。
那么controller和views中就好辦了棠笑。只需要寫邏輯就可以了
*controller中梦碗,購物車肯定是用ajax更好,所以還是繼續(xù)ajax蓖救。

 class CartsController < ApplicationController
  before_action :authenticate_user!, only: [:show]
  def show
    @cart = current_user.cart
  end

  def add_to_cart
    if !current_user.nil?
      @product = Product.find(params[:product_id])
      @cart = current_user.cart
      @cart.add_to_cart(@product)
      @total_count = @cart.product_count
      @product_count = @cart.select_cart_relationship(@product).product_number
      flash.now[:notice] = "成功添加1件#{@product.name}到購物車"
      render 'change_cart'
    else
      flash[:notice] = 'please sign in first.'
      flash.keep(:notice)
      render js: "window.location = '/users/sign_in'"
    end
  end

  def delete_from_cart
    if !current_user.nil?
      @product = Product.find(params[:product_id])
      @cart = current_user.cart
      @cart.delete_from_cart(@product)
      @total_count = current_user.cart.product_count
      if @cart.products.exists?(@product)
        @product_count = @cart.select_cart_relationship(@product).product_number 
        flash.now[:notice] = "已經(jīng)刪除1件#{@product.name}"
        render 'change_cart'
      else
        flash.now[:notice] = "刪除了最后一件#{@product.name}"
        render 'delete_from_cart'
      end
    else
      flash[:notice] = 'please sign in first.'
      flash.keep(:notice)
      render js: "window.location = '/users/sign_in'"
    end
  end
end

views中(其中還學(xué)到了在js.erb文件中洪规,<%= render text%>可以嵌入在 "" 字符串中的任意位置):
change_cart.js.erb

var total_count = "Cart(<%= render text: @total_count %>)";
var summary = "共<%= render text: @cart.product_count %>件物品,總計<%= render text: @cart.total_summary %>";
//更新頁面上總個數(shù)和總價顯示
$(".cart_count").text(total_count);
$(".summary").text(summary);
//更新提示
$("#sidebar_nav").next().remove();
$("#sidebar_nav").after("<%= j render('layouts/flash') %>");
//更新列表中某一類product的個數(shù)
var xx = $("a[href$='add_to_cart/<%= render text: @product.id %>']");
xx.prev().text("<%= render text: @product_count %>");

delete_from_cart.js.erb

//與change_cart.js.erb類似循捺,只是把數(shù)量為0的product移除不顯示
var total_count = "Cart(<%= render text: @total_count %>)";
var summary = "共<%= render text: @cart.product_count %>件物品斩例,總計<%= render text: @cart.total_summary %>";
//更新頁面上總個數(shù)和總價顯示
$(".cart_count").text(total_count);
$(".summary").text(summary);
//更新提示
$("#sidebar_nav").next().remove();
$("#sidebar_nav").after("<%= j render('layouts/flash') %>");
//移除數(shù)量為0的product的顯示
var xx = $("a[href$='add_to_cart/<%= render text: @product.id %>']");
xx.parent().parent().remove();

好了,就這樣吧从橘。念赶。。估計過段時間再來看也不大會看得懂恰力。叉谜。。牺勾。正罢。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市驻民,隨后出現(xiàn)的幾起案子翻具,更是在濱河造成了極大的恐慌,老刑警劉巖回还,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件裆泳,死亡現(xiàn)場離奇詭異,居然都是意外死亡柠硕,警方通過查閱死者的電腦和手機工禾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蝗柔,“玉大人闻葵,你說我怎么就攤上這事⊙⑸ィ” “怎么了槽畔?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長胁编。 經(jīng)常有香客問我厢钧,道長鳞尔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任早直,我火速辦了婚禮寥假,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘霞扬。我一直安慰自己糕韧,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布祥得。 她就那樣靜靜地躺著兔沃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪级及。 梳的紋絲不亂的頭發(fā)上乒疏,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天,我揣著相機與錄音饮焦,去河邊找鬼怕吴。 笑死,一個胖子當(dāng)著我的面吹牛县踢,可吹牛的內(nèi)容都是我干的转绷。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼硼啤,長吁一口氣:“原來是場噩夢啊……” “哼议经!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起谴返,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤煞肾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后嗓袱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體籍救,經(jīng)...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年渠抹,在試婚紗的時候發(fā)現(xiàn)自己被綠了蝙昙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,444評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡梧却,死狀恐怖奇颠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情放航,我是刑警寧澤大刊,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站三椿,受9級特大地震影響缺菌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜搜锰,卻給世界環(huán)境...
    茶點故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一伴郁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蛋叼,春花似錦焊傅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至歌馍,卻和暖如春握巢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背松却。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工暴浦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晓锻。 一個月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓歌焦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親砚哆。 傳聞我的和親對象是個殘疾皇子独撇,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,455評論 2 359

推薦閱讀更多精彩內(nèi)容