packages/core/src/links connect them: two that store data in pivot tables and three read-only links resolved from a foreign key on one side. Resolve query from the container to read across them.
Product to attribute value
The values a product holds, for every attribute type. This is the link the attachment workflows write.Product to scoped attributes
The inline attributes created on a product. The link is read-only and resolvesProductAttribute.product_id against product.id.
Attribute to category
The product categories a global attribute is offered for. An attribute with no category links is offered for every product.createProductAttributesWorkflow writes these links from category_ids. Write more yourself with the link service, using the keys product_attribute_id and product_category_id.
Attribute to product option
The MedusaProductOption that backs a variant axis. The link is read-only and keyed on ProductAttribute.product_option_id.
Attribute value to option value
The MedusaProductOptionValue that backs one value of a variant axis. The link is read-only and keyed on ProductAttributeValue.product_option_value_id.
Core code reads the mirror through the foreign keys, not through these links. Do the same: read the ids from the attribute, then query the option.
The workflows keep the mirror in step. Never write
product_option_id or product_option_value_id yourself, or the attribute and its option will drift apart.