class Auction
belongs_to :item
belongs_to :user, :foreign_key => :current_winner_id
has_many :auction_bids
end
class User
has_many :auction_bids
end
class AuctionBid
belongs_to :auction
belongs_to :user
end
Аукцион отображен на странице, пользователь вводит сумму и нажимает предложение. Код контроллера мог бы выглядеть примерно так:
class MyController
def bid
@ab = AuctionBid.new(params[:auction_bid])
@ab.user = current_user
if @ab.save
render :json => {:response => 'YAY!'}
else
render :json => {:response => 'FAIL!'}
end
end
end
Это работает отлично до сих пор! Однако я должен удостовериться, чтобы пара других вещей произошла.
@ab.auction.bid_count
потребности, которые будут увеличены одной.@ab.user.bid_count
потребности, которые будут увеличены одной@ab.auction.current_winner_id
потребности, которые будут установлены на @ab.user_id
Таким образом, User
и Auction
связанный с AuctionBid
нужны значения, обновленные также для AuctionBid#save
возвращать true.
И Base # save , и Base # destroy заключены в транзакция , которая гарантирует, что все, что вы делаете при проверках или обратных вызовах, будет происходить под защищенным покрытием транзакции. Таким образом, вы можете использовать проверки для проверки значений, от которых зависит транзакция, или вы можете создавать исключения в обратных вызовах для отката, включая обратные вызовы after_ *.
class AuctionBid < ActiveRecord::Base
belongs_to :auction, :counter_cache => true
belongs_to :user
validate :auction_bidable?
validate :user_can_bid?
validates_presence_of :auction_id
validates_presence_of :user_id
# the real magic!
after_save :update_auction, :update_user
def auction_bidable?
errors.add_to_base("You cannot bid on this auction!") unless auction.bidable?
end
def user_can_bid?
errors.add_to_base("You cannot bid on this auction!") unless user.can_bid?
end
protected
def update_auction
auction.place_bid(user)
auction.save!
end
def update_user
user.place_bid
user.save!
end
end
Франсуа Босолей +1. Благодарим за рекомендацию : foreign_key
, но столбцы current_winner _ *
необходимо кэшировать в базе данных для оптимизации запроса.
Алекс +1. Спасибо, что помогли мне начать работу с Model.transaction {...}
. Хотя это не стало для меня полным решением, это определенно помогло мне в правильном направлении.
Вероятно, вы могли бы переопределить AuctionBid.save, примерно так:
def save
AuctionBid.transaction {
auction.bid_count += 1
user.bid_count += 1
auction.current_winner_id = user_id
auction.save!
user.save!
return super
}
end
Возможно, вам также потребуется перехватить исключения, возникающие в блоке транзакции, и вернуть false . Я думаю, вам также нужно добавить own_to: аукцион
к AuctionBid
, чтобы иметь возможность ссылаться на объект аукциона.
Вы хотите включить кэширование счетчика , добавив: counter_cache в ассоциации own_to.
class Auction
belongs_to :item
belongs_to :user, :foreign_key => :current_winner_id
has_many :auction_bids
end
class User
has_many :auction_bids
end
class AuctionBid
belongs_to :auction, :counter_cache => true
belongs_to :user, :counter_cache => true
end
Не забудьте добавить столбцы при миграции. Чтобы создать аукционную ставку и установить пользователя, я бы предложил использовать следующий код:
class MyController
def bid
@ab = current_user.auction_bids.build(params[:auction_bid])
if @ab.save
render :json => {:response => 'YAY!'}
else
render :json => {:response => 'FAIL!'}
end
end
end
Сохраняет шаг и гарантирует, что вы никогда не забудете назначить пользователя.
Последнее требование - найти действующего победителя. На самом деле это ассоциация has_one на аукционе. Вам не нужен столбец:
class Auction
# has_one is essentially has_many with an enforced :limit => 1 added
has_one :winning_bid, :class_name => "AuctionBid", :order => "bid_amount DESC"
end