Self referencing tables and making friends with Ruby on Rails.
By: Camille Feghali
Tags: Flatiron School, Ruby, Web Development, Ruby on Rails, Activerecord

For my first project at Flatiron School’s Full-Stack Web-Dev program, I built a Karaoke Command Line Application. Using Ruby’s ActiveRecord Gem, I was able to setup the relations between my models’ many-to-many relationships. It looked something like this:

A playlist has many songs and songs belong to many playlists. With the tracklist serving as the join table, I built the app, felt good about my mastery of Object Relational Mapping and called it a day after my partner and I nailed the presentation. Later in the day, we were given the challenge to setup relationships of a social media domain where users can befriend other users. To explain it simply, my confidence plumetted — We since moved on from CLI apps to Rails, so this will be a walkthrough of how to set up a simple Rails app where users can befriend other users. Welcome to my self-referencing tables using Ruby on Rails blog!

Before we do anything, let’s sketch out our tables:

From these awesome tables,

We can say the following:

We have four users.

User with id 1 is followed by users 3&4 — User 1 is a followee of users 3 and 4 and users 3 and 4 are followers of user 1.

of users 3 and 4 and users 3 and 4 are of user 1. User 1 and User 2 follow each other — user 1 is both a followee and follower of user 2. And vice versa.

Let’s get started:

Step — 1: Create the rails app.

Run rails new <Your-App-Name> to create your app.

To make sure everything works smoothly, cd into the app folder and run rails -v to make sure you're on Rails 5.2.2 as this is the version of Rails I'm using at the moment of writing.

A small issue I have faced is the compatibility of Rails 5 and the default sqlite version it comes with — But this is easily resolved by going to your Gemfile , and changing the line gem 'sqlite3' to gem 'sqlite3', '~> 1.3.6' .

Run bundle and we're all set.

Step — 2: Create your models, controllers & routes.

For the purpose of this exercise, we will only need two Models — The User Model and the Follow Model that will serve as the "join" table.

To create our User resource — in terminal, run:

rails g resource User name:string

Followed by:

rails g resource Follow follower_id:integer followee_id:integer

Then run your migrations with rake db:migrate , and you should see a schema.rb file in your /db directory that looks like this:

ActiveRecord::Schema.define(version: 2019_02_26_222107) do

​

create_table "follows", force: :cascade do |t|

t.integer "follower_id"

t.integer "followee_id"

t.datetime "created_at", null: false

t.datetime "updated_at", null: false

end

​

create_table "users", force: :cascade do |t|

t.string "name"

t.datetime "created_at", null: false

t.datetime "updated_at", null: false

end

​

end

Step — 3: Set up the relationships

This is the tricky part, we want to tell Rails to set a follower_id to a user_id and a followee_id to a user_id .

Edit your Follow Model to this:

class Follow < ApplicationRecord

belongs_to :follower, class_name: 'User'

belongs_to :followee, class_name: 'User'

end

And your User Model to this:

class User < ApplicationRecord

has_many :followed_users, foreign_key: :follower_id, class_name: 'Follow'

has_many :followees, through: :followed_users



has_many :following_users, foreign_key: :followee_id, class_name: 'Follow'

has_many :followers, through: :following_users

end

If you’re like me and haveonly been making One-to-Many or normal Many-to-Many relationships until now, the syntax may be confusing, so let’s break it down. Pay attention to the pluralization of the bold words.

The first two lines can be pseudo-coded like this: a User follows many Users, and their ids will be set in the followee_id column of the follows “join” table. And thus a user follows many users — or has many followees through followed_users.

The next two lines translate to: a User has many followers, and his followers’ ids will be set in the follower_id column of the follows “join” table. So a user has many followers, through following_users.

Let’s see it in action in our Rails console.

Run rails c to enter the console, and run the following lines.

charlie = User.create(name:"Charlie")

jeff = User.create(name:"Jeff")

ali = User.create(name:"Ali")

josh = User.create(name:"Josh")

Now let’s make Jeff, Ali, and Josh followers of Charlie.

charlie.followers << [jeff, ali, josh]

To make sure it worked, run:

charlie.followers

And you should receive an array holding the user instances of Jeff, Ali and Josh.

Great! now that our relations are setup, lets make a quick user interface for our awesome Social media followers app!

Step — 4: Set-up our Database

Run rake db:reset to empty the database

to empty the database In your db/seeds.rb file pase the following: This will create five users and make four of them follow one.

User.create(name: "Charlie") User.create(name: "Jeff") User.create(name: "Ali") User.create(name: "Quinn") User.create(name: "Masha") User.first.followers << [User.second, User.third, User.fourth, User.fifth,]

Run rake db:seed to fill our database with dummy data.

Step — 5: Set-up our Controller actions and instance methods

For the purpose of the exercise, we will only make an index and show page.

In the /app/controllers/users_controller.rb copy the following:

class UsersController < ApplicationController

​

def index

@users = User.all

end

​

def new

@user = User.new

end

​

def create

@user = User.create(name: params[:user][:name])

redirect_to users_path

end

​

def show

@user = User.find(params[:id])

@followers = @user.followers

@followees = @user.followees

@not_following = @user.not_following

end

​

def update

@user = User.find(params[:id])

# byebug

User.find_by(name: params[:user][:name]).followers << @user

redirect_to "/users/#{@user.id}"

end

​

end

And add the following method to your User model:

def not_following

@following = self.followees

@users = User.all

@not_following = @users - @following

end

Step — 6: Set-up our Views

In your app/views/users create two files:

index.erb

new.erb

In the index.erb file enter the following code:

<h1>The users of this app are</h1>

<% @users.each do |user| %>

<p><%= link_to user.name, user_path(user) %></p>

<% end %>

In the show.erb file enter the following code:

<h1>Hey! <%= @user.name %></h1>

<h2>Your followers are</h2>

<% @followers.each do |follower| %>

<p><%= follower.name %></p>

<% end %>

​

<h2>You follow</h2>

<% @followees.each do |followee| %>

<p><%= followee.name %></p>

<% end %>

​

<h2>Here's who you don't follow</h2>

<% @not_following.each do |user| %>

<p><%= user.name %></p> <%= form_for user, url: { action: "update" } do |f| %>

<%= f.hidden_field :name %>

<%= f.submit "Add Friend" %>

<% end %>

<% end %>

Step — 7: Try it out!

Launch the server with rails s .

. Enter the following url : http://localhost:3000/users

Click on one of the users.

Make friends! :)

Thanks for reading, I hope this helps you establish self-referencing models in your projects!

References:

https://medium.com/@TheDickWard/self-referential-relationships-aka-self-joins-in-rails-64f8f36ac311