Scope is decided by product_id
Theproduct_id field on ProductAttribute decides the scope. It’s null for a global attribute and holds the product’s id for an inline one:
The admin, vendor, and store attribute list routes all filter on
product_id: null, so inline attributes never appear in the catalog. You read them from the product through the scoped_attributes link instead.
Create an inline attribute
You create an inline attribute while you attach it. Anadd entry with a title instead of an id creates a product-scoped attribute, its values, and the links in one step:
type, the workflow infers it: is_variant_axis: true makes a multi_select axis, a boolean value makes a toggle, and anything else makes a text attribute. Inline attributes get no derived handle, so their names never clash with the catalog.
The two scopes behave differently over their life:
- Rename: an
updateentry with atitlerenames an inline attribute and, for an axis, its exclusive option, but it’s ignored for a global attribute - Remove: removing an inline attribute from its product deletes the attribute and its values, while removing a global one drops the product’s value links and, for an axis, detaches the option
- Delete:
deleteProductAttributesWorkflowrefuses to delete a global attribute while any of its values are linked to a product
Scope global attributes to categories
A global attribute can link to product categories through thecategories link. The admin and vendor attribute lists accept a product_category_id filter and return two groups: attributes linked to that category, and attributes linked to no category at all. Category links change where an attribute is offered:
- No categories: the attribute is offered for every product
- One or more categories: the attribute is offered only for products in those categories
category_ids to createProductAttributesWorkflow to create the links with the attribute.
Storefront filters
Shoppers filterGET /store/products with attributes[<handle>]=value query parameters. The store middleware turns each one into a filter on variants.options.value, so filtering works on variant axis values. Several values for one attribute are OR’d, and separate attributes are AND’d.
is_filterable doesn’t change that query. It’s a flag your storefront reads from the store attribute list to decide which attributes to render as facets.