2016-06-24 68 views
5

Buduję sklep w Railsach, który ma określony model sprzedaży. Muszę zezwolić użytkownikowi na dodanie tylko 3 elementów do jego zamówienia na 30 dni. Licznik 30-dniowy powinien rozpoczynać się po dodaniu pierwszego order_item. Po upływie 30 dni użytkownik będzie mógł dodać 3 zamówienia. Jeśli 30 dni nie minęło i na przykład, użytkownik dodaje dwa elementy order_items, to nadal może dodać jeszcze jedną order_item w ciągu 30 dni. Tak więc, jeśli użytkownik spróbuje dodać więcej niż 3 elementy, wyświetli komunikat o błędzie i zignoruje zapisywanie order_items w zamówieniu current_user.Jak ograniczyć użytkownika current_user do dodawania więcej niż 3 order_items do zamówienia za okres?

Mam produkty, zamówienia, order_items, użytkowników. Myślę, że powinienem dodać coś do modelu użytkownika, ale nie jestem pewien co.

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> 

Odpowiedz

2

chciałbym dodać begin_date i order_counter do modelu użytkownika. Za każdym razem, gdy dodajesz zamówienie, sprawdź, czy begin_date jest więcej niż 30 dni temu, a następnie zmień begin_date na aktualną datę. Jeśli wartość begin_date jest mniejsza niż 30 dni temu, zwiększ licznik. A jeśli licznik już 3 odrzuci zamówienie.

można dodać kolumny do tabeli użytkownika argument wiersza poleceń

rails generate migration AddOrderCounterToUser 

Spowoduje to utworzenie klasy w db/migracje:

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

Dodaj dodatkowe atrybuty w swoim UserController do zezwól na nie w user_params.

Następnie zmień metodę tworzenia w 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 

Można również umieścić kodu sprawdzającego w metodzie w modelu użytkownika, które mogłyby być czystsze.

+0

dostaję ten błąd w moim terminalu gdy próbuję dodać element do porządku: ArgumentError (porównanie Data z nieudaną próbą): app/controllers/order_items_controller.rb: 4: in '<' app/controllers/order_items_controller.rb: 4: in' create ' –

+0

Zaktualizowałem moją odpowiedź i dodałem sprawdzenie czy begin_date is Nil (w tym przypadku musisz ustawić begin_date, więc przejdź do innego przypadku) – irene

+0

Nie mam żadnych błędów, ale wciąż mogę dodać nieograniczoną ilość order_items .. –

1

Ogólnie, gdy nie chcesz tworzyć elementu w szynach w pewnych okolicznościach, powinieneś wybrać obsługę sytuacji za pomocą walidatorów.

można podjąć gniazdowania podejść tutaj: Nest to Twój OrderItem tras w Order (można znaleźć dalsze informacje na temat gniazdowania w Rails Guides about Nested Routing)

Należy rozpocząć dodając nową kolumnę bazy first_item_added_at wam Order modelu

rails generate migration AddFirstItemAddedAtToOrder 

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

Kiedy zagnieżdżanie, należy utworzyć nowy OrderItem drogą

POST /orders/:id/order_items 

Następnie trzeba dodać walidator do modelu 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 

Teraz tylko kontroler musi utworzyć nowy element i zapisać go

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 

Jeśli nie chcą gniazdo OrderItem, model nadal pozostaje taki sam, ale kontroler powinien wyglądać następująco:

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 

Należy pamiętać, że dodałem current_order.id do Twojej metody order_item_params.

EDIT: zastąpiony order_id: current_order.id przez order: current_order zapewnić odniesieniu do nowego OrderItem zanim zostanie on faktycznie zapisany

+0

Dostaję błąd: NoMethodError (metoda niezdefiniowany 'first_item_added_at” do zera: NilClass): app/models/order_item.rb: 46: w 'only_3_items_in_30_days' app/controllers/order_items_controller.rb: 6:' tworzyć ' –

+0

Przyczyny tego mogą zależeć od wybranego rozwiązania sterownika. Czy zastosowałeś podejście zagnieżdżone, czy drugie? W obu przypadkach pierwsze pytanie brzmi: czy kolejność, do której dodajesz elementy, jest już zachowana, gdy wywołasz 'OrderItemsController'? Po drugie, pomyliłem moment, w którym relacja między dwoma obiektami jest generowana przez ActiveRecord. Rozwiązanie tutaj zależy trochę od wersji kontrolera, którą wybrałeś. Zaktualizowałem metodę 'order_item_params' w drugim przykładzie. – Dervol

+0

Używam drugiej metody, o której wspomniałeś bez zagnieżdżania i zmieniłem parametry, ale teraz jest to błąd: ActionController :: RoutingError (niezdefiniowana zmienna lokalna lub metoda 'current_order 'dla OrderItemsController: Class): app/controllers/order_items_controller.rb: 15: w ' ' aplikacja/kontrolery/order_items_controller.rb: 1: w' ' –

Powiązane problemy