Reputation: 312
I have a model Friendship
where :friend_id
is also a foreign key as :user_id
friendships
id | user_id | friend_id
I'm aware that validating the uniqueness of the two fields would be something like this
validates :user_id, :uniqueness => {:scope => :friend_id}
But is there a way to validate
user_id = 1, friend_id = 3
user_id = 3, friend_id = 1
so that only
user_id = 1, friend_id = 3
is stored?
Upvotes: 2
Views: 1494
Reputation: 96604
One More!
def unique_relationship?
self.class.where("(user_id = ? and friend_id = ?) or
(user_id = ? and friend_id = ?)",
user_id, friend_id, friend_id, user_id).empty?
end
Upvotes: 1
Reputation: 96604
Reading the question more closely I now think that it's really
validates :friend_id, :uniqueness => {:scope => :user_id}
Upvotes: 2
Reputation: 96604
validate :relationship_uniqueness
def relationship_uniqueness
existing_record = Friendship.find(:first, :conditions => ["user_id = ? AND friend_id = ?", user_id, friend_id])
unless existing_record.blank?
errors.add(:user_id, "has already been saved for this relationship")
end
end
Upvotes: 1
Reputation: 16844
This is hard problem.
How do you model a symmetric relationship?
Last time I did this I decided that in fact it is not symmetric.
For an idea of how to model this look at Disapora's Contact
class
class Contact < ActiveRecord::Base
belongs_to :user
belongs_to :person
validates :person, :presence => true
validates_presence_of :user
validates_uniqueness_of :person_id, :scope => :user_id
end
You'll note they don't do anything special to ensure uniqueness.
What they do instead is carefully consider each point of the interaction that creates or breaks a friendship. We need to be careful to wrap them in transactions.
Basically, when a user sends a "friend request"
When the "friend" accepts this "request"
If the "friend" chooses to break the friendship
Each of these actions need to be done in a transaction.
As long as these transactions are one correctly we should never have a one-way friendship.
Upvotes: 1
Reputation: 96604
I would try:
validate :unique_relationship
def unique_relationship
(user_id.to_s + friend_id.to_s).uniq
end
Upvotes: 0
Reputation: 312
So I thought of a way to work around this, but it's very messy
friendships
id
1
friend_lists
id | friendship_id | user_id
1 | 1 | 1
2 | 1 | 3
This way, I will have to make sure one friendship_id
can only be entered twice into friend_lists
, and do
validates :friendship_id, :uniqueness => {:scope => :user_id}
Upvotes: 0