Reputation: 783
I have 3 tables.
product
product_attribute_mappings
product_attribute_values
Here are some rows of each table.
products
id |name
1058|shoes
product_attribute_mappings
id | product_id | product_attribute_id
438 | 1058 | 9
product_attribute_values
id | product_attribute_mapping_id | value
2001 | 438 | 18 oz
2002 | 438 | 19 oz
As you can see here,
product.id = product_attribute_mappings.product_id
product_attribute_values.product_attribute_mapping_id = product_attribute_mappings.id
I want to get all product attributes values like
product.product_attribute_values # ["18 oz", "19 oz"]
But I am not sure how I can make models with associations to get as I want.
Does anyone have any idea?
Upvotes: 0
Views: 34
Reputation: 101831
What you have is a really strange backwards variant of the Entity Attribute Value (EAV) pattern. It would make more sense if you had the normalized attribute definitions (eg volume, weight, number of doodads, etc) on one table and the entitiy, attribute and value on one table.
class Product < ApplicationRecord
has_many :product_attributes
has_many :product_attribute_types, through: :product_attributes
# eager loading scope
def self.eager_load_attributes
eager_load(product_attributes: :product_attribute_types)
end
end
# This is the normalization table that stores the definition of an attribute
# rails g model ProductAttribute name:string unit:string
class ProductAttributeType< ApplicationRecord
has_many :product_attributes
has_many :product_attribute_types, through: :product_attributes
end
# This is the actual table that defines the attributes
# rails g model ProductAttribute product:belongs_to product_attribute_type:belongs_to value:jsonb
class ProductAttribute < ApplicationRecord
belongs_to :product # the entity
belongs_to :product_attribute_type # the attribute
# just shortcuts
delegates :name, to: :product_attribute_type
delegates :unit, to: :product_attribute_type
end
This uses a JSON column as the value to alieviate one of the classical issues with EAV which is that you have to cast everything into a single (usually string) type column. JSON can store numbers (not terribly well), strings, arrays and objects.
This lets you iterate through the products and attributes with:
# eager loading avoids a n+1 query
@products = Product.eager_load_attributes.all
@products.each do |product|
product.product_attributes.each do |attr|
puts "#{attr.name}: #{attr.value}{attr.unit}"
end
end
Upvotes: 1
Reputation: 362
You can change your association name from product_attribute_values
to product_attribute_values_association
, then define product_attribute_values
as instance methods
Upvotes: 0