How to Set up Relationships in Rails with Friends or Followers
Creating social relationships between users is a core functionality of any social networking site or web application. A good relationship system should allow users to make connections with each other, send friend requests, follow other users, and view their friends and followers. In this article, we’ll explore how to set up relationships in Rails with friends or followers.
Introduction
Defining relationships between data entities is one of the fundamentals of web development. In the context of social networking sites, a relationship is a connection between two users that can be categorized into different types such as friendship and following.
Building a reliable relationship management system can improve user experience and engagement on your platform. Ruby on Rails is an excellent framework for creating social features due to its strong database modeling capabilities and easy-to-use migration system.
In this article, we’ll explore how to set up relationships in Rails with friends or followers using models, migrations and associated methods for building out our social features.
Setting Up Relationships
Before diving into setting up individual relationships like friendships or followers in Rails, it’s essential to understand the core concepts of relationship modeling.
There are different types of relationships which are:
- One-to-one
- One-to-many
- Many-to-many
In the context of a social media app, we’ll typically use many-to-many relationships since most users will have more than one friend or follower.
To accomplish this in Rails, we need to define a model that represents our relationship table. A simple way to achieve this is by creating a new file called ‘relationship.rb’ in the ‘models’ folder in your application.
The Relationship Model
We’ll create our relationship model as follows:
rails generate model Relationship user_id:integer friend_id:integer status:string
The ‘status’ column here has a default value of ‘pending’, which indicates that a relationship invitation has not been accepted. You can also add timestamps by passing in --timestamps
to the generator.
Migrations for Relationships
Now that our model is set up, the next step is to create a migration file. We’ll use the following command to create it:
rails generate migration CreateRelationships
In the generated file, we’ll specify the schema for our relationship model as follows:
“`
class CreateRelationships < ActiveRecord::Migration[6.1]
def change
create_table :relationships do |t|
t.integer :user_id
t.integer :friend_id
t.string :status, default: 'pending'
t.timestamps
end
add_index :relationships, [:user_id, :friend_id], unique: true
end
end
```
The command above creates a new table in our database called ‘relationships’. After this, we add index for user_id and friend_id fields to ensure uniqueness such that no two records will have the same combination of user_id and friend_id.
Next, run rails db:migrate
from the terminal to record these changes in your database.
Adding Associations to Models
After defining our relationships and migrations, we can now associate them with our models.
We need to specify relationships between users and their friends/followers. This can be achieved through associations in Rails.
There are three association types in Rails — belongs_to, has_one/many and has_and_belongs_to_many (HABTM).
- belongs_to: is used when a model record belongs to a single parent record, e.g. for messages or comments.
- has_one/many: is used when a model record has one or many children records, e.g. in case of posts and comments relationship.
- has_and_belongs_to_many: is used when a model record can have many children records through multiple parents records, e.g. for friends or followers relationship between users.
We’ll first specify the associations for our Relationship models as shown below:
“`
class Relationship < ApplicationRecord
belongs_to :user
belongs_to :friend, class_name: "User"
end
```
Here we’ve specified that each 'relationship' will belong to a 'user', and that the 'friend' field should be associated with 'User' model with an alias of ‘friend’.
To complete the association, we’ll add definitions in our User model as follows:
```
class User < ApplicationRecord
has_many :relationships
has_many :friends, through: :relationships
end
```
In this example, we’re using has_many associations for relationship and friends. Note that the ‘through’ option specifies the path to find friends.
Note: It’s important to define all associations in both models involved in the relationship.
Testing Relationships
After defining our migrations and models along with their respective associations, it’s essential to also test our relationships to ensure everything is set up correctly.
Rails comes with a built-in framework called ‘RSpec’ which enables creating tests for your application.
Rspec-Rails uses Ruby’s ‘expectation syntax’ which makes it easy to read and write tests.
Below is a simple example of testing that a user has many friends:
“`
describe ‘User’ do
it ‘has an association’ do
u = User.reflect_on_association(:friends)
expect(u.macro).to eq(:has_many)
end
end
“`
You can run your tests by typing rspec
in the terminal.
Setting up Friends Relationship
Setting up friendships involves connecting individuals, who must both approve the friendship requests.
To set this up, we’ll create an additional table that will store friendships related data with friend_id and user_id fields as well as a column called ‘status’ specific for friendship purposes.
This field will allow us to represent different states in the life cycle of a friendship relationship, such as ‘pending’, ‘accepted’ and ‘rejected’.
Adding these extra columns will enable us to represent friendships correctly in our models and thus be able to accept or decline friend requests.
Let’s take a look at how the Friendship model might look:
The Friendship Model
We’ll be creating a new file called ‘friendship.rb’ in models folder using this command:
rails generate model Friendship user_id:integer friend_id:integer status:string
The next step is to create a migration for our Friendship model. We’ll use this command to generate the migration:
rails generate migration CreateFriendships
In our migration file, we’ll add the following code to describe the schema for our friendship model:
“`
class CreateFriendships < ActiveRecord::Migration[6.1]
def change
create_table :friendships do |t|
t.references :user, null: false, foreign_key: true
t.references :friend, null: false, foreign_key: {to_table: :users}
t.string :status
t.timestamps
end
end
end
```
In this model, we’ve specified that each Friendship will belong to a user and a friend of a user. The status column will hold the status for each friendship instance.
After creating the Friendship model and migration, it’s time to add associations to our models.
Add Associations to Models
In our User model, we’ll add associations as shown below:
“`
class User < ApplicationRecord
has_many :friendships, dependent: :destroy
has_many :inverse_friendships, class_name: 'Friendship', foreign_key: 'friend_id', dependent: :destroy
has_many :friends, through: :friendships, source: 'friend'
has_many :inverse_friends, through: :inverse_friendships, source: 'user'
def self.friends?(user_one_id, user_two_id)
Friendship.where(user_id: [user_one_id,user_two_id], friend_id:[user_one_id,user_two_id], status: "accepted").count == 2
end
end
```
We’re using has_many and belongs_to associations to define the relationships between users and friendships.
To find a user’s accepted friends or inverse-friends, we’re using the ‘through’ option to specify associations using friendships.
The last method friends? is defined to check if two given user_ids are already friends by checking number of records with “status” as “accepted” between both.
We also need to define associations in our Friendship model as shown below:
```
class Friendship < ApplicationRecord
belongs_to :user
belongs_to :friend, class_name: 'User'
end
```
The user and friend fields are two-way foreign keys references in our Friendship model. This means that when we create a new friendship record, the record will automatically be associated with both the user and the friend.
Defining Methods for Requesting and Accepting Friendships
Next, we’ll define methods that will help us to request and accept friendships.
“`
class Friendship < ApplicationRecord
def self.request(user, friend)
unless user == friend || Friendship.exists?(user: user, friend: friend)
transaction do
create!(user: user, friend: friend, status: 'pending')
create!(user: friend, friend: user, status: 'requested')
end
end
end
def self.accept(user,friend)
request = where(user: user, friend: friend,status:'pending').first
reverse_request = where(user:friend,friend:user,status:"requested").first
if request && reverse_request
transaction do
friends!(request)
end
else
false
end
end
private
def self.friends!(request)
request.update(status:'accepted')
reverse_request.update(status:'accepted')
Friendship.create!(user:request.user,friend:request.friend,status:'accepted')
Friendship.create!(friend:request.user,user:request.friend,status:'accepted')
end
end
```
The request method will initiate a pending friendship from the primary user’s account to a second account. The accept method which accepts the friendships needs to confirm that two records exist before making any changes their ‘status’ flag.
If two records are found in “pending” and “requested”, which implies that there is a connection pending between two users then it goes ahead and update both records to show the status as accepted.
When we have added these two methods to our application codebase we can call them using these lines of code below:
```
Friendship.request(user_1,user_2)
Friendship.accept(user_1,user_2)
```
Implementing User Interface for Sending and Accepting Friend Requests
In Rails, we can use the existing CRUD functionality to implement our user interface for manually sending and accepting friend requests.
The first step is to create a form that allows users to send a friend request. This can be done by generating a new Rails controller using the scaffold command:
“`
rails g scaffold FriendRequest sender:integer receiver:integer status:string
“`
The next step is to generate appropriate actions in our friend request controller.
When writing code for the templates, it’s essential to have some validation on both sender and receiver’s ID fields with dropdowns if possible.
We make these changes to our migration for friend requests:
“`
class CreateFriendRequests < ActiveRecord::Migration[6.1]
def change
create_table :friend_requests do |t|
t.references :sender, null: false, foreign_key: {to_table: :users}
t.references :receiver, null: false, foreign_key: {to_table: :users}
t.string :status,default: 'pending'
t.timestamps
end
end
end
```
Finally, we update our models User and FriendRequest by adding the associations as shown below:
```
class User < ApplicationRecord
has_many :sent_friend_requests, class_name: 'FriendRequest', foreign_key: 'sender_id'
has_many :received_friend_requests, class_name: 'FriendRequest', foreign_key: 'receiver_id'
end
class FriendRequest < ApplicationRecord
belongs_to :sender, class_name: 'User'
belongs_to :receiver, class_name: 'User'
end
```
Setting up Followers Relationship
A one-way following relationship refers to someone who follows another person without requiring explicit approval from that person.
To create a following relationship, we’ll add an additional table to store the follow data. This follows the same pattern as we did with friendship but this time, it will have only one column referring to user_id and another referring to a follower’s id.
The Follow Model
We’ll create a new file called ‘follow.rb’ in models folder using the command below:
rails generate model Follow user_id:integer follower_id:integer
After generating the Follow model file, we’ll create the migration file using the command below:
rails generate migration CreateFollows
In the migration file, we’ll describe the schema for Follow class as shown below:
“`
class CreateFollows < ActiveRecord::Migration[6.1]
def change
create_table :follows do |t|
t.references :user, null: false, foreign_key: true
t.references :follower, null: false, foreign_key: {to_table: :users}
t.timestamps
end
add_index :follows, [:user_id, :follower_id], unique: true
end
end
```
In this migration, we’ve defined that each follow record belongs to one user while followers belong to users as well.
The second line of code specifies that followers are references to users model and they are referenced using their ID column.
After creating our migration for our Follow model, we’ll need to add our associated models.
Add Associations to Models
Inside our User model, we’ll add the following associations:
“`
class User < ApplicationRecord
has_many :follows, dependent: :destroy
has_many :followers_relationships, class_name: 'Follow', foreign_key: 'follower_id', dependent: :destroy
has_many :following_users, through: :follows, source: 'follower'
has_many :followers, through: :followers_relationships, source: 'user'
end
```
In the above code snippet, we have specified two sets of associations. One is for the users who are following one or more other users. The second is for any user who has one or several followers.
Next up is defining follow and unfollow models using class methods.
Defining Methods for Following and Unfollowing Users
After creating associations to our User model, a couple of new class methods need to be defined on our Follow model to facilitate users to follow and unfollow relationships as defined below:
“`
class Follow < ApplicationRecord
def self.follow(user_id, follower_id)
create(user_id: user_id,follower_id:follower_id)
end
def self.unfollow(user_id,follower_id)
where("user_id=? and follower_id=?", user_id, follower_id).destroy_all
end
end
```
After adding these methods to our application logic, we can simply invoke them like this:
```
Follow.follow(current_user.id,params[:id])
Follow.destroy(current_user.id,params[:id])
```
Implementing User Interface for Following and Unfollowing Users
Like with friend requests relationship implementation, we’ll also use standard Rails CRUD functionality and partials to create a UI that will allow users to follow and unfollow other users seamlessly.
The first step to creating a user interface is generating the Follow controller using the scaffold command in Terminal:
“`
rails g scaffold FollowRelationship following_id:integer follower_id:integer
“`
Advanced Topics in Setting up Relationships with Friends or Followers
The core functionalities of a relationship system are explained in detail
7 FAQs about Setting Up Relationships in Rails with Friends or Followers
1. What is the difference between a friend and a follower in Rails?
A friend relationship is mutual, meaning both users must confirm the friendship request. Follower relationships are one-sided, where one user can follow another without confirmation from the other user.
2. How do I set up user relationships in Rails?
You can use gems like “acts_as_follower” or “acts_as_friendable” to set up user relationships in Rails. These gems provide easy-to-use methods for creating and managing relationships between users.
3. Can users have both friends and followers?
Yes, users can have both friends and followers in Rails. The two types of relationships are not mutually exclusive.
4. How do I display a list of a user’s friends or followers?
You can use the “friends” or “followers” methods provided by the “acts_as_friendable” or “acts_as_follower” gems to retrieve a list of a user’s friends or followers, respectively. Then, you can display this list however you like using Rails’ built-in view templates.
5. Can I customize the information displayed about each relationship?
Yes, you can customize the information displayed about each relationship by modifying the views associated with the “acts_as_friendable” or “acts_as_follower” gems. Most commonly, users will display profile pictures and names for each friend or follower.
6. How do I allow users to delete friendships or followers?
You can use the “destroy_friendship!” or “unfollow!” methods provided by the “acts_as_friendable” or “acts_as_follower” gems, respectively. These methods will remove the relationship between users and update any associated records (such as friend/follower counts).
7. Is there a way to limit the number of friends or followers a user can have?
Yes, you can add custom validations to your user model to enforce limits on the number of friends or followers a user can have. For example, you could add a validation that prevents users from having more than 500 followers.
keys takeaways
4 Key Takeaways for Setting Up Relationships in Rails with Friends or Followers
1. Understand the Relationship Model
Before starting to set up relationships in Rails, it’s important to understand the relationship model that best fits your needs. Consider whether you need a simple friend-relationship or a more complex follow-relationship.
2. Use Appropriate Associations
Once you have decided on the relationship model, use appropriate associations like has_many, belongs_to, and has_many through to connect different models. Code these associations effectively for easy retrieval of data between associated models.
3. Create Validations
To ensure the integrity of your data and improve user experience, it’s crucial to create validations for the relationships between models. Validate unique combinations of friend_id and follower_id and establish their presence in tables for seamless functionality.
4. Optimize the Display of Relationships
One key factor in developing social networks is optimizing the display of relationship management: be it choosing styles for followers and friends counts or incorporating sorting functionality based on those counts. Consider using authentication techniques like Devise Gem and utilizing jQuery to make the interface sleek and intuitive.
By following these four key takeaways, setting up rich relationships in Rails with friends or followers will become a breeze.