2016-06-24 2 views
5

Ich baue ein Geschäft in Rails, das ein bestimmtes Verkaufsmodell hat. Ich muss einem Benutzer erlauben, nur 3 Artikel zu seiner Bestellung pro 30 Tage hinzuzufügen. Der 30-Tage-Zähler sollte mit dem ersten order_item beginnen. Nach Ablauf von 30 Tagen kann der Benutzer 3 Bestellungen hinzufügen. Wenn 30 Tage nicht bestanden haben, und als Beispiel, fügt der Benutzer zwei order_items hinzu, die er innerhalb von 30 Tagen noch einen weiteren order_item hinzufügen kann. Auch wenn der Benutzer versucht, mehr als 3 Elemente hinzuzufügen, um eine Fehlermeldung anzuzeigen und das Speichern der order_items in der Bestellung von current_user zu ignorieren.Wie kann current_user das Hinzufügen von mehr als 3 order_items zu einer Bestellung pro Zeitraum einschränken?

Ich habe Produkte, Bestellungen, order_items, Benutzer. Ich denke, dass ich etwas zum Benutzermodell hinzufügen sollte, aber ich bin nicht sicher was.

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> 

Antwort

2

ich ein begin_date und ein order_counter zu Benutzermodell hinzufügen würde. Jedes Mal, wenn Sie eine Bestellung hinzufügen, schauen Sie, ob die begin_date vor mehr als 30 Tagen ist, dann setzen Sie die begin_date auf das tatsächliche Datum. Wenn das begin_date weniger als 30 Tage zurückliegt, erhöhen Sie den Zähler. Und wenn der Zähler bereits 3 ist, verweigern Sie die Bestellung.

Sie können die Spalten an der Benutzertabelle durch das Argument Befehlszeile

rails generate migration AddOrderCounterToUser 

hinzufügen Das wird eine Klasse in db/Migrationen erstellen:

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

Fügen Sie die zusätzlichen Attribute in Ihrem UserController zu erlauben sie in user_params.

dann das Erstellen ändern Methode in Ihrer 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 

Sie auch die Prüfcodes in einem Verfahren, in dem Benutzermodell setzen, die sauberer sein würde.

+0

gespeichert Ich erhalte diesen Fehler in meinem Terminal, wenn ich versuche, ein Element zu einer Bestellung hinzufügen: Argument (Vergleich Datum mit Null fehlgeschlagen): app/controllers/order_items_controller.rb: 4: in '<' app/controllers/order_items_controller.rb: 4: in' create' –

+0

Ich habe meine Antwort aktualisiert und hinzugefügt, ob begin_date ist Nil (in diesem Fall müssen Sie begin_date setzen, also gehen Sie in den Fall sonst) – irene

+0

Ich habe keine Fehler, aber ich kann noch unbegrenzte order_items hinzufügen .. –

1

Wenn Sie unter bestimmten Umständen kein Element in Schienen erstellen möchten, sollten Sie im Allgemeinen die Situation mit Validatoren behandeln.

Sie könnten hier eine Verschachtelung Ansätze nehmen: Nest Ihr OrderItem Routen unter Order (Sie nisten weitere Informationen über die Rails Guides about Nested Routing finden)

Sie durch das Hinzufügen einer neuen Datenbankspalte first_item_added_at Sie Order Modell beginnen sollte

rails generate migration AddFirstItemAddedAtToOrder 

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

Wenn nisten, würden Sie einen neuen OrderItem über die Route erstellen

POST /orders/:id/order_items 

Dann haben Sie einen Validator auf Ihr OrderItem Modell

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 

Jetzt Ihren Controller muss nur hinzufügen, ein neues Element erstellen und

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 

speichern Wenn Sie nicht wollen, zu OrderItem Nest, als das Modell noch gleich bleibt, aber der Controller würde wie folgt aussehen:

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 

Bitte beachten Sie, dass ich current_order.id zu Ihrer order_item_params Methode hinzugefügt habe.

EDIT: ersetzt order_id: current_order.id durch order: current_order die Beziehung zu den neuen OrderItem zu schaffen, bevor es tatsächlich

+0

Ich erhalte einen Fehler: NoMethodError (undefinierte Methode 'first_item_added_at 'für nil: NilClass): app/models/order_item.rb: 46: in' only_3_items_in_30_days' app/controllers/order_items_controller.rb: 6: in 'create ' –

+0

Die Gründe dafür können von der gewählten Controller-Lösung abhängen. Hast du den verschachtelten Ansatz oder den anderen gewählt? In beiden Fällen lautet die erste Frage: Ist die Reihenfolge, in der Sie Elemente hinzufügen, bereits bestehen geblieben, wenn Sie den 'OrderItemsController' aufrufen? Die zweite Sache könnte sein, dass ich den Moment verwechselt habe, als die Beziehung zwischen zwei Objekten von ActiveRecord erzeugt wurde. Die Lösung hängt hier ein wenig von der Controller-Version ab, die Sie gewählt haben. Ich habe die Methode 'order_item_params' im zweiten Beispiel aktualisiert. – Dervol

+0

Ich verwende die zweite Methode, die Sie ohne Verschachtelung erwähnt haben, und ich habe Params geändert, aber jetzt ist dies der Fehler: ActionController :: RoutingError (undefinierte lokale Variable oder Methode 'current_order 'für OrderItemsController: Class): app/controllers/order_items_controller.rb: 15: in ' ' app/controllers/order_items_controller.rb: 1: in' ' –

Verwandte Themen