Active Record Associations Guide
Tags: ruby, beginners, programming, webdev
Published: 2022-03-22 23:53:24

## Active Record Associations
Programmers use associations in code to be able to access data in different tables. Associations allow us to shorten down our code and makes things simpler but helping us not write as much SQL. Associations follow the idea of "convention over configuration", which is the most ideal practice when it comes to programming.


## Types of Active Record Associations
There can be different types of relationships between Classes. We're going to go through the following: 
- one-to-one
- one-to-many
- many-to-many


## Our Example
To be consistent throughout this blog, we're going to stick with one example and create different associations through that. In our example, we are going to have a Cookbook, which has an author, a publisher, recipes, and ingredients. We'll walk through the different relationships each of these may have with one another.


## Where to Begin
The best place to start is by mapping out what data we have and understanding how our different tables will relate to each other. This helps us understand what kind of relationship the Classes have with each other, and will especially help us figure out where the foreign keys belong. The best way to do this is by creating an Entity Relationship Diagram (ERD).


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0lkceajrsrt7bs9mqfln.png)

This ERD shows us our different tables, which columns that may have, and a rough idea as to the relationship amongst by tracking which tables have other tables foreign keys. This table may make a bit more sense as we walk through each relationship so take a look back at it along the way.

Now we can start building out our associations in code by using Active Record macros (a method that helps write our code).


## One-to-One Relationship
A one-to-one relationship means that one table belongs to solely another. In our example, we can say that the cookbook has one publisher, and that publisher belongs to the cookbook. We write this using the **has_one** macro along with the **belongs_to**

```ruby
class Cookbook < ActiveRecord::Base
  has_one :publisher
end

class Publisher < ActiveRecord::Base
  belongs_to :cookbook
end

```
_Syntax Notes_: **has_one** and **belongs_to** both will point to a word in _singular_ form.

---
This establishes a _one-to-one, bi-directional_ connection since they point to each other.

We can establish a _one-to-one, one-directional_ connection by using **belongs_to** on it's own. Such as, an editor may belong to a publisher, but the publisher may not always have or use the editor.

```ruby
class Editor
  belongs_to :publisher
end
```

We can create another _one-to-one, bi-directional_ connection by using a join table. In this case we would use the macro **has_one through:**. We have an author, that has a publisher, who publishes their cookbook. Therefore, the cookbook has an author. The publisher belongs both to the cookbook and the author, as a "middle-man". We would write this like this:

```ruby
class Cookbook < ActiveRecord::Base
  has_one :publisher
  has_one :author through: :publisher
end

class Publisher < ActiveRecord::Base
  belongs_to :cookbook
  belongs_to :author
end

class Author < ActiveRecord::Base
  has_one :publisher
end
```
_Note_: The order of macros matters! We need to put our has_one :car, before going through it in our Owner class.


## One-to-Many Relationship
A one-to-many relationship means that one class has many instances of another class, and that second class will belong to the first class. Our cookbook has many ingredients in it, and those ingredients belong to the cookbook. We can write this out using the **has_many** macro, along with the **belongs_to** macro.

```ruby
class Cookbook < ActiveRecord::Base
   has_many :ingredients
end

class Ingredient < ActiveRecord::Base
   belongs_to :cookbook
end

```
_Syntax Notes_: **has_many** will always point to a word in _plural_ form. 

---

If you ever get confused, it's helpful to say aloud what it would sound like, as Ruby is very programmer friendly and makes it sound like our day to day grammar.

---

## Many-to-Many Relationship
Many-to-many means one table can have many instances of another, and visa versa. We've established our cookbook has many ingredients. Those ingredients make up (belong to)a recipe. We can then say our cookbook has many recipes, through the ingredients it has listed.  We would write this using the **has_many through** macro. 

```ruby
class Cookbook < ActiveRecord::Base
   has_many :ingredients
   has_many :recipes, through: :ingredients
end

class Ingredient < ActiveRecord::Base
  belongs_to :cookbook
  belongs_to :recipe

class Recipe < ActiveRecord::Base
   has_many :ingredients
   belongs_to :cookbook
end

```
In this case, the Ingredients class is a join table, because it joins the relationship between the two others, by allowing Cookbook to go through it to access Recipes.


We can also use the macro **has_and_belongs_to_many** which creates a direct many-to-many connection with another model, _without_ the joined table. We could say we have a recipe that combines two ingredients, to then use as an ingredient in another recipe (think making your own pasta sauce). The recipe has many ingredients, the ingredients have many recipes, the recipe belongs to the ingredients, and the ingredients also belong to the recipes. (Ik ik, it's a lot). This is how we would write it:

```ruby
class Ingredient < ActiveRecord::Base
  has_and_belongs_to_many :recipes
end

class Recipe < ActiveRecord::Base
  has_and_belongs_to_many :ingredients
end

```
*Of note, this does not follow the relationship set up with our ERD since there will be a change to the foreign keys. In this situation, another join or junction table will be created to hold both foreign keys. The standard is to call the join table "recipes_ingredients".


## Creating Associations during Migration
We have been creating our associations through macros in our classes. We can also create associations when we make our tables before migration, which will help assist us with setting up the foreign keys. For example with our Cookbook:

```ruby
class CreateANewCookbook < ActiveRecord::Migration[6.1]
  def change
    create_table :cookbook do |t|
      t.string :name
    end

    create_table :ingredient do |t|
      t.string :name
      t.belongs_to :cookbook
      t.belongs_to :recipe
    end

    create_table : recipe do |t|
      t.string :item_name
    end
end

```


## Polymorphic Associations
Polymorphic associations are when a model can belong to multiple other models, but with only a single association and doesn't go through a join table. Let's say we have food pictures. Those pictures could either belong to a recipe or it can belong to the cookbook (spread throughout not connected to the recipe). These two locations aren't connected to each other, and the pictures have a single association with each. 

```ruby
class Picture < ActiveRecord::Base
  belongs_to :imageable, polymorphic: true
end

class Cookbook < ActiveRecord::Base
  has_many :pictures, as: :imageable
end

class Recipe < ActiveRecord::Base
  has_many :pictures, as: :imageable
end
 
```
*imageable is an unwritten naming-convention. Imageable means that the Picture model doesn't belong to either Cookbook or Recipe, but belongs to imageable, which can then be used to create the association between either Cookbook and Picture or Recipe and Picture.


## To Conclude
When we establish these relationships, it allows us then to use various methods on the classes. If we create a relationship between Ingredient and Cookbook, we can then call self(Cookbook).ingredient as a method, and so on and so forth. These Active Record Associations act as our getter and setter methods we may have used previously, so we no longer need to initiate or read the instance, because this does it for us. 

You know have your _associate_ degree - Happy coding!

---
Sources

https://guides.rubyonrails.org/association_basics.html

https://stackoverflow.com/questions/59173076/correct-foreign-keys-for-has-and-belongs-to-many-association

https://www.pilanites.com/polymorphic-associations-rails/