2016-06-24 25 views
5

Sto costruendo un negozio in Rails che ha uno specifico modello di vendita. Devo consentire a un utente di aggiungere solo 3 articoli al suo ordine per 30 giorni. Il contatore dei 30 giorni dovrebbe iniziare con l'aggiunta del primo order_item. Alla scadenza di 30 giorni, l'utente sarà in grado di aggiungere 3 ordini. Se non sono passati 30 giorni e per un esempio, l'utente aggiunge due order_item, gli sarebbe comunque consentito aggiungere un altro order_item entro 30 giorni. Così pure se l'utente tenta di aggiungere più di 3 elementi per mostrare un messaggio di errore e ignorare il salvataggio di order_items nell'ordine di current_user.Come limitare current_user all'aggiunta di più di 3 order_items a un ordine per periodo di tempo?

Ho prodotti, ordini, order_items, utenti. Immagino che dovrei aggiungere qualcosa al modello dell'utente, ma non sono sicuro di cosa.

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> 

risposta

2

vorrei aggiungere un begin_date e un order_counter al modello utente. Ogni volta che aggiungi un ordine, controlla se lo begin_date è più di 30 giorni fa, quindi imposta begin_date nella data effettiva. Se la data_inizio è inferiore a 30 giorni fa, aumentare il contatore. E se il contatore è già 3 rifiutare l'ordine.

È possibile aggiungere le colonne alla tabella utente l'argomento della riga di comando

rails generate migration AddOrderCounterToUser 

Questo creerà una classe in db/migrazioni:

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

Aggiungere gli attributi aggiuntivi nella vostra UserController a autorizzarli in user_params.

quindi modificare il metodo di creare nella vostra 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 

Si può anche mettere il codice di controllo in un metodo nel modello di utente, che sarebbe più pulito.

+0

sto ottenendo questo errore nel mio terminale quando provo ad aggiungere un elemento a un ordine: ArgumentError (confronto Data con errore nullo): app/controller/order_items_controller.rb: 4: in '<' app/controller/order_items_controller.rb: 4: in' create' –

+0

Ho aggiornato la mia risposta e aggiunto un controllo se start_date è aggiornato Nil (in questo caso devi impostare begin_date, quindi vai nel caso else) – irene

+0

Non ho errori ma sono ancora in grado di aggiungere unlimited order_items .. –

1

In genere, quando non si desidera creare un elemento nei binari in determinate circostanze, è necessario scegliere di gestire la situazione tramite i validatori.

Si potrebbe prendere un annidamento approcci qui: Nest tuoi OrderItem rotte sotto Order (si possono trovare ulteriori informazioni sulla nidificazione nel Rails Guides about Nested Routing)

Si dovrebbe iniziare con l'aggiunta di una nuova colonna di database first_item_added_at a voi Order modello

rails generate migration AddFirstItemAddedAtToOrder 

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

Quando nidificazione, è necessario creare un nuovo OrderItem per via

POST /orders/:id/order_items 

Poi, si deve aggiungere un validatore al modello 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 

Ora il controller ha solo bisogno di creare un nuovo elemento e salvarlo

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 

Se non si desidera nido OrderItem, rispetto al modello rimane sempre la stessa, ma il controller sarà simile:

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 

Si prega di notare che ho aggiunto current_order.id al metodo order_item_params.

EDIT: sostituito da order_id: current_order.idorder: current_order per fornire la relazione al nuovo OrderItem prima che sia effettivamente salvato

+0

Ricevo un errore: NoMethodError (metodo non definito 'first_item_added_at 'per nil: NilClass): app/models/order_item.rb: 46: in' only_3_items_in_30_days' app/controller/order_items_controller.rb: 6: in 'create ' –

+0

I motivi possono dipendere dalla soluzione del controller che hai scelto. Hai preso l'approccio annidato o l'altro? In entrambi i casi, la prima domanda sarebbe: è l'ordine a cui si aggiungono elementi già persistenti quando si chiama "OrderItemsController"? La seconda cosa potrebbe essere che ho sbagliato il momento in cui la relazione tra due oggetti è generata da ActiveRecord. La soluzione qui dipende un po 'dalla versione del controller che hai scelto. Ho aggiornato il metodo 'order_item_params' nel secondo esempio. – Dervol

+0

Sto usando il secondo metodo che hai citato senza nidificazione e ho modificato i parametri ma ora questo è l'errore: ActionController :: RoutingError (variabile locale non definita o metodo 'current_order 'per OrderItemsController: Class): app/controller/order_items_controller.rb: 15: in ' ' app/controller/order_items_controller.rb: 1: in' ' –