@spree/docs 0.1.177 → 0.1.178
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/dist/developer/cli/quickstart.md +1 -1
- package/dist/developer/contributing/creating-an-extension.md +2 -2
- package/dist/developer/core-concepts/addresses.md +3 -3
- package/dist/developer/core-concepts/architecture.md +4 -4
- package/dist/developer/core-concepts/calculators.md +2 -2
- package/dist/developer/core-concepts/carts.md +207 -0
- package/dist/developer/core-concepts/channels.md +3 -3
- package/dist/developer/core-concepts/customers.md +1 -1
- package/dist/developer/core-concepts/events.md +1 -1
- package/dist/developer/core-concepts/fulfillments.md +278 -0
- package/dist/developer/core-concepts/imports-exports.md +6 -18
- package/dist/developer/core-concepts/inventory.md +3 -3
- package/dist/developer/core-concepts/media.md +1 -1
- package/dist/developer/core-concepts/metafields.md +1 -1
- package/dist/developer/core-concepts/orders.md +92 -339
- package/dist/developer/core-concepts/payments.md +1 -1
- package/dist/developer/core-concepts/promotions.md +68 -195
- package/dist/developer/core-concepts/returns-exchanges-claims.md +173 -0
- package/dist/developer/core-concepts/store-credits-gift-cards.md +16 -18
- package/dist/developer/core-concepts/stores.md +1 -1
- package/dist/developer/core-concepts/taxes-discounts-fees.md +199 -0
- package/dist/developer/core-concepts/taxes.md +3 -3
- package/dist/developer/create-spree-app/quickstart.md +1 -1
- package/dist/developer/customization/api.md +36 -11
- package/dist/developer/customization/configuration.md +207 -37
- package/dist/developer/customization/decorators.md +8 -8
- package/dist/developer/customization/permissions.md +34 -229
- package/dist/developer/customization/quickstart.md +159 -108
- package/dist/developer/customization/validations.md +1 -1
- package/dist/developer/dashboard/overview.md +1 -1
- package/dist/developer/how-to/build-a-b2b-store.md +21 -0
- package/dist/developer/how-to/build-a-marketplace.md +25 -0
- package/dist/developer/how-to/custom-api-authentication.md +1 -1
- package/dist/developer/how-to/custom-delivery-rate-provider.md +242 -0
- package/dist/developer/how-to/custom-document-numbers.md +154 -0
- package/dist/developer/how-to/custom-order-routing.md +1 -1
- package/dist/developer/how-to/custom-payment-method.md +1 -1
- package/dist/developer/how-to/custom-promotion.md +76 -115
- package/dist/developer/how-to/custom-report.md +2 -2
- package/dist/developer/how-to/custom-stock-splitter.md +5 -5
- package/dist/developer/how-to/sell-digital-products.md +20 -0
- package/dist/developer/multi-tenant/quickstart.md +1 -1
- package/dist/developer/providers/dam.md +14 -0
- package/dist/developer/providers/erp.md +31 -0
- package/dist/developer/providers/fulfillment.md +25 -0
- package/dist/developer/{deployment/telemetry.md → providers/observability.md} +1 -1
- package/dist/developer/providers/overview.md +44 -0
- package/dist/developer/providers/pim.md +25 -0
- package/dist/developer/providers/sso.md +20 -0
- package/dist/developer/sdk/admin/extending.md +1 -1
- package/dist/developer/sdk/admin/quickstart.md +1 -1
- package/dist/developer/sdk/admin/resources.md +1 -1
- package/dist/developer/sdk/extending.md +1 -1
- package/dist/developer/tutorial/admin-api.md +13 -0
- package/dist/developer/tutorial/dashboard-plugin.md +14 -0
- package/dist/developer/tutorial/events.md +7 -161
- package/dist/developer/tutorial/introduction.md +15 -35
- package/dist/developer/tutorial/model.md +7 -98
- package/dist/developer/tutorial/store-api.md +13 -0
- package/dist/developer/tutorial/storefront.md +12 -0
- package/dist/developer/tutorial/testing.md +7 -711
- package/dist/developer/upgrades/5.6-to-6.0.md +2 -2
- package/package.json +1 -1
- package/dist/developer/admin/admin.md +0 -214
- package/dist/developer/admin/authentication.md +0 -59
- package/dist/developer/admin/components.md +0 -711
- package/dist/developer/admin/custom-css.md +0 -256
- package/dist/developer/admin/custom-javascript.md +0 -116
- package/dist/developer/admin/extending-ui.md +0 -1839
- package/dist/developer/admin/form-builder.md +0 -444
- package/dist/developer/admin/helper-methods.md +0 -531
- package/dist/developer/admin/navigation.md +0 -805
- package/dist/developer/admin/tables.md +0 -490
- package/dist/developer/advanced/adding_spree_to_rails_app.md +0 -92
- package/dist/developer/core-concepts/adjustments.md +0 -113
- package/dist/developer/core-concepts/reports.md +0 -208
- package/dist/developer/core-concepts/shipments.md +0 -307
- package/dist/developer/core-concepts/users.md +0 -303
- package/dist/developer/customization/authentication.md +0 -100
- package/dist/developer/customization/checkout.md +0 -202
- package/dist/developer/customization/emails.md +0 -18
- package/dist/developer/customization/routes.md +0 -24
- package/dist/developer/multi-vendor/installation.md +0 -61
- package/dist/developer/multi-vendor/quickstart.md +0 -17
- package/dist/developer/tutorial/admin.md +0 -206
- package/dist/developer/tutorial/api.md +0 -606
- package/dist/developer/tutorial/extending-models.md +0 -393
- package/dist/developer/tutorial/sdk.md +0 -170
- package/dist/developer/upgrades/2.0-to-2.1.md +0 -46
- package/dist/developer/upgrades/2.1-to-2.2.md +0 -59
- package/dist/developer/upgrades/2.2-to-2.3.md +0 -44
- package/dist/developer/upgrades/2.3-to-2.4.md +0 -42
- package/dist/developer/upgrades/3.0-to-3.1.md +0 -47
- package/dist/developer/upgrades/3.1-to-3.2.md +0 -34
- package/dist/developer/upgrades/3.2-to-3.3.md +0 -70
- package/dist/developer/upgrades/3.3-to-3.4.md +0 -36
- package/dist/developer/upgrades/3.4-to-3.5.md +0 -44
- package/dist/developer/upgrades/3.5-to-3.6.md +0 -40
- package/dist/developer/upgrades/3.6-to-3.7.md +0 -62
- package/dist/developer/upgrades/3.7-to-4.0.md +0 -152
- package/dist/developer/upgrades/4.0-to-4.1.md +0 -92
- package/dist/developer/upgrades/4.1-to-4.2.md +0 -109
- package/dist/developer/upgrades/4.10-to-5.0.md +0 -131
- package/dist/developer/upgrades/4.2-to-4.3.md +0 -100
- package/dist/developer/upgrades/4.3-to-4.4.md +0 -125
- package/dist/developer/upgrades/4.4-to-4.5.md +0 -94
- package/dist/developer/upgrades/4.5-to-4.6.md +0 -119
- package/dist/developer/upgrades/4.6-to-4.7.md +0 -39
- package/dist/developer/upgrades/4.8-to-4.9.md +0 -24
- package/dist/developer/upgrades/4.9-to-4.10.md +0 -24
- package/dist/developer/upgrades/4.x-to-4.8.md +0 -52
- package/dist/developer/upgrades/5.0-to-5.1.md +0 -28
- package/dist/developer/upgrades/5.1-to-5.2.md +0 -131
- package/dist/developer/upgrades/5.2-to-5.3.md +0 -338
- package/dist/developer/upgrades/5.3-to-5.4.md +0 -277
- package/dist/developer/upgrades/5.4-to-5.5.md +0 -301
- package/dist/developer/upgrades/5.5-to-5.6.md +0 -207
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Dashboard Plugin
|
|
3
|
+
description: Scaffold a dashboard plugin for ProductFaqs — a table, a form, and navigation, generated by the CLI and wired to the Admin API endpoint.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
With the Admin API in place, the back-office UI is a dashboard plugin: a table listing FAQs, a form to create and edit them, and a navigation entry — scaffolded by the CLI and customized from there.
|
|
7
|
+
|
|
8
|
+
## What this chapter covers
|
|
9
|
+
|
|
10
|
+
- Scaffolding the plugin with the CLI
|
|
11
|
+
- The resource table: columns, search and pagination against the Admin API
|
|
12
|
+
- The form: fields, validation errors mapped from API responses
|
|
13
|
+
- Registering navigation and permissions-aware visibility
|
|
14
|
+
- Running it against your development store
|
|
@@ -1,166 +1,12 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Events
|
|
3
|
-
description:
|
|
2
|
+
title: Events
|
|
3
|
+
description: Publish lifecycle events for FAQs and react to them — subscribers inside the app, webhooks for systems outside it.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Features rarely live alone — something usually needs to know when data changes. This chapter publishes lifecycle events for FAQs and consumes them both ways.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
## What this chapter covers
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|---|---|---|
|
|
14
|
-
| **Subscriber** | In your Spree app | Calling external APIs with your own client code, internal side effects, anything needing app context |
|
|
15
|
-
| **Webhook** | In the external system | Letting a third party receive HTTP callbacks — no Ruby in your app, endpoints managed from the admin |
|
|
16
|
-
|
|
17
|
-
We'll do both: push completed orders to an OMS with a subscriber, give the Brand model its own lifecycle events, and set up an outbound webhook.
|
|
18
|
-
|
|
19
|
-
## Step 1: Subscribe to a Core Event
|
|
20
|
-
|
|
21
|
-
When an order completes, send it to the OMS. Generate a subscriber:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
```bash Spree CLI (Docker)
|
|
25
|
-
spree generate subscriber OmsOrderSync order.completed
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
```bash Without Spree CLI
|
|
29
|
-
bin/rails g spree:subscriber OmsOrderSync order.completed
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
This creates the subscriber, a spec stub, and — crucially — **registers it** in `config/initializers/spree.rb` (injected into the existing `after_initialize` block every Spree app ships with). Subscribers are not auto-discovered; a subscriber that never gets appended to `Spree.subscribers` is a silent no-op, which is why the generator owns that step (re-runs are idempotent, and each new subscriber appends to the same initializer).
|
|
34
|
-
|
|
35
|
-
Fill in the handler:
|
|
36
|
-
|
|
37
|
-
```ruby app/subscribers/oms_order_sync_subscriber.rb
|
|
38
|
-
class OmsOrderSyncSubscriber < Spree::Subscriber
|
|
39
|
-
subscribes_to 'order.completed'
|
|
40
|
-
|
|
41
|
-
def handle(event)
|
|
42
|
-
order = Spree::Order.find_by_prefix_id(event.payload['id'])
|
|
43
|
-
return unless order
|
|
44
|
-
|
|
45
|
-
OmsClient.create_order(
|
|
46
|
-
number: order.number,
|
|
47
|
-
email: order.email,
|
|
48
|
-
line_items: order.line_items.map { |li| { sku: li.sku, quantity: li.quantity } }
|
|
49
|
-
)
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
Two things worth understanding:
|
|
55
|
-
|
|
56
|
-
- **Payloads carry prefixed IDs**, not raw database IDs — always look records up with `find_by_prefix_id`. The payload itself is the resource serialized with its v3 API serializer, so `event.payload['number']`, `['email']`, etc. are available directly when you don't need the full record.
|
|
57
|
-
- **Subscribers run async by default** — each `handle` call is an ActiveJob on the events queue, so a slow OMS API never blocks checkout. Pass `subscribes_to 'order.completed', async: false` only when you genuinely need synchronous execution.
|
|
58
|
-
|
|
59
|
-
Restart the server, complete a test order, and watch the job fire (`spree logs worker` — or your job backend's UI).
|
|
60
|
-
|
|
61
|
-
## Step 2: Give Brand Its Own Lifecycle Events
|
|
62
|
-
|
|
63
|
-
Core models like Payment and Shipment publish `*.created` / `*.updated` / `*.deleted` events automatically. Your models can too — add one line to the Brand model:
|
|
64
|
-
|
|
65
|
-
```ruby app/models/spree/brand.rb {3}
|
|
66
|
-
module Spree
|
|
67
|
-
class Brand < Spree.base_class
|
|
68
|
-
publishes_lifecycle_events
|
|
69
|
-
|
|
70
|
-
# ... existing code ...
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
Now `brand.created`, `brand.updated`, and `brand.deleted` fire after the matching transactions commit — and because you created `Spree::Api::V3::BrandSerializer` in the [API step](api.md), the payloads automatically use it (the events system resolves the serializer by naming convention). Anyone — subscriber or webhook — can react to brand changes with the same JSON shape your API serves.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
```bash Spree CLI (Docker)
|
|
79
|
-
spree generate subscriber BrandSync brand.created brand.updated
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
```bash Without Spree CLI
|
|
83
|
-
bin/rails g spree:subscriber BrandSync brand.created brand.updated
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
```ruby app/subscribers/brand_sync_subscriber.rb
|
|
88
|
-
class BrandSyncSubscriber < Spree::Subscriber
|
|
89
|
-
subscribes_to 'brand.created', 'brand.updated'
|
|
90
|
-
|
|
91
|
-
def handle(event)
|
|
92
|
-
SearchIndexer.upsert_brand(event.payload)
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
## Step 3: Publish a Custom Event
|
|
98
|
-
|
|
99
|
-
Lifecycle events cover persistence; custom events express *domain* moments. Say featuring a brand should notify the marketing platform:
|
|
100
|
-
|
|
101
|
-
```ruby
|
|
102
|
-
brand.publish_event('brand.featured')
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
The payload defaults to the serializer output; pass your own hash as the second argument when the event needs different data. Subscribers consume it like any other event name.
|
|
106
|
-
|
|
107
|
-
## Step 4: Outbound Webhooks — No Code Required
|
|
108
|
-
|
|
109
|
-
When the consumer is an external system you don't deploy code into, use webhooks. In the admin, go to **Settings → Webhooks**, add an endpoint with the destination URL, and pick the events to deliver — `order.completed`, `brand.created`, anything publishing in your store. The endpoint's **signing secret is shown once on creation** — store it in the receiving system.
|
|
110
|
-
|
|
111
|
-
Each delivery is an HTTP POST with this envelope:
|
|
112
|
-
|
|
113
|
-
```json
|
|
114
|
-
{
|
|
115
|
-
"id": "550e8400-e29b-41d4-a716-446655440000",
|
|
116
|
-
"name": "order.completed",
|
|
117
|
-
"created_at": "2026-06-11T12:00:00Z",
|
|
118
|
-
"data": { "id": "or_m3Rp9wXz", "number": "R123456789", "...": "..." },
|
|
119
|
-
"metadata": {}
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
And three headers the receiver should use:
|
|
124
|
-
|
|
125
|
-
| Header | Contents |
|
|
126
|
-
|---|---|
|
|
127
|
-
| `X-Spree-Webhook-Event` | The event name |
|
|
128
|
-
| `X-Spree-Webhook-Timestamp` | Unix timestamp of the delivery |
|
|
129
|
-
| `X-Spree-Webhook-Signature` | `HMAC-SHA256(secret, "{timestamp}.{body}")` |
|
|
130
|
-
|
|
131
|
-
Verify the signature before trusting a payload:
|
|
132
|
-
|
|
133
|
-
```ruby
|
|
134
|
-
def verified?(request, secret)
|
|
135
|
-
timestamp = request.headers['X-Spree-Webhook-Timestamp']
|
|
136
|
-
expected = OpenSSL::HMAC.hexdigest('SHA256', secret, "#{timestamp}.#{request.raw_post}")
|
|
137
|
-
ActiveSupport::SecurityUtils.secure_compare(expected, request.headers['X-Spree-Webhook-Signature'])
|
|
138
|
-
end
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
Delivery semantics to design around: failed deliveries (timeouts, connection errors, non-2xx responses) are **recorded, not retried automatically** — redeliver from the endpoint's delivery history in the admin, or via `POST /api/v3/admin/webhook_endpoints/:webhook_endpoint_id/deliveries/:id/redeliver`. After **15 consecutive failures** the endpoint auto-disables and store staff get an email; a successful delivery resets the counter.
|
|
142
|
-
|
|
143
|
-
## Testing a Subscriber
|
|
144
|
-
|
|
145
|
-
Subscribers are plain Ruby — test `handle` directly with a constructed event:
|
|
146
|
-
|
|
147
|
-
```ruby spec/subscribers/oms_order_sync_subscriber_spec.rb
|
|
148
|
-
require 'rails_helper'
|
|
149
|
-
|
|
150
|
-
RSpec.describe OmsOrderSyncSubscriber do
|
|
151
|
-
it 'pushes completed orders to the OMS' do
|
|
152
|
-
order = create(:completed_order_with_totals)
|
|
153
|
-
event = Spree::Event.new(name: 'order.completed', payload: { 'id' => order.prefixed_id })
|
|
154
|
-
|
|
155
|
-
expect(OmsClient).to receive(:create_order).with(hash_including(number: order.number))
|
|
156
|
-
|
|
157
|
-
described_class.new.handle(event)
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
## Related Documentation
|
|
163
|
-
|
|
164
|
-
- [Events](../core-concepts/events.md) — the full event catalog, subscriber DSL, and delivery guarantees
|
|
165
|
-
- [Webhooks](../core-concepts/webhooks.md) — endpoint management, security, and the per-event payload schemas
|
|
166
|
-
- [Extending Core Models](extending-models.md) — when a decorator is the right tool instead
|
|
10
|
+
- Publishing lifecycle events from the model
|
|
11
|
+
- A subscriber reacting inside the application
|
|
12
|
+
- Delivering the event to an external system as a webhook
|
|
@@ -1,44 +1,24 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Tutorial
|
|
3
|
-
|
|
4
|
-
description: Build a complete custom Brands feature in Spree — model, admin UI, Store and Admin APIs, events, and TypeScript storefront integration — end to end.
|
|
2
|
+
title: "Tutorial: Build a Custom Feature"
|
|
3
|
+
description: Build Product FAQs end to end — a custom model, an Admin API endpoint, a dashboard plugin, a Store API endpoint, and storefront rendering through the TypeScript SDK.
|
|
5
4
|
---
|
|
6
5
|
|
|
7
|
-
This tutorial
|
|
6
|
+
This tutorial builds one complete feature — **Product FAQs**, questions and answers attached to products — through every layer of a Spree 6 application. By the end you will have a custom model, back-office management in the dashboard, and customer-facing display on the storefront.
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
## The two tracks
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
You need a running Spree app. The fastest path is [create-spree-app](../create-spree-app/quickstart.md) — a Docker-based project with the [Spree CLI](../cli/quickstart.md), no local Ruby required:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npx create-spree-app@latest my-store
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
An existing app with Spree gems works just as well. **Every command in this tutorial is shown in two forms** — **Spree CLI** (runs inside Docker) and **Without Spree CLI** (runs directly on your machine) — pick the tab that matches your setup and ignore the other.
|
|
10
|
+
The tutorial is organized so you only build what your feature needs:
|
|
20
11
|
|
|
21
|
-
|
|
12
|
+
| Track | Chapters | Build this when |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| Foundation | [Model](model.md) | Always |
|
|
15
|
+
| **A — manage it in the dashboard** | [Admin API](admin-api.md) → [Dashboard plugin](dashboard-plugin.md) | The feature is operated by staff — internal tooling, back-office data |
|
|
16
|
+
| **B — expose it on the storefront** | [Store API](store-api.md) → [Storefront](storefront.md) | Customers see the feature |
|
|
17
|
+
| Finishing | [Events](events.md) → [Testing](testing.md) | Always |
|
|
22
18
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
## What you'll build
|
|
26
|
-
|
|
27
|
-
* A `Brand` model with a rich text description and an uploadable logo — one generator command
|
|
28
|
-
* An admin UI for managing brands — one generator command, then customized
|
|
29
|
-
* A `brand` association on Products, surfaced in the product admin form
|
|
30
|
-
* Store API endpoints (`GET /api/v3/store/brands`) with serializers and prefixed IDs
|
|
31
|
-
* Typed TypeScript access to brands from your storefront via `@spree/sdk`
|
|
32
|
-
* Event subscribers and webhooks connecting the store to external systems
|
|
33
|
-
* Automated tests for all of it
|
|
34
|
-
|
|
35
|
-
## Tutorial Sections
|
|
19
|
+
An internal tool is Track A alone. A storefront feature over existing data is Track B alone. Product FAQs need both: staff write the answers, customers read them.
|
|
36
20
|
|
|
21
|
+
## Before you start
|
|
37
22
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
- [3. Extending Core Models](extending-models.md) — Connect Brands to Products using a decorator and an association
|
|
41
|
-
- [4. API](api.md) — Expose Brands through the Store and Admin APIs — generated in one command, or hand-built to learn the pieces
|
|
42
|
-
- [5. Events & Webhooks](events.md) — React to store activity and connect external systems — OMS, warehouse, ERP
|
|
43
|
-
- [6. SDK](sdk.md) — Consume Brand endpoints from TypeScript using the SDK
|
|
44
|
-
- [7. Testing](testing.md) — Write automated tests for your feature
|
|
23
|
+
- A running Spree application — see the [quickstart](../getting-started/quickstart.md)
|
|
24
|
+
- The AI tooling set up — the [agent skills and MCP server](../agentic/overview.md) make every step of this tutorial faster
|
|
@@ -1,104 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: Model
|
|
3
|
-
description:
|
|
3
|
+
description: Generate the ProductFaq model — migration, associations, validations and store scoping — the foundation both tracks build on.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
Every feature starts with a model. This chapter generates `ProductFaq` — question, answer, position, attached to a product and scoped to the store.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
## What this chapter covers
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
```bash Spree CLI (Docker)
|
|
16
|
-
spree generate model Brand name:string:index description:rich_text logo:attachment
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
```bash Without Spree CLI
|
|
20
|
-
bin/rails g spree:model Brand name:string:index description:rich_text logo:attachment
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
> **INFO:** Throughout this tutorial every command is shown in two forms. **Spree CLI** is for projects created with [create-spree-app](../create-spree-app/quickstart.md), where the app runs in Docker and `spree <command>` routes into the container. **Without Spree CLI** is for apps running Spree backend directly on your machine.
|
|
25
|
-
|
|
26
|
-
This creates two files:
|
|
27
|
-
|
|
28
|
-
- `app/models/spree/brand.rb` — the model
|
|
29
|
-
- `db/migrate/XXXXXXXXXXXXXX_create_spree_brands.rb` — the migration
|
|
30
|
-
|
|
31
|
-
Now apply the migration:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
```bash Spree CLI (Docker)
|
|
35
|
-
spree migrate
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
```bash Without Spree CLI
|
|
39
|
-
bin/rails db:migrate
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
This creates the `spree_brands` table with an indexed `name` column. The `description` and `logo` attributes don't add columns — rich text lives in Action Text's `action_text_rich_texts` table, and uploads live in Active Storage's tables.
|
|
44
|
-
|
|
45
|
-
## Step 2: Understand the Generated Model
|
|
46
|
-
|
|
47
|
-
Open `app/models/spree/brand.rb`:
|
|
48
|
-
|
|
49
|
-
```ruby app/models/spree/brand.rb
|
|
50
|
-
module Spree
|
|
51
|
-
class Brand < Spree.base_class
|
|
52
|
-
has_prefix_id :brand
|
|
53
|
-
|
|
54
|
-
has_rich_text :description
|
|
55
|
-
has_one_attached :logo
|
|
56
|
-
|
|
57
|
-
validates :name, presence: true
|
|
58
|
-
|
|
59
|
-
self.whitelisted_ransackable_attributes = %w[name]
|
|
60
|
-
self.whitelisted_ransackable_associations = %w[]
|
|
61
|
-
self.whitelisted_ransackable_scopes = %w[]
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
Line by line:
|
|
67
|
-
|
|
68
|
-
- **`Spree.base_class`** — inherits all Spree model functionality (multi-store scoping helpers, preferences, and more). The class is namespaced under `Spree::`, so it's available as `Spree::Brand`.
|
|
69
|
-
- **`has_prefix_id :brand`** — records get Stripe-style public IDs like `brand_k5nR8xLq`. APIs never expose raw database IDs.
|
|
70
|
-
- **`has_rich_text :description`** — formatted content via [Action Text](https://guides.rubyonrails.org/action_text_overview.html).
|
|
71
|
-
- **`has_one_attached :logo`** — file uploads via [Active Storage](https://guides.rubyonrails.org/active_storage_overview.html), with image processing and direct-to-storage uploads.
|
|
72
|
-
- **`validates :name, presence: true`** — generated automatically for required columns.
|
|
73
|
-
- **`whitelisted_ransackable_attributes`** — controls which attributes API clients may filter and sort by. Only allowlisted attributes are queryable, so adding `name` here is what later makes `?q[name_cont]=nike` work.
|
|
74
|
-
|
|
75
|
-
## Step 3: Try It in the Console
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
```bash Spree CLI (Docker)
|
|
79
|
-
spree console
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
```bash Without Spree CLI
|
|
83
|
-
bin/rails console
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
```ruby
|
|
88
|
-
brand = Spree::Brand.create!(name: "Wilson")
|
|
89
|
-
brand.prefixed_id # => "brand_k5nR8xLq"
|
|
90
|
-
|
|
91
|
-
brand.update!(description: "<h1>Hello</h1><p>World</p>")
|
|
92
|
-
brand.description.to_s # => rendered rich text HTML
|
|
93
|
-
brand.description.to_plain_text # => "Hello World"
|
|
94
|
-
|
|
95
|
-
Spree::Brand.find_by_prefix_id!("brand_k5nR8xLq") # lookup by public ID
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
We'll upload the logo through the admin UI in the next step — the model side is already done.
|
|
99
|
-
|
|
100
|
-
> **TIP:** The generator accepts more attribute types and options — references with auto-resolved class names (`user:belongs_to`), unique indexes (`slug:string:uniq`), soft delete (`--paranoid`), and custom fields support (`--metafields`). Run it with `--help` to see everything.
|
|
101
|
-
|
|
102
|
-
## Next Step
|
|
103
|
-
|
|
104
|
-
The model is complete. Now let's give admins a UI to manage brands: [Admin Dashboard](admin.md).
|
|
10
|
+
- Generating the model and migration from the command line
|
|
11
|
+
- Store scoping — why every store-owned record belongs to a store, and what that buys you
|
|
12
|
+
- The association to products, validations, and ordering
|
|
13
|
+
- Running the migration and creating a record in the console
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Store API
|
|
3
|
+
description: Expose FAQs to customers — a read-only Store API endpoint with a serializer that shows only what shoppers should see.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Customers read FAQs through the Store API. This chapter adds the customer-facing endpoint — read-only, scoped to the current store, with a serializer deliberately smaller than the admin one.
|
|
7
|
+
|
|
8
|
+
## What this chapter covers
|
|
9
|
+
|
|
10
|
+
- The store resource controller: read-only by default, and why
|
|
11
|
+
- The store serializer: public fields only — no timestamps, no internal data
|
|
12
|
+
- Scoping: only FAQs for purchasable products of the current store
|
|
13
|
+
- Nesting under the product endpoint
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Storefront
|
|
3
|
+
description: Render FAQs on the product page — regenerate the SDK types, extend the TypeScript client, and build the component in the Next.js storefront.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
The Store API endpoint becomes typed TypeScript and a component on the product page. This chapter closes the loop from Rails model to customer screen.
|
|
7
|
+
|
|
8
|
+
## What this chapter covers
|
|
9
|
+
|
|
10
|
+
- The type generation pipeline: serializer to TypeScript types to runtime schemas
|
|
11
|
+
- Extending the SDK client with the new resource
|
|
12
|
+
- Fetching and rendering FAQs on the product detail page in the Next.js storefront
|