ROR学习笔记(41)——简陋的购物车功能

这两天虽然游戏玩了一会,但好歹还是写了点东西,都是边看资料边写,所以显得很混乱,本来不想记下来,又怕以后忘记,所以还是记下来好了。。。。源码还是在:
https://github.com/kamionayuki/shop
已经有了products表了,所以只需要创建一个carts表就OK了,又因为每一个用户有且只有一个购物车,所以carts表除了id列,就只有一列user_id。而User这个模型又devise生成(具体不就多说了)这样,就有了products和carts两张表。购物车的话,应该是多对多关系,一个cart可以有多个prodcuct,而一个product也可以存在多个cart中。所以最开始想到了用 has_and_belongs_to_many 来实现 多对多的模型关联。所以创建了一张表:carts_products 只有两列:cart_id 和 product_id然后就可以用关联生成的方法cart.products.size来查询cart中有多少product。也可以通过代码来排重,查询有多少种product,甚至可以查询每一种product有多少个。
到现在,一切看起来很顺利。。。。但是有两个隐患

  1. 如果这样关联的话,carts_products这张表,就会很大很大。如果一个cart中只有一个product,但这个product有1000个,那么,carts_products表里就会有1000行数据,明显不科学。。。
  2. 没有办法去一件一件的删除cart中的product。如果一个product有10件,用delete和destroy都是直接把这10件都删除掉了。也不科学。。。
    发愁了,想到了增加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

这样就关联好了。但还是一样,没有办法去一件一件的删除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

其中的关键在 select_cart_relationship 这个方法。通过find_by_sql的方法进行查询,得到一个数组,然后取第一个元素。
它返回的值是一个CartRelationship的实例,这个实例有id,cart_id,product_id以及product_number的属性。
因此,我们就可以对Product_number进行更新和保存。逻辑直接看代码就OK了。

至此,我们可以通过cart.products来得到cart中拥有的product的种类
然后通过 select_cart_relationship(product).product_number这个方法来得到某一类product有多少个。所以我们也可以用以下的代码来得到这个cart中所有的product的个数

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

但是看后台数据库的执行,如果cart中有100类product,那么就会执行100次查询,虽然不知道性能会怎么样,但看起来太吓人了,所以又开始查资料,最后用下面的方法实现了:

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

具体里的的逻辑不太了解,但从表面上来看:是先实例化了一个数据库链接的实例
然后通过这个实例调用exec_query来直接执行mysql语句。
tips:可以先把mysql语句在数据库中执行一下,看是否正确
这样每查询一次cart中所有的product的个数,只需要执行一次查询就OK了。但由于是边看资料边写的,所以每调用一次product_count方法,就需要实例一个数据库链接的实例,可能也是蛮耗资源的?总之,数据库操作是实现了。
那么controller和views中就好办了。只需要写逻辑就可以了
*controller中,购物车肯定是用ajax更好,所以还是继续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] = "已经删除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中(其中还学到了在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 %>";
//更新页面上总个数和总价显示
$(".cart_count").text(total_count);
$(".summary").text(summary);
//更新提示
$("#sidebar_nav").next().remove();
$("#sidebar_nav").after("<%= j render('layouts/flash') %>");
//更新列表中某一类product的个数
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类似,只是把数量为0的product移除不显示
var total_count = "Cart(<%= render text: @total_count %>)";
var summary = "共<%= render text: @cart.product_count %>件物品,总计<%= render text: @cart.total_summary %>";
//更新页面上总个数和总价显示
$(".cart_count").text(total_count);
$(".summary").text(summary);
//更新提示
$("#sidebar_nav").next().remove();
$("#sidebar_nav").after("<%= j render('layouts/flash') %>");
//移除数量为0的product的显示
var xx = $("a[href$='add_to_cart/<%= render text: @product.id %>']");
xx.parent().parent().remove();

好了,就这样吧。。。估计过段时间再来看也不大会看得懂。。。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容