I have three models: User, Product, Offer and a problem with the relationship between these models.
Scenario:
User 1 posts a product
User 2 can send User 1 an offer with an price e.g $ 10
User 1 can accept or reject the offer
My questions are now:
What is the right relationship between User, Product and Offer?
How can I handle those "accept or reject" actions?
Is there maybe a better solution?
User model:
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
Product model:
class Product < ActiveRecord::Base
attr_accessible :content, :price, :title, :tag_list, :productimage, :user_id
belongs_to :user
has_many :offers, :through => :users
end
Offer model:
class Offer < ActiveRecord::Base
attr_accessible :offer_price, :status, :user_id, :product_id
has_many :products
has_many :users, through: :products
end
Thanks in advance :)
EDIT:
I am using Rails 3.2.8
How about
For your scenario:
You could refine this depending on your needs - e.g. if the same product could be posted by different users.
Your models are good enough, except for the relations. The confusion starts when you are trying to differentiate the owned products vs interested products(offered) and product owner vs interested users(users who placed the offer). If you can come up with a better naming convention, you can easily fix it.
1. Better relations
With these relations I think you can get all the basic information you would be interested. For Example,
2. Handling accept and reject actions.
From your description, I see there can be 2 possible state changes to an offer, "created" -> "accept" or "created" -> "reject". I suggest you to look at state_machine. State machine will add nice flavor to your model with its helper methods, which I think will be very useful in your case. So your
Offer
model will look something like this,I hope this gives you a better picture.
Warning: here comes a small novel.
Part 1: setting up the associations
I'd recommend reading the Rails guide on associations thoroughly, bookmark it, and read it again, because this is a key thing to understand properly, and can be a bit tricky - there are lots of options once you go beyond basic associations.
One thing to notice about your app is that your users have two roles, buyers and sellers. You're going to need to be careful with the names of your associations - Does
@user.offers
return the offers the user has made, or the offers the user has received? You might want to be able to put lists of both these things in the user's profile.The basic relationships you're describing are fairly simple:
A user can sell many products, so
User has_many :products
andProduct belongs_to :user
A user can make many offers, so
User has_many :offers
andOffer belongs_to :user
A product may receive many offers so
Product has_many :offers
andOffer belongs_to :product
That's all well and good, and you could certainly get by just doing this - in which case you can skip down to Part 2 :)
However, as soon as you start trying to add the
through
relationships the waters are going to get muddy. After all,Offer belongs_to :user
(the buyer), but it also has a user through product (the seller)User has_many :products
(that they are selling), but they also have many products through offers (that they are buying - well, trying to buy).Aargh, confusing!
This is the point when you need the
:class_name
option, which lets you name an association differently to the class it refers to, and the:source
option, which lets you name associations on the 'from' model differently to the 'through' model.So you might then form your associations like this:
Although if you renamed your
user_id
columns toseller_id
in theproducts
table, andbuyer_id
in theoffers
table, you wouldn't need those:foreign_key
options.Part 2: accepting/rejecting offers
There's a number of ways to tackle this. I would put a boolean field
accepted
onOffer
and then you could have something likeand you could find the outstanding offers (where
accepted
is null)To get the accept/reject logic happening in the controller, you might consider adding new RESTful actions (the linked guide is another one worth reading thoroughly!). You should find a line like
in config/routes.rb, which provides the standard actions
index
,show
,edit
, etc. You can change it toand put something like this in your
OffersController
Then you can issue a POST request to
offers/3/accept
and it will cause the offer with id 3 to be accepted. Something like this in a view should do it:Note that I didn't just write
Offer.find(params[:id])
because then a crafty user could accept offers on the behalf of the seller. See Rails Best Practices.