@mercurjs/docs 2.3.8-canary.3 → 2.3.9-canary.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/content/learn/architecture.mdx +166 -263
- package/content/learn/introduction.mdx +9 -1
- package/content/learn/use-cases.mdx +70 -0
- package/content/platform/attribute/concepts/attribute-types.mdx +73 -46
- package/content/platform/attribute/concepts/global-vs-inline.mdx +54 -32
- package/content/platform/attribute/concepts/variant-axes.mdx +54 -32
- package/content/platform/attribute/guides/attach-attributes-to-a-product.mdx +96 -53
- package/content/platform/attribute/guides/create-a-variant-axis.mdx +106 -57
- package/content/platform/attribute/guides/create-an-attribute.mdx +105 -56
- package/content/platform/attribute/overview.mdx +37 -33
- package/content/platform/attribute/reference/data-models.mdx +53 -38
- package/content/platform/attribute/reference/events.mdx +47 -24
- package/content/platform/attribute/reference/links.mdx +98 -27
- package/content/platform/attribute/reference/service.mdx +71 -32
- package/content/platform/attribute/reference/workflows.mdx +158 -28
- package/content/platform/catalog/concepts/master-products.mdx +39 -37
- package/content/platform/catalog/concepts/product-seller-allowlist.mdx +56 -35
- package/content/platform/catalog/concepts/status-lifecycle.mdx +55 -49
- package/content/platform/catalog/concepts/variants-categories-collections.mdx +55 -35
- package/content/platform/catalog/guides/allowlist-stores.mdx +100 -37
- package/content/platform/catalog/guides/create-a-master-product.mdx +124 -48
- package/content/platform/catalog/guides/publish-or-reject-a-product.mdx +120 -55
- package/content/platform/catalog/overview.mdx +39 -35
- package/content/platform/catalog/reference/data-models.mdx +154 -43
- package/content/platform/catalog/reference/events.mdx +41 -25
- package/content/platform/catalog/reference/links.mdx +196 -28
- package/content/platform/catalog/reference/service.mdx +57 -35
- package/content/platform/catalog/reference/workflows.mdx +124 -30
- package/content/platform/commission/concepts/order-commission-lines.mdx +54 -52
- package/content/platform/commission/concepts/rule-matching.mdx +53 -63
- package/content/platform/commission/concepts/rules-and-rates.mdx +61 -59
- package/content/platform/commission/guides/batch-update-rules.mdx +69 -41
- package/content/platform/commission/guides/create-a-rate.mdx +101 -53
- package/content/platform/commission/guides/refresh-order-commission-lines.mdx +104 -35
- package/content/platform/commission/overview.mdx +43 -35
- package/content/platform/commission/reference/data-models.mdx +59 -45
- package/content/platform/commission/reference/events.mdx +28 -34
- package/content/platform/commission/reference/links.mdx +42 -33
- package/content/platform/commission/reference/service.mdx +103 -28
- package/content/platform/commission/reference/workflows.mdx +101 -27
- package/content/platform/offer/concepts/pricing-and-inventory.mdx +63 -49
- package/content/platform/offer/concepts/shipping.mdx +28 -29
- package/content/platform/offer/concepts/what-is-an-offer.mdx +48 -45
- package/content/platform/offer/guides/bulk-create-offers.mdx +97 -61
- package/content/platform/offer/guides/create-an-offer.mdx +112 -60
- package/content/platform/offer/guides/manage-offer-inventory.mdx +78 -38
- package/content/platform/offer/overview.mdx +39 -32
- package/content/platform/offer/reference/data-models.mdx +77 -47
- package/content/platform/offer/reference/events.mdx +44 -18
- package/content/platform/offer/reference/links.mdx +92 -40
- package/content/platform/offer/reference/service.mdx +66 -31
- package/content/platform/offer/reference/workflows.mdx +146 -37
- package/content/platform/order-group/concepts/computed-totals.mdx +40 -37
- package/content/platform/order-group/concepts/order-splitting.mdx +41 -39
- package/content/platform/order-group/concepts/the-order-group.mdx +39 -32
- package/content/platform/order-group/guides/list-order-groups.mdx +96 -56
- package/content/platform/order-group/guides/retrieve-an-order-group.mdx +93 -42
- package/content/platform/order-group/guides/split-a-cart.mdx +115 -40
- package/content/platform/order-group/overview.mdx +42 -32
- package/content/platform/order-group/reference/data-models.mdx +48 -31
- package/content/platform/order-group/reference/events.mdx +29 -17
- package/content/platform/order-group/reference/links.mdx +90 -29
- package/content/platform/order-group/reference/service.mdx +55 -31
- package/content/platform/order-group/reference/workflows.mdx +83 -24
- package/content/platform/payout/concepts/account-lifecycle.mdx +64 -67
- package/content/platform/payout/concepts/accounts-and-onboarding.mdx +50 -40
- package/content/platform/payout/concepts/payout-pipeline.mdx +70 -73
- package/content/platform/payout/guides/create-a-payout-account.mdx +65 -41
- package/content/platform/payout/guides/process-a-provider-webhook.mdx +84 -38
- package/content/platform/payout/guides/start-provider-onboarding.mdx +90 -46
- package/content/platform/payout/overview.mdx +43 -34
- package/content/platform/payout/reference/data-models.mdx +63 -36
- package/content/platform/payout/reference/events.mdx +56 -30
- package/content/platform/payout/reference/links.mdx +52 -22
- package/content/platform/payout/reference/service.mdx +72 -33
- package/content/platform/payout/reference/workflows.mdx +80 -19
- package/content/platform/product-edit/concepts/change-actions.mdx +52 -45
- package/content/platform/product-edit/concepts/change-pipeline.mdx +60 -52
- package/content/platform/product-edit/concepts/status-and-auto-confirm.mdx +43 -46
- package/content/platform/product-edit/guides/confirm-or-decline-a-change.mdx +104 -56
- package/content/platform/product-edit/guides/edit-a-product.mdx +126 -46
- package/content/platform/product-edit/guides/request-a-revision.mdx +99 -39
- package/content/platform/product-edit/overview.mdx +41 -34
- package/content/platform/product-edit/reference/data-models.mdx +73 -47
- package/content/platform/product-edit/reference/events.mdx +39 -29
- package/content/platform/product-edit/reference/links.mdx +32 -24
- package/content/platform/product-edit/reference/service.mdx +45 -26
- package/content/platform/product-edit/reference/workflows.mdx +173 -31
- package/content/platform/review/concepts/product-vs-seller-reviews.mdx +42 -41
- package/content/platform/review/concepts/ratings-and-moderation.mdx +45 -44
- package/content/platform/review/concepts/the-review-model.mdx +52 -45
- package/content/platform/review/guides/compute-aggregate-ratings.mdx +62 -37
- package/content/platform/review/guides/create-a-review.mdx +109 -39
- package/content/platform/review/guides/moderate-a-review.mdx +83 -37
- package/content/platform/review/guides/respond-to-a-review.mdx +85 -40
- package/content/platform/review/overview.mdx +43 -33
- package/content/platform/review/reference/data-models.mdx +41 -27
- package/content/platform/review/reference/events.mdx +55 -37
- package/content/platform/review/reference/links.mdx +80 -22
- package/content/platform/review/reference/service.mdx +61 -33
- package/content/platform/review/reference/workflows.mdx +80 -24
- package/content/platform/store/concepts/lifecycle.mdx +68 -44
- package/content/platform/store/concepts/store-entity.mdx +45 -27
- package/content/platform/store/concepts/team.mdx +56 -35
- package/content/platform/store/guides/create-a-store.mdx +102 -35
- package/content/platform/store/guides/manage-the-team.mdx +122 -32
- package/content/platform/store/guides/moderate-a-store.mdx +107 -38
- package/content/platform/store/overview.mdx +41 -34
- package/content/platform/store/reference/data-models.mdx +132 -57
- package/content/platform/store/reference/events.mdx +42 -17
- package/content/platform/store/reference/links.mdx +67 -49
- package/content/platform/store/reference/service.mdx +105 -33
- package/content/platform/store/reference/workflows.mdx +304 -24
- package/content/references/api/conventions.mdx +20 -0
- package/content/resources/integrations/magento2.mdx +162 -0
- package/content/resources/integrations/overview.mdx +10 -0
- package/content/resources/integrations/stripe-connect.mdx +1 -1
- package/content/resources/tutorials/custom-api-route.mdx +3 -2
- package/llms.txt +113 -111
- package/package.json +1 -1
|
@@ -1,83 +1,132 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "Create a variant axis"
|
|
2
|
+
title: "Create a variant axis and build variants on it"
|
|
3
3
|
sidebarTitle: "Create a variant axis"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Create a multi_select axis attribute, then create a product whose variants bind to it."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
generate a product's variants from server code.
|
|
9
|
-
|
|
10
|
-
A variant axis is a `multi_select` attribute with `is_variant_axis` set. Creating
|
|
11
|
-
one mirrors a native Medusa `ProductOption`, so the values a product selects along
|
|
12
|
-
the axis become the dimensions Medusa uses to generate variants.
|
|
13
|
-
|
|
14
|
-
## Create the axis attribute
|
|
15
|
-
|
|
16
|
-
```ts title="src/api/custom/attributes/route.ts"
|
|
17
|
-
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
18
|
-
import { createProductAttributesWorkflow } from "@mercurjs/core/workflows"
|
|
19
|
-
|
|
20
|
-
export async function POST(req: MedusaRequest, res: MedusaResponse) {
|
|
21
|
-
const { result } = await createProductAttributesWorkflow(req.scope).run({
|
|
22
|
-
input: {
|
|
23
|
-
attributes: [
|
|
24
|
-
{
|
|
25
|
-
name: "Size",
|
|
26
|
-
type: "multi_select",
|
|
27
|
-
is_variant_axis: true,
|
|
28
|
-
is_filterable: true,
|
|
29
|
-
values: [{ name: "S" }, { name: "M" }, { name: "L" }],
|
|
30
|
-
},
|
|
31
|
-
],
|
|
32
|
-
},
|
|
33
|
-
})
|
|
7
|
+
Create a variant axis such as Size, then create a product that uses part of it and has one variant per selected value. You need this whenever products in your catalog come in sizes, colors, or any other dimension shoppers pick between.
|
|
34
8
|
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
```
|
|
9
|
+
## Before you begin
|
|
38
10
|
|
|
39
|
-
|
|
40
|
-
`ProductAttributeValue` for each option value. It wires `product_option_id` and
|
|
41
|
-
`product_option_value_id` behind the scenes.
|
|
11
|
+
You need the following:
|
|
42
12
|
|
|
43
|
-
|
|
13
|
+
- **A Mercur server**: an API app with `@mercurjs/core` registered through `withMercur()`
|
|
14
|
+
- **The concept**: read [Variant axes and product options](/platform/attribute/concepts/variant-axes) to see how an axis maps to a Medusa `ProductOption`
|
|
44
15
|
|
|
45
|
-
|
|
46
|
-
Medusa generates a variant for each selected value.
|
|
16
|
+
## Build the axis and the product
|
|
47
17
|
|
|
48
|
-
|
|
49
|
-
|
|
18
|
+
<Steps>
|
|
19
|
+
<Step title="Create the axis attribute">
|
|
20
|
+
An axis is a `multi_select` attribute with `is_variant_axis: true`. The workflow creates a global `ProductOption` titled `Size` with the same three values.
|
|
21
|
+
|
|
22
|
+
```ts title="src/scripts/seed-size-axis.ts"
|
|
23
|
+
import type { ExecArgs } from "@medusajs/framework/types"
|
|
24
|
+
import { AttributeType } from "@mercurjs/types"
|
|
25
|
+
import { createProductAttributesWorkflow } from "@mercurjs/core/workflows"
|
|
50
26
|
|
|
27
|
+
export default async function seedSizeAxis({ container }: ExecArgs) {
|
|
28
|
+
const { result } = await createProductAttributesWorkflow(container).run({
|
|
29
|
+
input: {
|
|
30
|
+
attributes: [
|
|
31
|
+
{
|
|
32
|
+
name: "Size",
|
|
33
|
+
type: AttributeType.MULTI_SELECT,
|
|
34
|
+
is_variant_axis: true,
|
|
35
|
+
is_filterable: true,
|
|
36
|
+
values: [{ name: "S" }, { name: "M" }, { name: "L" }],
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
},
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
container.resolve("logger").info(`Created axis ${result[0].id}`)
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Run it with `npx medusa exec ./src/scripts/seed-size-axis.ts`. Medusa keeps global option titles unique, so a second global axis named `Size` fails.
|
|
47
|
+
</Step>
|
|
48
|
+
<Step title="Create a product on the axis">
|
|
49
|
+
Mercur’s `createProductsWorkflow` takes `attributes` and `variants` together. It attaches the axis with the values you pick, then creates the variants against it.
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { createProductsWorkflow } from "@mercurjs/core/workflows"
|
|
53
|
+
|
|
54
|
+
const { result } = await createProductsWorkflow(req.scope).run({
|
|
55
|
+
input: {
|
|
56
|
+
created_by: req.auth_context.actor_id,
|
|
57
|
+
products: [
|
|
58
|
+
{
|
|
59
|
+
title: "Linen shirt",
|
|
60
|
+
attributes: [
|
|
61
|
+
{
|
|
62
|
+
id: "pattr_01JD8Q1234567",
|
|
63
|
+
value_ids: ["pattrval_01JD8Q1", "pattrval_01JD8Q2"],
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
variants: [
|
|
67
|
+
{ title: "Linen shirt / S", options: { Size: "S" } },
|
|
68
|
+
{ title: "Linen shirt / M", options: { Size: "M" } },
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Run this inside an authenticated route handler, so `req.auth_context.actor_id` records who created the product. Each variant names its option value by the option title, which is the attribute name. The product only holds `S` and `M`, so those are the variants it can have.
|
|
77
|
+
</Step>
|
|
78
|
+
</Steps>
|
|
79
|
+
|
|
80
|
+
## Change the values a product uses
|
|
81
|
+
|
|
82
|
+
Run `createAndLinkProductAttributesToProductWorkflow` with an `update` entry to grow or shrink the subset. The workflow moves the option values on the product and the pivot links together.
|
|
83
|
+
|
|
84
|
+
```ts
|
|
51
85
|
await createAndLinkProductAttributesToProductWorkflow(req.scope).run({
|
|
52
86
|
input: {
|
|
53
|
-
product_id: "
|
|
54
|
-
|
|
87
|
+
product_id: "prod_01JD8Q1234567",
|
|
88
|
+
update: [
|
|
89
|
+
{
|
|
90
|
+
id: "pattr_01JD8Q1234567",
|
|
91
|
+
add: ["pattrval_01JD8Q3"],
|
|
92
|
+
remove: ["pattrval_01JD8Q1"],
|
|
93
|
+
},
|
|
94
|
+
],
|
|
55
95
|
},
|
|
56
96
|
})
|
|
57
97
|
```
|
|
58
98
|
|
|
59
|
-
|
|
60
|
-
Only `multi_select` attributes can be variant axes. The `value_ids` you pass
|
|
61
|
-
are the per-product subset of the axis's values. Only those become variants.
|
|
62
|
-
</Note>
|
|
99
|
+
After the run, the product offers `M` and `L`. Create the `L` variant with Medusa’s `createProductVariantsWorkflow` and `options: { Size: "L" }`.
|
|
63
100
|
|
|
64
|
-
##
|
|
101
|
+
## Create an axis for one product
|
|
65
102
|
|
|
66
|
-
|
|
67
|
-
form instead of an existing id. This creates an exclusive `ProductOption`, a
|
|
68
|
-
scoped attribute (`product_id` set), and the value mirror in one call:
|
|
103
|
+
When only one product needs a dimension, add an inline axis instead of a global one. It creates an exclusive option (`is_exclusive: true`), a product-scoped `multi_select` attribute, and its values, and attaches them to the product.
|
|
69
104
|
|
|
70
105
|
```ts
|
|
71
106
|
await createAndLinkProductAttributesToProductWorkflow(req.scope).run({
|
|
72
107
|
input: {
|
|
73
|
-
product_id: "
|
|
74
|
-
add: [
|
|
108
|
+
product_id: "prod_01JD8Q1234567",
|
|
109
|
+
add: [
|
|
110
|
+
{ title: "Cut", is_variant_axis: true, values: ["Slim", "Regular"] },
|
|
111
|
+
],
|
|
75
112
|
},
|
|
76
113
|
})
|
|
77
114
|
```
|
|
78
115
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
116
|
+
An inline axis holds all of its values, so there’s no subset to pick. You add values to it later with `update` and `add: [{ value: "Relaxed" }]`.
|
|
117
|
+
|
|
118
|
+
## Add values to a global axis
|
|
119
|
+
|
|
120
|
+
`createProductAttributeValuesWorkflow` adds values to the attribute and to its global option in one run. Products don’t gain the new value until you add it to their subset.
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
await createProductAttributeValuesWorkflow(req.scope).run({
|
|
124
|
+
input: { attribute_id: "pattr_01JD8Q1234567", values: [{ name: "XL" }] },
|
|
125
|
+
})
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Next steps
|
|
129
|
+
|
|
130
|
+
- [Every add and update form for the batch workflow](/platform/attribute/guides/attach-attributes-to-a-product)
|
|
131
|
+
- [Global and inline attributes](/platform/attribute/concepts/global-vs-inline)
|
|
132
|
+
- [The workflows that keep the option in sync](/platform/attribute/reference/workflows)
|
|
@@ -1,81 +1,130 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "Create
|
|
2
|
+
title: "Create a catalog attribute"
|
|
3
3
|
sidebarTitle: "Create an attribute"
|
|
4
|
-
description: "Create a
|
|
4
|
+
description: "Create a global attribute with its values from a route, a seed script, or a catalog import."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
own server code, for example in a seed script, a custom API route, or a catalog
|
|
9
|
-
import.
|
|
7
|
+
Create global catalog attributes from your own server code, for example when you seed a marketplace or import a taxonomy from a product information system.
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
`ProductAttribute` records along with their values. Run it from any place that
|
|
13
|
-
has access to the Medusa container.
|
|
9
|
+
## Before you begin
|
|
14
10
|
|
|
15
|
-
|
|
11
|
+
You need the following:
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
- **A Mercur server**: an API app with `@mercurjs/core` registered through `withMercur()`
|
|
14
|
+
- **Workflow imports**: every workflow on this page comes from `@mercurjs/core/workflows`
|
|
15
|
+
|
|
16
|
+
## Create the attribute
|
|
17
|
+
|
|
18
|
+
<Steps>
|
|
19
|
+
<Step title="Run createProductAttributesWorkflow from a route">
|
|
20
|
+
The workflow takes a list of attributes, each with its values, and returns the created records.
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
22
|
+
```ts title="src/api/admin/catalog-import/attributes/route.ts"
|
|
23
|
+
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
24
|
+
import { AttributeType } from "@mercurjs/types"
|
|
25
|
+
import { createProductAttributesWorkflow } from "@mercurjs/core/workflows"
|
|
26
|
+
|
|
27
|
+
export async function POST(req: MedusaRequest, res: MedusaResponse) {
|
|
28
|
+
const { result } = await createProductAttributesWorkflow(req.scope).run({
|
|
29
|
+
input: {
|
|
30
|
+
attributes: [
|
|
31
|
+
{
|
|
32
|
+
name: "Material",
|
|
33
|
+
type: AttributeType.SINGLE_SELECT,
|
|
34
|
+
is_filterable: true,
|
|
35
|
+
values: [{ name: "Cotton" }, { name: "Linen" }],
|
|
36
|
+
},
|
|
37
|
+
],
|
|
30
38
|
},
|
|
31
|
-
|
|
32
|
-
},
|
|
33
|
-
})
|
|
39
|
+
})
|
|
34
40
|
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
```
|
|
41
|
+
res.status(201).json({ product_attributes: result })
|
|
42
|
+
}
|
|
43
|
+
```
|
|
38
44
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
Leaving out `product_id` makes the attribute global. The service derives the handle `material` and a handle for each value.
|
|
46
|
+
</Step>
|
|
47
|
+
<Step title="Scope it to categories">
|
|
48
|
+
Pass `category_ids` to link the attribute to product categories in the same run. An attribute with no categories is offered for every product.
|
|
42
49
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
```ts
|
|
51
|
+
await createProductAttributesWorkflow(req.scope).run({
|
|
52
|
+
input: {
|
|
53
|
+
attributes: [
|
|
54
|
+
{
|
|
55
|
+
name: "Thread count",
|
|
56
|
+
type: AttributeType.UNIT,
|
|
57
|
+
category_ids: ["pcat_01JD8Q1234567"],
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
})
|
|
62
|
+
```
|
|
48
63
|
|
|
49
|
-
|
|
64
|
+
A `unit` attribute has no predefined values. Each product gets its own value when you attach the attribute.
|
|
65
|
+
</Step>
|
|
66
|
+
<Step title="Add values later">
|
|
67
|
+
Run `createProductAttributeValuesWorkflow` to add values to an existing select attribute. It takes one `attribute_id` and the new values.
|
|
50
68
|
|
|
51
|
-
|
|
52
|
-
|
|
69
|
+
```ts
|
|
70
|
+
import { createProductAttributeValuesWorkflow } from "@mercurjs/core/workflows"
|
|
53
71
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
{
|
|
59
|
-
name: "Thread count",
|
|
60
|
-
type: "unit",
|
|
61
|
-
category_ids: ["pcat_bedding"],
|
|
72
|
+
await createProductAttributeValuesWorkflow(req.scope).run({
|
|
73
|
+
input: {
|
|
74
|
+
attribute_id: "pattr_01JD8Q1234567",
|
|
75
|
+
values: [{ name: "Wool" }, { name: "Silk", rank: 3 }],
|
|
62
76
|
},
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
77
|
+
})
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
For a variant axis, the workflow also adds the values to the mirror `ProductOption`.
|
|
81
|
+
</Step>
|
|
82
|
+
</Steps>
|
|
83
|
+
|
|
84
|
+
## Attach custom data with additional_data
|
|
85
|
+
|
|
86
|
+
Every catalog workflow accepts `additional_data` and passes it to its completion hook. Register a handler on `productAttributesCreated` to store your own data next to the attribute without forking the workflow.
|
|
87
|
+
|
|
88
|
+
```ts title="src/workflows/hooks/product-attributes-created.ts"
|
|
89
|
+
import { createProductAttributesWorkflow } from "@mercurjs/core/workflows"
|
|
90
|
+
|
|
91
|
+
createProductAttributesWorkflow.hooks.productAttributesCreated(
|
|
92
|
+
async ({ attributes, additional_data }, { container }) => {
|
|
93
|
+
const logger = container.resolve("logger")
|
|
94
|
+
const source = additional_data?.imported_from
|
|
95
|
+
|
|
96
|
+
for (const attribute of attributes) {
|
|
97
|
+
logger.info(`Attribute ${attribute.id} imported from ${source}`)
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
)
|
|
66
101
|
```
|
|
67
102
|
|
|
68
|
-
|
|
103
|
+
Pass the data when you run the workflow, for example `additional_data: { imported_from: "legacy_pim" }`. The workflow also exposes a `validate` hook that runs before anything is written, so you can throw there to reject the input.
|
|
104
|
+
|
|
105
|
+
## Update or delete an attribute
|
|
69
106
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
107
|
+
`updateProductAttributesWorkflow` takes a `selector` and an `update`. Keep these rules in mind:
|
|
108
|
+
|
|
109
|
+
- **Type is fixed**: the service throws if `update.type` differs from the current type
|
|
110
|
+
- **Renames reach the option**: changing `name` on an axis also renames its `ProductOption`
|
|
111
|
+
- **Deletes check usage**: `deleteProductAttributesWorkflow` throws `NOT_ALLOWED` while any value is linked to a product
|
|
73
112
|
|
|
74
113
|
```ts
|
|
75
|
-
|
|
114
|
+
import { updateProductAttributesWorkflow } from "@mercurjs/core/workflows"
|
|
115
|
+
|
|
116
|
+
await updateProductAttributesWorkflow(req.scope).run({
|
|
76
117
|
input: {
|
|
77
|
-
|
|
78
|
-
|
|
118
|
+
selector: { id: "pattr_01JD8Q1234567" },
|
|
119
|
+
update: { name: "Fabric", is_filterable: false },
|
|
79
120
|
},
|
|
80
121
|
})
|
|
81
122
|
```
|
|
123
|
+
|
|
124
|
+
The run renames the attribute and turns off its facet flag. The handle stays `material`, because handles are derived only on create.
|
|
125
|
+
|
|
126
|
+
## Next steps
|
|
127
|
+
|
|
128
|
+
- [Attach the attribute to a product](/platform/attribute/guides/attach-attributes-to-a-product)
|
|
129
|
+
- [Create a variant axis](/platform/attribute/guides/create-a-variant-axis)
|
|
130
|
+
- [Subscribe to attribute events](/platform/attribute/reference/events)
|
|
@@ -1,82 +1,86 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Attributes"
|
|
3
3
|
sidebarTitle: "Overview"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Describe the shared catalog with typed attributes, turn them into variant axes, and expose them as storefront filters."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Use
|
|
8
|
-
materials, sizes, capacities, or warranty toggles, and reuse it across the whole
|
|
9
|
-
marketplace.
|
|
7
|
+
Use attributes to give every product in your marketplace the same typed vocabulary, from sizes and materials to capacities and yes/no flags.
|
|
10
8
|
|
|
11
|
-
The Attribute
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Each one is either a global catalog entry reused everywhere or an inline field
|
|
16
|
-
scoped to a single product.
|
|
9
|
+
An attribute is a typed field that operators define once and attach to products in the shared master catalog. It describes products the same way across every store, feeds storefront filters, and, when it’s a variant axis, decides which variants a product can have. The Product Attribute module holds the attributes and their values. Links connect them to Medusa’s products, categories, and product options, so a variant axis is a real Medusa `ProductOption` underneath.
|
|
10
|
+
|
|
11
|
+
<img className="block dark:hidden" src="/images/figures/attribute-overview-light.svg" alt="The global attribute catalog lists Size, Material, Thread count, Gift wrap, and Care with their types, and Size is the one variant axis, backed by the global Medusa product option Size behind it." />
|
|
12
|
+
<img className="hidden dark:block" src="/images/figures/attribute-overview-dark.svg" alt="The global attribute catalog lists Size, Material, Thread count, Gift wrap, and Care with their types, and Size is the one variant axis, backed by the global Medusa product option Size behind it." />
|
|
17
13
|
|
|
18
14
|
<Note>
|
|
19
|
-
|
|
20
|
-
Product Attribute module (`MercurModules.PRODUCT_ATTRIBUTE`, id prefix
|
|
21
|
-
`pattr`). Products are the shared master catalog. Attributes never belong to a
|
|
22
|
-
store. Sellers list against master products through offers.
|
|
15
|
+
The data models are `ProductAttribute` (id prefix `pattr`) and `ProductAttributeValue` (id prefix `pattrval`), resolved with `MercurModules.PRODUCT_ATTRIBUTE` from `@mercurjs/types`. Attributes belong to the catalog, never to a store.
|
|
23
16
|
</Note>
|
|
24
17
|
|
|
25
18
|
## Key features
|
|
26
19
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
- **
|
|
30
|
-
- **
|
|
31
|
-
- **
|
|
32
|
-
- **
|
|
20
|
+
The Product Attribute module covers the full life of a catalog attribute:
|
|
21
|
+
|
|
22
|
+
- **Five types**: `single_select`, `multi_select`, `text`, `unit`, and `toggle`, each storing its values differently
|
|
23
|
+
- **Variant axes**: a `multi_select` attribute with `is_variant_axis` creates a Medusa `ProductOption` that variants bind to
|
|
24
|
+
- **Global or inline**: define an attribute once for the whole catalog, or create a one-off attribute on a single product
|
|
25
|
+
- **Category scoping**: link an attribute to product categories so it’s offered only for products in them
|
|
26
|
+
- **Storefront filters**: shoppers filter products by axis values, and `is_filterable` tells your storefront which attributes to show as facets
|
|
27
|
+
- **One batch workflow**: attach, detach, and update all of a product’s attributes in a single run
|
|
28
|
+
- **Hooks and events**: catalog workflows expose hooks and emit `product-attribute.*` and `product-attribute-value.*` events
|
|
29
|
+
|
|
30
|
+
## How it works
|
|
31
|
+
|
|
32
|
+
An attribute reaches a product in four steps:
|
|
33
|
+
|
|
34
|
+
1. An operator runs `createProductAttributesWorkflow`. For a variant axis, it also creates a global `ProductOption` with the same values.
|
|
35
|
+
2. A product is created with Mercur’s `createProductsWorkflow`, or edited with `createAndLinkProductAttributesToProductWorkflow`, and picks the attributes and values it uses.
|
|
36
|
+
3. Selected values link to the product. For an axis, the product also gets the option with only the selected subset of values.
|
|
37
|
+
4. Variants bind to the axis option by its title, for example `{ Size: "M" }`, and the storefront filters products by those option values.
|
|
33
38
|
|
|
34
39
|
## Get started
|
|
35
40
|
|
|
36
|
-
Learn how
|
|
41
|
+
Learn how attributes, values, and axes fit together.
|
|
37
42
|
|
|
38
43
|
<CardGroup cols={2}>
|
|
39
44
|
<Card title="Attribute types" icon="shapes" href="/platform/attribute/concepts/attribute-types">
|
|
40
|
-
The five
|
|
45
|
+
The five types and how each one stores its values.
|
|
41
46
|
</Card>
|
|
42
47
|
<Card title="Variant axes" icon="sitemap" href="/platform/attribute/concepts/variant-axes">
|
|
43
|
-
How
|
|
48
|
+
How an axis attribute mirrors a Medusa `ProductOption`.
|
|
44
49
|
</Card>
|
|
45
50
|
<Card title="Global vs inline" icon="layer-group" href="/platform/attribute/concepts/global-vs-inline">
|
|
46
|
-
Catalog
|
|
51
|
+
Catalog attributes, product-scoped attributes, categories, and filters.
|
|
47
52
|
</Card>
|
|
48
53
|
</CardGroup>
|
|
49
54
|
|
|
50
55
|
## Examples
|
|
51
56
|
|
|
52
|
-
Build against
|
|
57
|
+
Build against attributes in your own server code.
|
|
53
58
|
|
|
54
59
|
<CardGroup cols={2}>
|
|
55
60
|
<Card title="Create an attribute" icon="plus" href="/platform/attribute/guides/create-an-attribute">
|
|
56
61
|
Run `createProductAttributesWorkflow` from a route or seed script.
|
|
57
62
|
</Card>
|
|
58
63
|
<Card title="Attach to a product" icon="link" href="/platform/attribute/guides/attach-attributes-to-a-product">
|
|
59
|
-
Attach, detach, and update attributes in one
|
|
64
|
+
Attach, detach, and update a product’s attributes in one run.
|
|
60
65
|
</Card>
|
|
61
66
|
<Card title="Create a variant axis" icon="sitemap" href="/platform/attribute/guides/create-a-variant-axis">
|
|
62
|
-
|
|
67
|
+
Create an axis and build a product’s variants on it.
|
|
63
68
|
</Card>
|
|
64
69
|
</CardGroup>
|
|
65
70
|
|
|
66
71
|
## Resources
|
|
67
72
|
|
|
68
|
-
|
|
69
|
-
domain.
|
|
73
|
+
The data models, links, workflows, service methods, and events behind attributes.
|
|
70
74
|
|
|
71
75
|
<CardGroup cols={2}>
|
|
72
76
|
<Card title="Data models" icon="table" href="/platform/attribute/reference/data-models">
|
|
73
|
-
|
|
77
|
+
`ProductAttribute` and `ProductAttributeValue`, field by field.
|
|
74
78
|
</Card>
|
|
75
79
|
<Card title="Links" icon="link" href="/platform/attribute/reference/links">
|
|
76
|
-
|
|
80
|
+
Links to products, categories, and product options.
|
|
77
81
|
</Card>
|
|
78
82
|
<Card title="Workflows" icon="diagram-project" href="/platform/attribute/reference/workflows">
|
|
79
|
-
Catalog and product
|
|
83
|
+
Catalog workflows and product attachment workflows.
|
|
80
84
|
</Card>
|
|
81
85
|
<Card title="Service" icon="gear" href="/platform/attribute/reference/service">
|
|
82
86
|
Module service methods for working with records directly.
|
|
@@ -1,56 +1,71 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Attribute data models"
|
|
3
3
|
sidebarTitle: "Data models"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Every field on ProductAttribute and ProductAttributeValue, with defaults, nullability, indexes, and relations."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
The Attribute
|
|
8
|
-
lists its data models and their fields. For the full module overview, see the
|
|
9
|
-
[Attribute module overview](/platform/attribute/overview).
|
|
7
|
+
The Product Attribute module owns 2 data models: `ProductAttribute` and `ProductAttributeValue`. Both are defined in `packages/core/src/modules/product-attribute/models` and resolved through `MercurModules.PRODUCT_ATTRIBUTE` (`"product_attribute"`). Like every Medusa data model, each also gets `created_at`, `updated_at`, and `deleted_at` timestamps.
|
|
10
8
|
|
|
11
9
|
## ProductAttribute
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
A typed attribute in the shared catalog, either global or scoped to one product. Id prefix `pattr`.
|
|
14
12
|
|
|
15
|
-
| Field | Type |
|
|
13
|
+
| Field | Type | Description |
|
|
16
14
|
| --- | --- | --- |
|
|
17
|
-
| `id` |
|
|
18
|
-
| `
|
|
19
|
-
| `
|
|
20
|
-
| `description` |
|
|
21
|
-
| `type` |
|
|
22
|
-
| `is_required` | boolean | Default `false
|
|
23
|
-
| `is_filterable` | boolean | Default `false
|
|
24
|
-
| `is_variant_axis` | boolean | Default `false
|
|
25
|
-
| `rank` | number | Default `0
|
|
26
|
-
| `is_active` | boolean | Default `true` |
|
|
27
|
-
| `created_by` |
|
|
28
|
-
| `product_id` |
|
|
29
|
-
| `product_option_id` |
|
|
30
|
-
| `metadata` |
|
|
31
|
-
|
|
32
|
-
Relations
|
|
33
|
-
|
|
15
|
+
| `id` | `string` | Primary key, prefixed `pattr` |
|
|
16
|
+
| `handle` | `string \| null` | Unique among non-deleted attributes when set. Derived from `name` on create for global attributes |
|
|
17
|
+
| `name` | `string` | Display name. Searchable. For an axis, also the title of its `ProductOption` |
|
|
18
|
+
| `description` | `string \| null` | Free-form description |
|
|
19
|
+
| `type` | `AttributeType` | One of `single_select`, `multi_select`, `unit`, `toggle`, `text`. Can’t change after create |
|
|
20
|
+
| `is_required` | `boolean` | Default `false`. The remove workflows refuse to detach a required attribute |
|
|
21
|
+
| `is_filterable` | `boolean` | Default `false`. Marks the attribute as a storefront facet |
|
|
22
|
+
| `is_variant_axis` | `boolean` | Default `false`. With `multi_select`, backs the attribute with a `ProductOption` |
|
|
23
|
+
| `rank` | `number` | Default `0`. Sort order |
|
|
24
|
+
| `is_active` | `boolean` | Default `true` |
|
|
25
|
+
| `created_by` | `string \| null` | Id of whoever created the attribute. The built-in routes leave it unset |
|
|
26
|
+
| `product_id` | `string \| null` | `null` for a global attribute, the product’s id for an inline one |
|
|
27
|
+
| `product_option_id` | `string \| null` | Id of the mirror `ProductOption` for an axis attribute |
|
|
28
|
+
| `metadata` | `Record<string, unknown> \| null` | Custom key-value data |
|
|
29
|
+
|
|
30
|
+
Relations and constraints:
|
|
31
|
+
|
|
32
|
+
- **`values`**: has many `ProductAttributeValue`, deleted with the attribute
|
|
33
|
+
- **`IDX_product_attribute_handle_unique`**: unique `handle` where `deleted_at IS NULL AND handle IS NOT NULL`
|
|
34
|
+
- **`IDX_product_attribute_type`**: index on `type`
|
|
35
|
+
- **`IDX_product_attribute_product_id`**: index on `product_id` where it’s not null
|
|
34
36
|
|
|
35
37
|
## ProductAttributeValue
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
belongs to one attribute.
|
|
39
|
+
One selectable value of an attribute, or the stored value of a `text`, `unit`, or `toggle` attribute on a product. Id prefix `pattrval`.
|
|
39
40
|
|
|
40
|
-
| Field | Type |
|
|
41
|
+
| Field | Type | Description |
|
|
41
42
|
| --- | --- | --- |
|
|
42
|
-
| `id` |
|
|
43
|
-
| `
|
|
44
|
-
| `handle` |
|
|
45
|
-
| `
|
|
46
|
-
| `
|
|
47
|
-
| `
|
|
48
|
-
| `
|
|
43
|
+
| `id` | `string` | Primary key, prefixed `pattrval` |
|
|
44
|
+
| `attribute_id` | `string` | Id of the parent `ProductAttribute` |
|
|
45
|
+
| `handle` | `string \| null` | Unique per attribute when set. Derived from `name` on create for values of global select attributes |
|
|
46
|
+
| `name` | `string` | The value label, or the stored content for `text` and `unit` |
|
|
47
|
+
| `rank` | `number` | Default `0`. Sort order within the attribute |
|
|
48
|
+
| `is_active` | `boolean` | Default `true` |
|
|
49
|
+
| `product_option_value_id` | `string \| null` | Id of the mirror `ProductOptionValue` for an axis value |
|
|
50
|
+
| `metadata` | `Record<string, unknown> \| null` | Custom key-value data |
|
|
51
|
+
|
|
52
|
+
Relations and constraints:
|
|
53
|
+
|
|
54
|
+
- **`attribute`**: belongs to `ProductAttribute` through `attribute_id`
|
|
55
|
+
- **`IDX_product_attribute_value_handle_unique`**: unique `(attribute_id, handle)` where `deleted_at IS NULL AND handle IS NOT NULL`
|
|
56
|
+
|
|
57
|
+
## AttributeType enum
|
|
58
|
+
|
|
59
|
+
`AttributeType` is exported from `@mercurjs/types`:
|
|
49
60
|
|
|
50
|
-
|
|
61
|
+
| Member | Value |
|
|
62
|
+
| --- | --- |
|
|
63
|
+
| `SINGLE_SELECT` | `"single_select"` |
|
|
64
|
+
| `MULTI_SELECT` | `"multi_select"` |
|
|
65
|
+
| `UNIT` | `"unit"` |
|
|
66
|
+
| `TOGGLE` | `"toggle"` |
|
|
67
|
+
| `TEXT` | `"text"` |
|
|
51
68
|
|
|
52
69
|
<Note>
|
|
53
|
-
|
|
54
|
-
are the foreign keys behind the read-only mirror links to Medusa's product
|
|
55
|
-
options. See [Links](/platform/attribute/reference/links).
|
|
70
|
+
Neither model stores which products hold a value. That lives in the `product_attribute_value_link` pivot table, described in [Links](/platform/attribute/reference/links).
|
|
56
71
|
</Note>
|