Rails: Self Referential Tables
Tags: ruby, rails, sql, database
Published: 2022-08-04 19:30:00

For Sunday Night Football we crack open some cold ones with the boys, our friends. In other words, we're interacting with our friends. Virtually every social media application that has ever existed has some form of interaction between a user and their friends. You can message them, play games with them, tag them in posts, or add new friends... but what's actually happening on the back end? From a rails stand point, you can use Self Referrential Tables.

## What are self referrential tables?

Take my previous Sunday Night Football example; we're interacting with friends right? But the word "friend" is a word we use to explain the _relationship between two people_. This is the basis of self referrential tables. A user has_many friends, but in technical terms, a user has_many _other users_. Self referrential tables are very confusing at first, so I'll give you some examples on how this data dance looks on the backend.

## Models, serializers, and controllers... oh my!

For a self referrential table, we need two models (assuming a social media application for this example): The users, and the relationship: friends. Through this friends table you'll be able to connect users different ways. In this example we'll be looking at a following system. Take a look at this User.rb model from a recent project of mine:

![user model](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c7fprxrq8i2jrg5xzog1.png)

User.rb has_many catpanions, that's the name of the self referrential table connecting users. So what's this `has_many :friends, through: :catpanions` business? Well, let's now take a look at the Catpanions.rb model:

![catpanion model](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7n17bel35l91dg2r086s.png)

So a catpanion belongs_to a user, but _also_ belongs_to a friend. We haven't made a friend model, so what's going on here? We've essentially told Ruby that there's other instances called friends that are really just instances of the User class. Now we need our foreign_keys.

![catpanion serializer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jkhx9zef5ggchpviiiz4.png)
 
These IDs are accessible because we declared our relationships when creating the resources:

![catpanions create table](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0rk25fce45fiicf4uh02.png)

With all this set up, in order to create a friendship between two users you'll have to create the route in the controller handling the self referrential table. 

![route](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0iys63pixhhh8qt0puf9.png)
 
When we fetch this route, we're telling the engine to first see if the user we're trying to add already exists in our friendships. If so, throw an error. If not, you have a new friend! The params used for the create should be the user_id and the friend_id. If you create a route that shows all the friendships, it should look something like this:

![postman](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5rlmxfdqsowljnf6jpcy.png)

Keep in mind, the user_id is the current user, and the friend_id is the person that they have followed. For the final touches, we want a way to access the people we've followed, right? In your user serializer, add the attribute `:friends`:

![user serializer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/71ag5ofkvedorli0aoss.png)

Now if we take a look at a specific user, our data will reflect their friendships with the users they have followed:

![user json](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4wk5tg2gyepfpb2z59jl.png)
 
This kind of data can be extremely powerful when establishing some form of connection between users. If you're feeling a little overwhelmed don't worry, I had to play around with this concept for days to feel comfortable enough to use it in a project!