1.0 4 張表
表1: products : name | price | active
表2: orders : subtotal | tax | shipping | total | order_status(references)
表3: order_items : product_id(references) | order_id(references) | unit_price | total_price | quantity
表4: order_status : name
2.0 表的關系
product <----belongs_to/has_many-----> order_item <----has_many/belongs_to-----> order
多對多關系一般不會用 has_and_belongs_to_many
, 而是使用has_many :through 第三張表關聯(lián)
order <-----belongs_to/has_many-----> order_status
models
1.0
rails g model Product name 'price:decimal{12,3}' active:boolean
rails g model OrderStatus name:string
rails g model Order 'subtotal:decimal{12,3}' 'tax:decimal{12,3}' 'shipping:decimal{12,3}' 'total:decimal{12,3}' order_status:references
rails g model OrderItem product:references order:references 'unit_price:decimal{12,3}' quantity:integer 'total_price:decimal{12,3}'
rake db:migrate
2.0
app/models/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
app/models/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
app/models/order_status.rb:
class OrderStatus < ActiveRecord::Base
has_many :orders
end
app/models/product.rb:
class Product < ActiveRecord::Base
has_many :order_items
default_scope { where(active: true) }
end
controllers
rails g controller Products index
rails g controller Carts show
rails g controller OrderItems create update destroy
app/controllers/application_controller.rb:
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
helper_method :current_order
def current_order
if !session[:order_id].nil?
Order.find(session[:order_id])
else
Order.new
end
end
end
app/controllers/carts_controller.rb:
class CartsController < ApplicationController
def show
@order_items = current_order.order_items
end
end
app/controllers/order_items_controller.rb:
class OrderItemsController < ApplicationController
def create
@order = current_order
@order_item = @order.order_items.new(order_item_params)
@order.save
session[:order_id] = @order.id
end
def update
@order = current_order
@order_item = @order.order_items.find(params[:id])
@order_item.update_attributes(order_item_params)
@order_items = @order.order_items
end
def destroy
@order = current_order
@order_item = @order.order_items.find(params[:id])
@order_item.destroy
@order_items = @order.order_items
end
private
def order_item_params
params.require(:order_item).permit(:quantity, :product_id)
end
end
app/controllers/products_controller.rb:
class ProductsController < ApplicationController
def index
@products = Product.all
@order_item = current_order.order_items.new
end
end
routes.rb:
Rails.application.routes.draw do
resources :products, only: [:index]
resource :cart, only: [:show]
resources :order_items, only: [:create, :update, :destroy]
root to: "products#index"
end