如何在父母中正确嵌入mongoid文件的副本?

时间:2015-11-07 20:21:39

标签: ruby-on-rails ruby mongodb mongoid

我有像这样定义的Mongoid类(为了简洁省略了不相关的字段):

class Product
  include Mongoid::Document

  belongs_to :category
end

class Order
  include Mongoid::Document

  embeds_many :order_items
end

class OrderItem
  include Mongoid::Document

  embeds_one :product
  embedded_in :order

  field :count, type: Integer
end

这意味着,我将产品保存在单独的收集中,当用户进行购买时,我想嵌入她在订单文档中购买的所有产品的副本(因此我有她购买的确切商品的快照以及将来编辑的内容不要改变已经购买的产品。 这是制作嵌入式副本的正确方法吗?还是应该更改我的架构?例如,创建新的文档类,如EmbeddedProduct,并从Product复制相关字段?

目前的解决方案似乎有效,但从我在文档和论坛中看到的内容看来,文档应该是嵌入式的,也可以是单独的集合,而不是两者。

1 个答案:

答案 0 :(得分:0)

这个问题似乎没有得到任何关注:)

无论如何,我认为最好的方法是不将产品嵌入order_item,而是将相关字段从产品复制到order_item。

解决方案如下所示

class Product
  include Mongoid::Document

  # product fields like name, price, ...

  belongs_to :category
end

class Order
  include Mongoid::Document

  # order fields like date, total_amount, address ...

  embeds_many :order_items
end

class OrderItem
  include Mongoid::Document
  include Mongoid::MoneyField

  embedded_in :order

  # you can also store reference to original product
  belongs_to :original_product

  field :count, type: Integer
  field :name
  field :ean
  field :sku
  money_field :final_price

  def self.from_product(product)
    item = self.new
    # ... assign fields from product, eg: 
    # item.name = product.name

    item
  end
end

当用户点击“添加到购物车”时,您可以执行以下操作:

def add
  # check if product is already in the cart
  order_item = @order.items.detect {|item| item.product_id == params[:product_id]}

  # if not, create from product in database
  if order_item.nil?
    product = Product.find(params[:product_id])
    order_item = OrderItem.from_product(product)

    # and add to items array
    @order.items.push(order_item)
  end

  # set count that we got from product form
  # (we can do this since order_item is 
  # also reference to the order_item inside array of order_items)
  order_item.count = params[:count]

  # emit event
  event = Events::CartItemChanged.new(order_item: order_item, order: @order, request_params: params)
  broadcast :cart_item_changed, event

  # no need to call validations since we're just adding an item
  @order.save! :validate => false

  redirect_to cart_path
end