如何限制current_user在每个时间段内向订单添加超过3个order_items?

时间:2016-06-24 12:28:01

标签: ruby-on-rails ruby model-view-controller

我正在Rails中建立一个具有特定销售模型的商店。我需要允许用户每30天只向其订单添加3个项目。 30天计数器应该在添加第一个order_item时开始。一旦30天到期,用户将能够添加3个订单。如果30天没有通过,例如,用户添加了两个ord​​er_items,他仍然可以在30天内再添加一个order_item。 因此,如果用户尝试添加超过3个项目以显示错误消息,并忽略将order_items保存到current_user的订单。

我有产品,订单,order_items,用户。我想我应该向用户模型添加一些内容,但我不确定是什么。

order_items_controller.rb

def create
    @order = current_order
    @order_item = @order.order_items.new(order_item_params)
    @order.user_id = current_user.id
    @order.save
    session[:order_id] = @order.id

  respond_to do |format|
    format.js { flash[:notice] = "ORDER HAS BEEN CREATED." } 
  end
  end
private
  def order_item_params
    params.require(:order_item).permit(:quantity, :product_id, :user_id)
  end
end

user.rb

class User < ActiveRecord::Base
  has_many :identities, dependent: :destroy
  has_many :order
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :omniauthable, :invitable, :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable
end

order_item.rb

class OrderItem < ActiveRecord::Base
  belongs_to :product
  belongs_to :order

  validates :quantity, presence: true, numericality: { only_integer: true, greater_than: 0 }
  validate :product_present
  validate :order_present

  before_save :finalize

  def unit_price
    if persisted?
      self[:unit_price]
    else
      product.price
    end
  end

  def total_price
    unit_price * quantity
  end

private
  def product_present
    if product.nil?
      errors.add(:product, "is not valid or is not active.")
    end
  end

  def order_present
    if order.nil?
      errors.add(:order, "is not a valid order.")
    end
  end

  def finalize
    self[:unit_price] = unit_price
    self[:total_price] = quantity * self[:unit_price]
  end
end

order.rb

class Order < ActiveRecord::Base
  belongs_to :order_status
  has_many :order_items
  before_create :set_order_status
  before_save :update_subtotal

  def subtotal
    order_items.collect { |oi| oi.valid? ? (oi.quantity * oi.unit_price) : 0 }.sum
  end
private
  def set_order_status
    self.order_status_id = 1
  end

  def update_subtotal
    self[:subtotal] = subtotal
  end
end

carts_controller.rb

class CartsController < ApplicationController
  def show
    @order_items = current_order.order_items
  end

的routes.rb

resources :order_items, only: [:create, :update, :destroy, :new]

form.html.erb

<%= form_for OrderItem.new,  html: {class: "add-to-cart"}, remote: true do |f| %>


        <div class="input-group">
          <%= f.hidden_field :quantity, value: 1, min: 1 %>
          <div class="input-group-btn">
            <%= f.hidden_field :product_id, value: product.id %>
            <%= f.submit "Add to Cart", data: { confirm: 'Are you sure that you want to order this item for current month?'}, class: "btn btn-default black-background white" %>
          </div>
        </div>
      <% end %>
    </div>

2 个答案:

答案 0 :(得分:2)

我会向用户模型添加begin_dateorder_counter。每次添加订单时,查看begin_date是否超过30天,然后将begin_date设置为实际日期。如果begin_date少于30​​天前,请增加计数器。如果计数器已经3拒绝订单。

您可以通过命令行参数

将列添加到用户表
rails generate migration AddOrderCounterToUser

这将在db / migrations中创建一个类:

class AddPartNumberToProducts < ActiveRecord::Migration
  def change
    add_column :users, :begin_date, :date
    add_column :users, :order_counter, :integer 
  end
end

UserController中添加其他属性,以便在user_params中允许这些属性。

然后更改OrderItemController

中的创建方法
def create
  now = Date.today
  success = false
  if current_user.begin_date && ((now - 30) < current_user.begin_date)
     if current_user.order_counter >= 3
        # deal with the case that order should not be created, 
        # for example redirect.
     else
       current_user.order_counter += 1
       current_user.save
       success = true
     end
  else 
    current_user.order_counter = 1
    current_user.begin_date = now
    current_user.save
    success = true
  end
  if success
    @order = current_order
    @order_item = @order.order_items.new(order_item_params)
    @order.user_id = current_user.id
    @order.save
    session[:order_id] = @order.id

    respond_to do |format|
      format.js { flash[:notice] = "ORDER HAS BEEN CREATED." } 
    end
  else 
    respond_to do |format|
      format.js { flash[:notice] = "CREATION NOT POSSIBLE." } 
    end
  end
end

您还可以将检查代码放在用户模型中的方法中,这样可以更清晰。

答案 1 :(得分:1)

通常,在某些情况下,如果您不想在rails中创建元素,则应选择通过验证器处理该情况。

你可以在这里采取嵌套方法: 将OrderItem路线嵌套在Order下(您可以在Rails Guides about Nested Routing找到有关嵌套的更多信息)

您应首先向first_item_added_at模型

添加新数据库列Order
rails generate migration AddFirstItemAddedAtToOrder

class AddFirstItemAddedAtToOrder < ActiveRecord::Migration
  def change
    add_column :orders, :first_item_added_at, :date
  end 
 end

嵌套时,您可以通过路径

创建新的OrderItem
POST /orders/:id/order_items

然后,您必须在OrderItem模型中添加验证器

class OrderItem < ActiveRecord::Base
    validate :only_3_items_in_30_days


  private

  def only_3_items_in_30_days
    now = Date.new
    days_since_first = now - order.first_item_added_at

    if order.order_items.count > 2 && days_since_first < 30
      errors.add(:base, 'only 3 items in 30 days are allowed')
    end
    true      # this is to make sure the validation chain is not broken in case the check fails
  end
end

现在你的控制器只需要创建一个新项并保存它

def create
  @item = OrderItem.new(item_params)
  if @item.save
     render <whatever_you_want_to_render>
  else
    # @item will contain the errors set in the model's validator
    render <error_reaction>
  end
end

private

def item_params
  params.require(:order_item).permit(
    :attribute_1,
    :attribute_2,
    :order_id       # << this one is very important
  )
end

如果你不希望嵌套OrderItem,那么模型仍然保持不变,但你的控制器看起来像:

def create
  @item = OrderItem.new(order_item_params)
  session[:order_id] = current_order.id

  if @item.save
    respond_to do |format|
      format.js { flash[:notice] = "ORDER HAS BEEN CREATED." } 
    end
  else
    render <handling for error>
  end 
end

private
def order_item_params
  base_params = params.require(:order_item)
                      .permit(:quantity, :product_id, :user_id)
  base_params.merge(order: current_order)
 end

请注意,我已将current_order.id添加到您的order_item_params方法。

编辑:将order_id: current_order.id替换为order: current_order,以便在实际保存之前提供与新OrderItem的关系