我在订购时遇到了麻烦。我发布了下面的错误。我认为问题与OrderController.rb中的create方法有关,我确实已经定义了total_price方法,但除此之外我不知道如何解决这个问题。任何帮助,将不胜感激。谢谢。
class OrderTransaction
def initialize order, nonce
@order = order
@nonce = nonce
end
def execute
@result = Braintree::Transaction.sale(
amount: order.total_price,
payment_method_nonce: nonce
)
end
def ok?
@result.success?
end
private
attr_reader :order, :nonce
end
class Order < ActiveRecord::Base
belongs_to :user
has_many :order_items
def total_price
order_items.inject(0) { |sum, item| sum + item.total_price }
end
end
class OrdersController < ApplicationController
before_filter :initialize_cart
def index
@orders = Order.order(created_at: :desc).all
end
def create
@order_form = OrderForm.new(
user: User.new(order_params[:user]),
cart: @cart
)
if @order_form.save
notify_user
if charge_user
redirect_to root_path, notice: "Thank you for placing the order."
else
flash[:warning] = <<EOF
Your order ID is #{@order_form.order.id}.
<br/>
Something went wrong.
EOF
redirect_to new_payment_order_path(@order_form.order)
end
else
render "carts/checkout"
end
end
def update
@order = Order.find params[:id]
@previous_state = @order.state
if @order.update state_order_params
notify_user_about_state
redirect_to orders_path, notice: "Order was updated."
end
end
def new_payment
@order = Order.find params[:id]
@client_token = Braintree::ClientToken.generate
end
def pay
@order = Order.find params[:id]
transaction = OrderTransaction.new @order, params[:payment_method_nonce]
transaction.execute
if transaction.ok?
redirect_to root_path, notice: "Thank you for placing the order."
else
render "orders/new_payment"
end
end
private
def notify_user
@order_form.user.send_reset_password_instructions
OrderMailer.order_confirmation(@order_form.order).deliver
end
def notify_user_about_state
OrderMailer.state_changed(@order, @previous_state).deliver
end
def order_params
params.require(:order_form).permit(
user: [ :name, :phone, :address, :city, :country, :postal_code, :email ]
)
end
def charge_user
transaction = OrderTransaction.new @order, params[:payment_method_nonce]
transaction.execute
transaction.ok?
end
def state_order_params
params.require(:order).permit(:state)
end
end
class OrderItem < ActiveRecord::Base
belongs_to :order
belongs_to :product
def total_price
self.quantity * self.product.price
end
end
class OrderForm
include ActiveModel::Model
attr_accessor :user, :order # credit_card
attr_writer :cart
def save
set_password_for_user
if valid?
persist
true
else
false
end
end
def has_errors?
user.errors.any?
end
private
def valid?
user.valid?
end
def persist
user.save
@order = Order.create! user: user
build_order_items
end
def set_password_for_user
user.password = Digest::SHA1.hexdigest(user.email + Time.now.to_s)[0..8]
end
def build_order_items
@cart.items.each do |item|
@order.order_items.create! product_id: item.product_id, quantity: item.quantity
end
end
end
class OrderItem < ActiveRecord::Base
belongs_to :order
belongs_to :product
def total_price
self.quantity * self.product.price
end
end
答案 0 :(得分:2)
问题出在charge_user
类OrdersController
类中,您调用此代码:
transaction = OrderTransaction.new @order, params[:payment_method_nonce]
你在这个方法中没有真正定义@order
,即@order
在这里是nil
,这会导致你在这里遇到问题并且你得到这个错误:{{1} }
在调用此行代码之前,在undefined method total_price for nil:NilClass
方法中设置@order
值,并确保charge_user
不 @order
:
nil
一种可能的解决方案是修改transaction = OrderTransaction.new @order, params[:payment_method_nonce]
方法以采用charge_user
这样的参数:
order
并且,在您的def charge_user(order)
transaction = OrderTransaction.new order, params[:payment_method_nonce]
transaction.execute
transaction.ok?
end
方法调用中,这样:
create
这将解决您的问题。
答案 1 :(得分:2)
作为标准注释,任何NilClass
错误基本上意味着您尚未定义您尝试操作的变量。
解决问题的关键是找出为什么没有定义变量,并填充它。
def execute
@result = Braintree::Transaction.sale(
amount: order.total_price,
payment_method_nonce: nonce
)
end
这是Rails表示该变量未填充的地方。
但是,与编程中的许多问题一样,问题的原因可能没有定义......
我最初认为问题在于你没有打电话给@order
。但是,该类使用order
初始化,因此不应该成为问题。所以你必须看看你如何调用这个类:
transaction = OrderTransaction.new @order, params[:payment_method_nonce]
这推测@order
已定义。
我猜测它不是。
这就是我要做的事情:
def create
@order_form = OrderForm.new(
user: User.new(order_params[:user]),
cart: @cart
)
if @order_form.save
notify_user
@order = @order_form.order #-> not efficient but should create @order
if charge_user
redirect_to root_path, notice: "Thank you for placing the order."
else
flash[:warning] = <<EOF
Your order ID is #{@order_form.order.id}.
<br/>
Something went wrong.
EOF
redirect_to new_payment_order_path(@order_form.order)
end
else
render "carts/checkout"
end
end
就我个人而言,我认为这突出了代码结构的一个更深层次的问题:
- 您正在创建
OrderForm
对象,但正在处理@order_form.order
- 你的控制器充满了很小的方法,这些方法让人很沮丧
- 您的控制器用于订单,但构建
OrderForm
个对象
我尽力做出controller as thin as possible:
#app/controllers/orders_controller.rb
class OrdersController < ApplicationController
def new
@order = current_user.order.new
end
def create
@order = current_user.order.new order_params
if @order.save
@order.charge
end
end
private
def order_params
params.require(:order).permit(:x, :y, :z, order_products_attributes: [:product, :qty])
end
end
我有更模块化的模型结构:
#app/models/order.rb
class Order < ActiveRecord::Base
belongs_to :user
has_many :order_products
has_many :products, through: :order_products, extend ProductQty
has_many :payments, inverse_of: :order
scope :cart, -> { order_products }
def total_price
products.pluck(:price, :qty) #-> need to work out
end
def charge
payment = payments.create
payment.execute ? payment.success : payment.error #-> something conditional
end
end
#app/models/order_product.rb
class OrderProduct < ActiveRecord::Base
#columns id | order_id | product_id | qty | created_at | updated_at
belongs_to :order
belongs_to :product
end
#app/models/payment.rb
class Payment < ActiveRecord::Base
belongs_to :order, inverse_of: :payments
def execute
Braintree::Transaction.sale(amount: order.total_price)
end
end
#app/models/product.rb
class Product < ActiveRecord::Base
has_many :order_products
has_many :orders, through: :order_products
end
#app/models/concerns/product_qty.rb
module ProductQty
#Load
def load
products.each do |qty|
proxy_association.target << qty
end
end
#Private
private
#Products
def products
return_array = []
through_collection.each_with_index do |through,i|
associate = through.send(reflection_name)
associate.assign_attributes({qty: items[i]})
return_array.concat Array.new(1).fill( associate )
end
return_array
end
#######################
# Variables #
#######################
#Association
def reflection_name
proxy_association.source_reflection.name
end
#Foreign Key
def through_source_key
proxy_association.reflection.source_reflection.foreign_key
end
#Primary Key
def through_primary_key
proxy_association.reflection.through_reflection.active_record_primary_key
end
#Through Name
def through_name
proxy_association.reflection.through_reflection.name
end
#Through
def through_collection
proxy_association.owner.send through_name
end
#Captions
def items
through_collection.map(&:qty)
end
#Target
def target_collection
proxy_association.target
end
end
我想在某处包含 cart ,我将不得不再次这样做。
目前,您可以执行以下操作:
@order = current_user.orders.find params[:id]
@order.products.each do |product|
product.qty #-> 5
@order.total_price #-> prices * qtys
-
这不完整或经过测试,但我希望它能告诉您如何通过模块化来大幅提升代码结构。 IE保留尽可能多的方法绑定到您的对象。
简而言之,您应该能够执行以下操作:
@order = current_users.orders.find params[:id]
if @order.payments.any?
@payment = @order.payment.first
@payment.success?
end