How to Implement a Friendship Model in Rails 3 for a Social Networking Application?
To access all pending friendships you can use an association:
has_many :pending_friends,
:through => :friendships,
:source => :friend,
:conditions => "confirmed = 0" # assuming 0 means 'pending'
To make the friendship bidirectional, you may want to replace your boolean confirmed column with a string status column that has one of the following three values: 'pending', 'requested' and 'accepted' (optionally 'rejected'). This will help keep track of who made the friendship request.
When a friendship request is sent (say from Foo to Bar), you create two friendship records (encapsulated in a transaction): one requested and one pending to reflect resp. that Bar has a requested friendship from Foo and Foo has a pending friendship with Bar.
def self.request(user, friend)
unless user == friend or Friendship.exists?(user, friend)
transaction do
create(:user => user, :friend => friend, :status => 'pending')
create(:user => friend, :friend => user, :status => 'requested')
end
end
end
When the friendship is accepted (e.g. by Bar), both friendship records are set to accepted.
def self.accept(user, friend)
transaction do
accepted_at = Time.now
accept_one_side(user, friend, accepted_at)
accept_one_side(friend, user, accepted_at)
end
end
def self.accept_one_side(user, friend, accepted_at)
request = find_by_user_id_and_friend_id(user, friend)
request.status = 'accepted'
request.accepted_at = accepted_at
request.save!
end
This is largely covered in chapter 14 of the Railspace book by Michael Hartl and Aurelius Prochazka. Here's the source code which should help you refine your solution.
the best way to implement a friendship model in rails
I would prefer the version that needs two connections between the friends, one for each direction. The reason is the same you mentioned: It allows more Rails-like queries on a user's friends.
Furthermore I think it would be clearer to have different tables for friendship request (one direction) and existing friendships (two directions)
Since you have a friendship model in the middle, I suggest to use the magic of callbacks. If you define some callbacks, it must be possible that you only have to take cake for one side of the connection, the callback should should be able to create (or delete) the matching complement.
# in friendship_requests
after_save :created_friendship
def accept
update_attributes(:status => 'accepted')
end
private
def created_friendship
sender.friends << receiver if status_changed? && status == 'accepted'
end
# in user.rb
has_and_belongs_to_many :friends, after_add: :create_complement_friendship,
after_remove: :remove_complement_friendship
private
def create_complement_friendship(friend)
friend.friends << self unless friend.friends.include?(self)
end
def remove_complement_friendship(friend)
friend.friends.delete(self)
end
This is just a first idea, for sure some validators and callbacks are missing...
Best database architecture for friendships in a social networking application
I would recommend the second model, separate tables for friend_request
and friendship
. While these tables may currently hold the exact same data, over time I would expect that they will diverge.
Here are some possible ways this might happen:-
- you want to allow them to send a message along with the friend request ("Hey, remember me from school")
- you decide you want to keep statistics about the friend request, such as when the request was made
- you decide you want to keep declined friend requests in the database (to disallow further requests once declined)
Because they are different things, you should create different tables rather than overloading a single table. It's not so difficult to create a table.
Should you put two rows in the table, representing both directions of the relationship? This is a question of normalisation vs performance.
The normalised answer is to only include a single row. This model:
- is easier to get update/insert/delete as there is only one row to worry about
- more difficult and slower to query as you will need to query from both directions
The de-normalised answer (because duplicated data is stored) is to include both rows: This model:
- is harder to update/insert/delete as there are two rows to keep synchronized
- faster to query as you can query from just one direction
Ruby on rails 3 friendship
You need to separate the @customer and @friend. In your link, you are setting :friend_id to the customer, and you are never setting the @customer id.
Try this:
def create
@customer = current_account
@friend = Account.find(params[:friend_id])
Friendship.request(@customer, @friend)
redirect
end
In the link_to you need:
<%= link_to "Add Friend", friendships_path(:friend_id => friend),: method => :post %>
how to create a friendship model on Rails 5
you can use the 'has_friendship' gem for this.
This gem is really simple to use
Create the migration
rails generate has_friendship
Add to the model
class User < ActiveRecord::Base
has_friendship
endand you can start to manage your friends
Managing friendship
# Create Friends
@joe = User.create(name: "Joe")
@doe = User.create(name: "Doe")
# @joe sends a friend request to @doe
@joe.friend_request(@doe)
# @doe can accept the friend request
@doe.accept_request(@joe)
# @doe can also decline the friend request
@doe.decline_request(@joe)
# @doe removes @joe from its friends
@doe.remove_friend(@joe)
again you can find more info on the gem Documention site https://github.com/sungwoncho/has_friendship
I hope that this helps :)
user has many :users, or must I use another way for a friend based social network?
I would suggest that a user has many relationships, this would leave you free to add new types of relationships in the future.
So you start with a "one user to another user" relationship table (minimally, just two IDs) and then in the future you could add a "one user to a group" relationship table (once you've created groups of course!)
Related Topics
Rake "Already Initialized Constant Wfkv_" Warning
"Gem Install Rails" Fails With Dns Error
Ruby Run Shell Command in a Specific Directory
Convert Unicode Codepoint to String Character in Ruby
Validation Failed: Upload File Has an Extension That Does Not Match Its Contents
Parallel Assignment Operator in Ruby
What Is a Regex to Match a String Not At the End of a Line
Rspec: Expect VS Expect With Block - What's the Difference
What Are the Magic $-Prefixed Variables in Ruby
Rails Engines Extending Functionality
How to Remove the Bom from a Utf-8 Encoded File
Overriding a Module Method from a Gem in Rails
Why Are My Basic Heroku Apps Taking Two Seconds to Load
Ruby on Rails - Access Controller Variable from Model
Why Are All Rails Helpers Available to All Views, All the Time? How to Disable This