Step 8: Polymorphic Relationships

View the Diff

Let’s introduce the concept of a Note. A Note can belong to a Department, an Employee, or a Team. For this, we’ll need to introduce the concept of polymorphism.

id notable_id notable_type body
1 1 Employee A Sample Note!
2 1 Department Another Sample Note!
3 1 Team A Third Sample Note!

The Rails Stuff 🚂

$ rails generate model Note notable:references{polymorphic}:index
$ bin/rails db:migrate

Make sure to add the corresponding model relationships:

# app/models/employee.rb
has_many :notes, as: :notable
# app/models/team.rb
has_many :notes, as: :notable
# app/models/department.rb
has_many :notes, as: :notable

# app/models/note.rb
belongs_to :notable, polymorphic: true

Finally, make sure to edit your seed file - check out the diff to see the necessary adjustments.

The Graphiti Stuff 🎨

$ bin/rails g graphiti:resource Note body:text

Let’s create our NoteResource:

class NoteResource < ApplicationResource
  attribute :body, :string

  filter :notable_id, :integer
  filter :notable_type, :string, allow: %w(Employee Department Team)

  polymorphic_belongs_to :notable do
    group_by(:notable_type) do
      on(:Employee)
      on(:Team)
      on(:Department)
    end
  end
end

And corresponding associations:

# app/models/employee.rb
polymorphic_has_many :notes, as: :notable
# app/models/team.rb
polymorphic_has_many :notes, as: :notable
# app/models/department.rb
polymorphic_has_many :notes, as: :notable

Digging Deeper 🧐

When defining a polymorphic relationship for our API, we’re saying “grab all the parent records, group them by a type column, and execute different queries for each type”. This way records with notable_type == 'Employee' can hit the employees table, but records with notable_type == 'Department' could in theory load from a different API altogether.

Each of the on lines defines a new belongs_to association. That means you can customize just like always:

on(:Team).belongs_to :team, resource: SomeCustomTeamResource do
  # assign {}
  # link {}
  # ... etc ...
end