5

我有三个模型:User、Product、Offer以及这些模型之间的关系有问题。

设想:

用户 1 发布产品

用户 2 可以向用户 1 发送报价,例如 $10

用户 1 可以接受或拒绝报价

我现在的问题是:

用户、产品和优惠之间的正确关系是什么?

我该如何处理那些“接受或拒绝”的动作?

有没有更好的解决方案?

用户型号:

class User < ActiveRecord::Base
    attr_accessible :name, :email, :password, :password_confirmation, :remember_me, :avatar, :screen_name
    has_many :products
    has_many :offers,:through => :products
end

产品型号:

class Product < ActiveRecord::Base
    attr_accessible :content, :price, :title, :tag_list, :productimage, :user_id
    belongs_to :user
    has_many :offers, :through => :users
end

提供型号:

class Offer < ActiveRecord::Base
    attr_accessible :offer_price, :status, :user_id, :product_id
    has_many :products
    has_many :users, through: :products
end

提前致谢 :)

编辑:

我正在使用 Rails 3.2.8

4

3 回答 3

7

警告:这是一本小小说。

第 1 部分:建立协会

我建议彻底阅读有关关联的 Rails 指南,将其添加为书签,然后再阅读一遍,因为这是正确理解的关键,并且可能有点棘手 - 一旦超出了基本关联,就有很多选择。

关于您的应用需要注意的一件事是,您的用户有两个角色,买家和卖家。您将需要小心关联的名称 - 是@user.offers返回用户提供的报价,还是用户收到的报价?您可能希望能够将这两件事的列表放入用户的个人资料中。

您描述的基本关系相当简单:

  • 一个用户可以销售许多产品,所以User has_many :productsProduct belongs_to :user

  • 一个用户可以提供许多报价,因此User has_many :offersOffer belongs_to :user

  • Product has_many :offers一个产品可能会收到很多报价Offer belongs_to :product

这一切都很好,你当然可以这样做——在这种情况下,你可以跳到第 2 部分 :)

但是,一旦您开始尝试添加through关系,水就会变得浑浊。毕竟,

  • Offer belongs_to :user(买方),但它也有一个用户通过产品(卖方)

  • User has_many :products(他们正在销售),但他们也通过优惠(他们正在购买 - 好吧,试图购买)提供许多产品。

啊,混乱!

这就是您需要:class_name选项的时候,它可以让您以不同的方式命名关联到它所引用的类,以及该:source选项,它可以让您在“from”模型上以与“through”模型不同的方式命名关联。

因此,您可能会像这样形成您的关联:

# User
has_many :products_selling, class_name: 'Product'
has_many :offers_received, class_name: 'Offer',
         through: :products_selling, source: :offers

has_many :offers_made, class_name: 'Offer'
has_many :products_buying, class_name: 'Product',
         through: :offers_made, source: :product


# Product
belongs_to :seller, class_name: 'User', foreign_key: :user_id
has_many :offers
has_many :buyers, class_name: 'User', through: :offers

# Offer
belongs_to :product
belongs_to :buyer, class_name: 'User', foreign_key: :user_id
has_one :seller, class_name: 'User', through: :product

尽管如果您将user_id列重命名为seller_idin the productstable 和buyer_idin the offerstable,则不需要这些:foreign_key选项。

第 2 部分:接受/拒绝报价

有很多方法可以解决这个问题。我会放一个布尔字段acceptedOffer然后你可以有类似的东西

# Offer
def accept
  self.accepted = true
  save
end

def reject
  self.accepted = false
  save
end

并且您可以找到未完成的优惠(其中accepted为空)

scope :outstanding, where(accepted: nil)

要让控制器中发生接受/拒绝逻辑,您可以考虑添加新的 RESTful 操作(链接指南是另一个值得彻底阅读的指南!)。你应该找到像这样的一行

resources :offers

在 config/routes.rb 中,它提供了标准操作indexshowedit等。您可以将其更改为

resources :offers do
  member do
    post :accept
    post :reject
  end
end

并把这样的东西放在你的OffersController

def accept
  offer = current_user.offers_received.find(params[:id])
  offer.accept
end

# similarly for reject

然后你可以发出一个 POST 请求offers/3/accept,它会导致 id 为 3 的提议被接受。视图中这样的事情应该这样做:

link_to "Accept this offer", accept_offer_path(@offer), method: :post 

请注意,我不只是写信Offer.find(params[:id]),因为狡猾的用户可以代表卖家接受报价。请参阅Rails 最佳实践

于 2012-10-15T12:15:13.007 回答
4

除了关系之外,您的模型已经足够好了。当您试图区分拥有的产品与感兴趣的产品(提供)以及产品所有者与感兴趣的用户(提供报价的用户)时,混淆就开始了。如果你能想出一个更好的命名约定,你可以很容易地修复它。

1. 更好的关系

class User < ActiveRecord::Base
  attr_accessible :name, :email, :password, :password_confirmation, :remember_me, :avatar, :screen_name
  has_many :owned_products, :class_name => "Product"
  has_many :offers 
  has_many :interested_products, :through => :offers
end

class Offer < ActiveRecord::Base
  attr_accessible :offer_price, :status, :user_id, :product_id
  belongs_to :interested_user, :class_name => "User", :foreign_key => :user_id
  belongs_to :interested_product, :class_name => "Product", :foreign_key => :product_id
end

class Product < ActiveRecord::Base
  attr_accessible :content, :price, :title, :tag_list, :productimage, :user_id
  belongs_to :owner, :foreign_key => :user_id, :class_name => "User"
  has_many :offers 
  has_many :interested_users, :through => :offers
end

通过这些关系,我认为您可以获得您感兴趣的所有基本信息。例如,

@product = Product.find(1)
@product.owner # would give you the user who created the product
@product.interested_users # would give you users who placed an offer for this product

@user = User.find(1)
@user.owned_products # would give you the products created by this user
@user.interested_products # would give you the products where the user placed an offer

2. 处理接受和拒绝动作。

根据您的描述,我看到报价可能有 2 种可能的状态更改,“已创建”->“接受”或“已创建”->“拒绝”。我建议你看看state_machine。状态机将使用它的辅助方法为您的模型添加很好的风味,我认为这对您的情况非常有用。所以你的Offer模型看起来像这样,

class Offer < ActiveRecord::Base
  # attr_accessible :title, :body
  attr_accessible :offer_price, :status, :user_id, :product_id
  belongs_to :interested_user, :class_name => "User", :foreign_key => :user_id
  belongs_to :interested_product, :class_name => "Product", :foreign_key => :product_id

  state_machine :status, :initial => :created do
    event :accept do
      transition :created => :accepted
    end
    event :reject do
      transition :created => :reject
    end
  end
end

#cool helper methods
@offer = Offer.new
@offer.accepted? #returns false
@offer.reject #rejects the offer
@offer.rejected? #returns true

我希望这能给你一个更好的画面。

于 2012-10-15T12:13:48.383 回答
2

怎么样

class User < ActiveRecord::Base
  has_many :products  # All products posted by this user
  has_many :offers    # All offers created by this user
end

class Product < ActiveRecord::Base
  belongs_to :user   # This is the user who posts the product (User 1)
  has_many :offers
end

class Offer < ActiveRecord::Base
  belongs_to :product
  belongs_to :user   # This is the user who creates the offer (User 2)

  # Use a 'state' field with values 'nil', 'accepted', 'rejected' 
end 

对于您的场景:

# User 1 posts a product
product = user1.products.create

# User 2 can send User 1 an offer with an price e.g $ 10
offer = user2.offers.create(:product => product)

# User 1 can accept or reject the offer
offer.state = 'rejected'

您可以根据您的需要来改进这一点——例如,如果不同的用户可以发布相同的产品。

于 2012-10-15T11:40:04.890 回答