0

I'm trying to set up a relationship like this:

I am making a list of all recipes across multiple recipe books. A recipe can only belong to one recipe book. A recipe has a list of components, which can either be an individual ingredient or another recipe.

So, for example, I have the recipe for a salad which has as its components lettuce (an ingredient), tomatoes (an ingredient), and dressing (a recipe). The dressing then has a list of it's own ingredients.

I want to utilize a polymorphic relationship called "Components" on the Recipe class that will self-reference the Recipe class or reference the Ingredients class.

For a recipe, I also want to be able to see if it's used in any other recipes and what the total ingredients are (i.e. it's direct ingredients and all the ingredients of its component recipes)

Can someone tell me how to set this up? I know how to do one or the other, but not both.

Thanks!

AELSchauer
  • 33
  • 4

1 Answers1

1
class Recipe < ApplicationRecord
  has_many :recipe_components
  has_many :components, through: :recipe_components
end
rails g model recipe:belongs_to component:belongs_to{polymorphic}
class RecipeComponent < ApplicationRecord
  belongs_to :recipe
  belongs_to :component, polymorphic: true
end

This will let you associate recipies with ingredients, other recipies or any other class you please. However its not really a true self-referential assocation as thats when you put a foreign key on a table that points back to the same table.

DB diagram

The two concepts are actually mutally exclusive* since a self join by defintion always points to a fixed table (itself). A join table that happens to reference the same table twice is not self-referential.

# Not self-referential 
class Friendship < ApplicationRecord
  belongs_to :user, class_name: 'User'
  belongs_to :other_user, class_name: 'User'
end

For a recipe, I also want to be able to see if it's used in any other recipes

Once you want to go the other way on the tree stuff starts getting crazy:

class Recipe < ApplicationRecord
  has_many :recipe_components_as_component,
    class_name: 'RecipeComponent',
    as: :component
  has_many :recipies, through: :recipe_components_as_component 
end

and what the total ingredients are (i.e. it's direct ingredients and all the ingredients of its component recipes)

This requires you to "walk the tree" with recursion and get all the associated items at each level. Huge question on its own and will very likely lead to you regretting using polymorphism. There are several gems that tackle this problem.

max
  • 96,212
  • 14
  • 104
  • 165
  • 1
    * or at least they should be. Polymorphic assocations are not actually referencing anything strictly speaking since its a wonky hack to mosh together object oriented code with a relational database. – max Sep 20 '21 at 17:22