@spree/docs 0.1.0
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/README.md +54 -0
- package/dist/api-reference/platform/authentication.md +38 -0
- package/dist/api-reference/store-api/authentication.md +188 -0
- package/dist/api-reference/store-api/errors.md +277 -0
- package/dist/api-reference/store-api/idempotency.md +129 -0
- package/dist/api-reference/store-api/introduction.md +34 -0
- package/dist/api-reference/store-api/localization.md +279 -0
- package/dist/api-reference/store-api/metadata.md +160 -0
- package/dist/api-reference/store-api/monetary-amounts.md +65 -0
- package/dist/api-reference/store-api/querying.md +399 -0
- package/dist/api-reference/store-api/rate-limitting.md +103 -0
- package/dist/api-reference/store-api/relations.md +185 -0
- package/dist/api-reference/storefront/authentication.md +88 -0
- package/dist/api-reference/tutorials/adyen-integration-guide-for-android.md +165 -0
- package/dist/api-reference/tutorials/adyen-integration-guide-for-ios.md +194 -0
- package/dist/api-reference/tutorials/quick-checkout-with-stripe.md +248 -0
- package/dist/api-reference/v2/fetching-multiple-resources.md +26 -0
- package/dist/api-reference/v2/filtering-and-sorting.md +53 -0
- package/dist/api-reference/v2/introduction.md +22 -0
- package/dist/api-reference/v2/pagination.md +37 -0
- package/dist/api-reference/webhooks-events.md +883 -0
- package/dist/developer/admin/admin.md +205 -0
- package/dist/developer/admin/authentication.md +59 -0
- package/dist/developer/admin/components.md +711 -0
- package/dist/developer/admin/custom-css.md +243 -0
- package/dist/developer/admin/custom-javascript.md +116 -0
- package/dist/developer/admin/extending-ui.md +1964 -0
- package/dist/developer/admin/form-builder.md +444 -0
- package/dist/developer/admin/helper-methods.md +531 -0
- package/dist/developer/admin/navigation.md +805 -0
- package/dist/developer/admin/tables.md +491 -0
- package/dist/developer/advanced/adding_spree_to_rails_app.md +106 -0
- package/dist/developer/cli/quickstart.md +137 -0
- package/dist/developer/contributing/creating-an-extension.md +258 -0
- package/dist/developer/contributing/developing-spree.md +339 -0
- package/dist/developer/contributing/quickstart.md +32 -0
- package/dist/developer/contributing/updating-extensions.md +67 -0
- package/dist/developer/core-concepts/addresses.md +265 -0
- package/dist/developer/core-concepts/adjustments.md +107 -0
- package/dist/developer/core-concepts/architecture.md +177 -0
- package/dist/developer/core-concepts/calculators.md +323 -0
- package/dist/developer/core-concepts/customers.md +230 -0
- package/dist/developer/core-concepts/events.md +624 -0
- package/dist/developer/core-concepts/imports-exports.md +698 -0
- package/dist/developer/core-concepts/inventory.md +191 -0
- package/dist/developer/core-concepts/markets.md +250 -0
- package/dist/developer/core-concepts/media.md +167 -0
- package/dist/developer/core-concepts/metafields.md +187 -0
- package/dist/developer/core-concepts/orders.md +328 -0
- package/dist/developer/core-concepts/payments.md +710 -0
- package/dist/developer/core-concepts/pricing.md +163 -0
- package/dist/developer/core-concepts/products.md +360 -0
- package/dist/developer/core-concepts/promotions.md +322 -0
- package/dist/developer/core-concepts/reports.md +206 -0
- package/dist/developer/core-concepts/search-filtering.md +237 -0
- package/dist/developer/core-concepts/shipments.md +212 -0
- package/dist/developer/core-concepts/slugs.md +111 -0
- package/dist/developer/core-concepts/staff-roles.md +123 -0
- package/dist/developer/core-concepts/store-credits-gift-cards.md +317 -0
- package/dist/developer/core-concepts/stores.md +117 -0
- package/dist/developer/core-concepts/taxes.md +135 -0
- package/dist/developer/core-concepts/translations.md +120 -0
- package/dist/developer/core-concepts/users.md +299 -0
- package/dist/developer/core-concepts/webhooks.md +378 -0
- package/dist/developer/create-spree-app/quickstart.md +158 -0
- package/dist/developer/customization/api.md +93 -0
- package/dist/developer/customization/authentication.md +88 -0
- package/dist/developer/customization/checkout.md +204 -0
- package/dist/developer/customization/configuration.md +55 -0
- package/dist/developer/customization/decorators.md +523 -0
- package/dist/developer/customization/dependencies.md +232 -0
- package/dist/developer/customization/emails.md +21 -0
- package/dist/developer/customization/extensions.md +92 -0
- package/dist/developer/customization/metadata.md +236 -0
- package/dist/developer/customization/model-preferences.md +130 -0
- package/dist/developer/customization/permissions.md +265 -0
- package/dist/developer/customization/quickstart.md +229 -0
- package/dist/developer/customization/routes.md +24 -0
- package/dist/developer/customization/v4/admin-panel.md +78 -0
- package/dist/developer/customization/v4/authentication.md +210 -0
- package/dist/developer/customization/v4/checkout.md +212 -0
- package/dist/developer/customization/v4/deface.md +251 -0
- package/dist/developer/customization/v4/images.md +86 -0
- package/dist/developer/customization/v4/storefront.md +450 -0
- package/dist/developer/deployment/assets.md +87 -0
- package/dist/developer/deployment/aws.md +335 -0
- package/dist/developer/deployment/caching.md +27 -0
- package/dist/developer/deployment/cdn.md +39 -0
- package/dist/developer/deployment/database.md +155 -0
- package/dist/developer/deployment/docker.md +128 -0
- package/dist/developer/deployment/emails.md +77 -0
- package/dist/developer/deployment/environment_variables.md +111 -0
- package/dist/developer/deployment/heroku.md +51 -0
- package/dist/developer/deployment/render.md +95 -0
- package/dist/developer/getting-started/quickstart.md +82 -0
- package/dist/developer/how-to/custom-payment-method.md +374 -0
- package/dist/developer/how-to/custom-promotion.md +373 -0
- package/dist/developer/how-to/custom-report.md +387 -0
- package/dist/developer/how-to/custom-search-provider.md +230 -0
- package/dist/developer/multi-store/quickstart.md +71 -0
- package/dist/developer/multi-store/setup.md +38 -0
- package/dist/developer/multi-tenant/configuration.md +41 -0
- package/dist/developer/multi-tenant/core-concepts.md +75 -0
- package/dist/developer/multi-tenant/installation.md +96 -0
- package/dist/developer/multi-tenant/quickstart.md +20 -0
- package/dist/developer/multi-vendor/installation.md +45 -0
- package/dist/developer/multi-vendor/quickstart.md +17 -0
- package/dist/developer/sdk/admin/quickstart.md +22 -0
- package/dist/developer/sdk/authentication.md +89 -0
- package/dist/developer/sdk/configuration.md +225 -0
- package/dist/developer/sdk/quickstart.md +82 -0
- package/dist/developer/sdk/store/account.md +67 -0
- package/dist/developer/sdk/store/cart-checkout.md +140 -0
- package/dist/developer/sdk/store/markets.md +151 -0
- package/dist/developer/sdk/store/payments.md +96 -0
- package/dist/developer/sdk/store/products.md +149 -0
- package/dist/developer/sdk/store/wishlists.md +52 -0
- package/dist/developer/security/pci_compliance.md +15 -0
- package/dist/developer/security/security_policy.md +68 -0
- package/dist/developer/storefront/blocks.md +285 -0
- package/dist/developer/storefront/custom-css.md +260 -0
- package/dist/developer/storefront/custom-javascript.md +166 -0
- package/dist/developer/storefront/helper-methods.md +1288 -0
- package/dist/developer/storefront/links.md +298 -0
- package/dist/developer/storefront/nextjs/architecture.md +150 -0
- package/dist/developer/storefront/nextjs/customization.md +141 -0
- package/dist/developer/storefront/nextjs/deployment.md +180 -0
- package/dist/developer/storefront/nextjs/quickstart.md +92 -0
- package/dist/developer/storefront/nextjs/spree-next-package.md +314 -0
- package/dist/developer/storefront/pages.md +163 -0
- package/dist/developer/storefront/sections.md +569 -0
- package/dist/developer/storefront/storefront.md +56 -0
- package/dist/developer/storefront/themes.md +161 -0
- package/dist/developer/tutorial/admin.md +134 -0
- package/dist/developer/tutorial/extending-models.md +380 -0
- package/dist/developer/tutorial/file-uploads.md +121 -0
- package/dist/developer/tutorial/introduction.md +33 -0
- package/dist/developer/tutorial/model.md +41 -0
- package/dist/developer/tutorial/page-builder.md +487 -0
- package/dist/developer/tutorial/rich-text.md +73 -0
- package/dist/developer/tutorial/seo.md +332 -0
- package/dist/developer/tutorial/storefront.md +352 -0
- package/dist/developer/tutorial/testing.md +558 -0
- package/dist/developer/upgrades/2.0-to-2.1.md +46 -0
- package/dist/developer/upgrades/2.1-to-2.2.md +59 -0
- package/dist/developer/upgrades/2.2-to-2.3.md +44 -0
- package/dist/developer/upgrades/2.3-to-2.4.md +42 -0
- package/dist/developer/upgrades/3.0-to-3.1.md +47 -0
- package/dist/developer/upgrades/3.1-to-3.2.md +34 -0
- package/dist/developer/upgrades/3.2-to-3.3.md +70 -0
- package/dist/developer/upgrades/3.3-to-3.4.md +36 -0
- package/dist/developer/upgrades/3.4-to-3.5.md +44 -0
- package/dist/developer/upgrades/3.5-to-3.6.md +40 -0
- package/dist/developer/upgrades/3.6-to-3.7.md +62 -0
- package/dist/developer/upgrades/3.7-to-4.0.md +152 -0
- package/dist/developer/upgrades/4.0-to-4.1.md +92 -0
- package/dist/developer/upgrades/4.1-to-4.2.md +109 -0
- package/dist/developer/upgrades/4.10-to-5.0.md +129 -0
- package/dist/developer/upgrades/4.2-to-4.3.md +100 -0
- package/dist/developer/upgrades/4.3-to-4.4.md +125 -0
- package/dist/developer/upgrades/4.4-to-4.5.md +94 -0
- package/dist/developer/upgrades/4.5-to-4.6.md +119 -0
- package/dist/developer/upgrades/4.6-to-4.7.md +39 -0
- package/dist/developer/upgrades/4.8-to-4.9.md +24 -0
- package/dist/developer/upgrades/4.9-to-4.10.md +24 -0
- package/dist/developer/upgrades/4.x-to-4.8.md +52 -0
- package/dist/developer/upgrades/5.0-to-5.1.md +28 -0
- package/dist/developer/upgrades/5.1-to-5.2.md +127 -0
- package/dist/developer/upgrades/5.2-to-5.3.md +338 -0
- package/dist/developer/upgrades/5.3-to-5.4.md +248 -0
- package/dist/developer/upgrades/quickstart.md +36 -0
- package/dist/integrations/analytics/google-analytics.md +64 -0
- package/dist/integrations/analytics/google-tag-manager.md +78 -0
- package/dist/integrations/integrations.md +39 -0
- package/dist/integrations/marketing/klaviyo.md +99 -0
- package/dist/integrations/payments/adyen.md +90 -0
- package/dist/integrations/payments/paypal.md +41 -0
- package/dist/integrations/payments/razorpay.md +45 -0
- package/dist/integrations/payments/stripe.md +109 -0
- package/dist/integrations/search/meilisearch.md +236 -0
- package/dist/integrations/sso-mfa-social-login/admin-dashboard.md +57 -0
- package/dist/integrations/sso-mfa-social-login/storefront.md +56 -0
- package/package.json +27 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Rich Text
|
|
3
|
+
description: Learn how to add rich text content to the Brands feature using Action Text
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
> **INFO:** This guide assumes you've completed the [Model](/developer/tutorial/model) and [Admin](/developer/tutorial/admin) tutorials.
|
|
7
|
+
|
|
8
|
+
Our Brand model has a `name` attribute to store the brand name. However it's missing a description. That's because we're going to use [Action Text](https://guides.rubyonrails.org/action_text_overview.html) to handle the rich text content.
|
|
9
|
+
|
|
10
|
+
## Step 1: Add Action Text to the Model
|
|
11
|
+
|
|
12
|
+
```ruby app/models/spree/brand.rb {4-5}
|
|
13
|
+
module Spree
|
|
14
|
+
class Brand < Spree::Base
|
|
15
|
+
# Action text for rich text content
|
|
16
|
+
has_rich_text :description
|
|
17
|
+
|
|
18
|
+
# ... other code ...
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
You don't need to run any migrations, as we won't be adding any new columns to the `spree_brands` table. That's because [Action Text](https://guides.rubyonrails.org/action_text_overview.html) stores all the rich text content in a separate table called `action_text_rich_texts`.
|
|
24
|
+
|
|
25
|
+
You can access the description content in code like this:
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
brand.description.to_s
|
|
29
|
+
# => "<h1>Hello</h1><p>World</p>"
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Or if you prefer plain text:
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
brand.description.to_plain_text
|
|
36
|
+
# => "Hello World"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Step 2: Add a WYSIWYG editor to the Admin Dashboard
|
|
40
|
+
|
|
41
|
+
In the Admin dashboard we want to use a WYSIWYG editor to add formatted text to the brand description. To do this, edit the `app/views/spree/admin/brands/_form.html.erb` file and add the following code:
|
|
42
|
+
|
|
43
|
+
```erb app/views/spree/admin/brands/_form.html.erb {4}
|
|
44
|
+
<div class="card mb-6">
|
|
45
|
+
<div class="card-body">
|
|
46
|
+
<%= f.spree_text_field :name %>
|
|
47
|
+
<%= f.spree_rich_text_area :description %>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This will render a WYSIWYG editor to the brand description field in the Admin dashboard.
|
|
53
|
+
|
|
54
|
+
## Step 3: Update permitted parameters in controller
|
|
55
|
+
|
|
56
|
+
To permit the `description` attribute to be saved, we need to update the permitted parameters in the controller. file and add the following code:
|
|
57
|
+
|
|
58
|
+
```ruby app/controllers/spree/admin/brands_controller.rb {9}
|
|
59
|
+
module Spree
|
|
60
|
+
module Admin
|
|
61
|
+
class BrandsController < ResourceController
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def permitted_resource_params
|
|
65
|
+
params.require(:brand).permit(
|
|
66
|
+
:name,
|
|
67
|
+
:description
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
```
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: SEO
|
|
3
|
+
description: Learn how to optimize your Brands feature for search engines
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
> **INFO:** This guide assumes you've completed the [Storefront](/developer/tutorial/storefront) tutorial. You should have working brand pages.
|
|
7
|
+
|
|
8
|
+
This guide covers SEO best practices for your custom Spree features, including friendly URLs, meta tags, and Open Graph data.
|
|
9
|
+
|
|
10
|
+
## SEO-Friendly URLs
|
|
11
|
+
|
|
12
|
+
Rather than using database IDs in URLs:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
https://example.com/brands/1
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Create human-readable URLs that help with SEO:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
https://example.com/brands/nike-sportswear
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Step 1: Add SEO Columns
|
|
25
|
+
|
|
26
|
+
Add columns for slug and meta fields:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
bin/rails g migration AddSeoFieldsToSpreeBrands slug:string:uniq meta_title:string meta_description:text
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
bin/rails db:migrate
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
This adds:
|
|
37
|
+
- `slug` - SEO-friendly URL with unique index
|
|
38
|
+
- `meta_title` - Custom page title for search engines
|
|
39
|
+
- `meta_description` - Description shown in search results
|
|
40
|
+
|
|
41
|
+
### Step 2: Add FriendlyId to the Model
|
|
42
|
+
|
|
43
|
+
Add FriendlyId to generate SEO-friendly URLs automatically:
|
|
44
|
+
|
|
45
|
+
```ruby app/models/spree/brand.rb {3-4,9}
|
|
46
|
+
module Spree
|
|
47
|
+
class Brand < Spree::Base
|
|
48
|
+
extend FriendlyId
|
|
49
|
+
friendly_id :slug_candidates, use: %i[slugged]
|
|
50
|
+
|
|
51
|
+
# ... other code ...
|
|
52
|
+
|
|
53
|
+
validates :name, presence: true
|
|
54
|
+
validates :slug, presence: true, uniqueness: { scope: spree_base_uniqueness_scope }
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The `slug_candidates` method is already defined in `Spree::Base`:
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
def slug_candidates
|
|
63
|
+
[
|
|
64
|
+
:name,
|
|
65
|
+
[:name, :uuid_for_friendly_id]
|
|
66
|
+
]
|
|
67
|
+
end
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This generates the slug from the name. If the name is taken, it appends a UUID.
|
|
71
|
+
|
|
72
|
+
### Step 3: Add SEO Fields to Admin
|
|
73
|
+
|
|
74
|
+
Spree provides a reusable SEO partial that handles slug, meta title, and meta description with a live search preview. Update your admin form:
|
|
75
|
+
|
|
76
|
+
```erb app/views/spree/admin/brands/_form.html.erb {1-2,5-12,15,25-32}
|
|
77
|
+
<div class="row" data-controller="slug-form seo-form">
|
|
78
|
+
<div class="col-lg-8">
|
|
79
|
+
<div class="card mb-6">
|
|
80
|
+
<div class="card-body">
|
|
81
|
+
<%= f.spree_text_field :name,
|
|
82
|
+
required: true,
|
|
83
|
+
data: {
|
|
84
|
+
seo_form_target: 'sourceTitleInput',
|
|
85
|
+
slug_form_target: 'name',
|
|
86
|
+
action: 'slug-form#updateUrlFromName'
|
|
87
|
+
} %>
|
|
88
|
+
<%= f.spree_rich_text_area :description,
|
|
89
|
+
data: { seo_form_target: 'sourceDescriptionInput' } %>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
</div>
|
|
93
|
+
|
|
94
|
+
<div class="col-lg-4">
|
|
95
|
+
<div class="card mb-6">
|
|
96
|
+
<div class="card-header">
|
|
97
|
+
<h5 class="card-title"><%= Spree.t(:logo) %></h5>
|
|
98
|
+
</div>
|
|
99
|
+
<div class="card-body">
|
|
100
|
+
<%= f.spree_file_field :logo, width: 300, height: 300, crop: true %>
|
|
101
|
+
</div>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<%= render 'spree/admin/shared/seo',
|
|
105
|
+
f: f,
|
|
106
|
+
title: @brand.name,
|
|
107
|
+
meta_title: @brand.meta_title,
|
|
108
|
+
description: @brand.description,
|
|
109
|
+
slug: @brand.slug,
|
|
110
|
+
slug_path: 'brands',
|
|
111
|
+
placeholder: 'Add a name and description to see how this brand might appear in a search engine listing' %>
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The SEO partial provides:
|
|
117
|
+
- **Live preview** of how the page will appear in search results
|
|
118
|
+
- **Meta title** field with character count
|
|
119
|
+
- **Meta description** field
|
|
120
|
+
- **Slug** field with auto-generation from name
|
|
121
|
+
|
|
122
|
+
The `seo-form` Stimulus controller syncs the preview with your form inputs in real-time.
|
|
123
|
+
|
|
124
|
+
Don't forget to permit the SEO attributes in your controller:
|
|
125
|
+
|
|
126
|
+
```ruby app/controllers/spree/admin/brands_controller.rb {6-8}
|
|
127
|
+
def permitted_resource_params
|
|
128
|
+
params.require(:brand).permit(
|
|
129
|
+
:name,
|
|
130
|
+
:description,
|
|
131
|
+
:logo,
|
|
132
|
+
:slug,
|
|
133
|
+
:meta_title,
|
|
134
|
+
:meta_description)
|
|
135
|
+
end
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Step 4: Use Slugs in Storefront
|
|
139
|
+
|
|
140
|
+
Update your controller to use `friendly.find`:
|
|
141
|
+
|
|
142
|
+
```ruby app/controllers/spree/brands_controller.rb
|
|
143
|
+
def load_brand
|
|
144
|
+
@brand = Spree::Brand.friendly.find(params[:id])
|
|
145
|
+
end
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The `friendly.find` method:
|
|
149
|
+
- Finds by slug first (e.g., `/brands/nike-sportswear`)
|
|
150
|
+
- Falls back to ID if no slug matches
|
|
151
|
+
- Raises `ActiveRecord::RecordNotFound` if neither is found
|
|
152
|
+
|
|
153
|
+
### URL Helpers
|
|
154
|
+
|
|
155
|
+
Always pass the model object to path helpers:
|
|
156
|
+
|
|
157
|
+
```erb
|
|
158
|
+
<%= link_to brand.name, spree.brand_path(brand) %>
|
|
159
|
+
<%# => /brands/nike-sportswear %>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
FriendlyId overrides `to_param` to return the slug automatically.
|
|
163
|
+
|
|
164
|
+
### Handling Slug Changes
|
|
165
|
+
|
|
166
|
+
To redirect old URLs when slugs change, enable slug history:
|
|
167
|
+
|
|
168
|
+
```ruby app/models/spree/brand.rb
|
|
169
|
+
extend FriendlyId
|
|
170
|
+
friendly_id :slug_candidates, use: %i[slugged history]
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Old slugs will automatically resolve to the current slug.
|
|
174
|
+
|
|
175
|
+
## Meta Tags & Open Graph
|
|
176
|
+
|
|
177
|
+
Spree automatically generates meta tags and Open Graph data. The system uses a helper called `object` that finds the main instance variable based on your controller name.
|
|
178
|
+
|
|
179
|
+
For `BrandsController`, Spree looks for `@brand` and generates:
|
|
180
|
+
- Meta description
|
|
181
|
+
- Open Graph tags (og:title, og:description, og:image)
|
|
182
|
+
- Twitter Card tags
|
|
183
|
+
|
|
184
|
+
### Page Title
|
|
185
|
+
|
|
186
|
+
Override `accurate_title` in your controller to set the page `<title>`:
|
|
187
|
+
|
|
188
|
+
```ruby app/controllers/spree/brands_controller.rb
|
|
189
|
+
private
|
|
190
|
+
|
|
191
|
+
def accurate_title
|
|
192
|
+
if @brand
|
|
193
|
+
@brand.meta_title.presence || @brand.name
|
|
194
|
+
else
|
|
195
|
+
Spree.t(:brands)
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Meta Description
|
|
201
|
+
|
|
202
|
+
Spree checks these sources in order:
|
|
203
|
+
|
|
204
|
+
1. `@page_description` instance variable (if set)
|
|
205
|
+
2. `@brand.meta_description` (if the model has this attribute)
|
|
206
|
+
3. `current_store.meta_description` (fallback)
|
|
207
|
+
|
|
208
|
+
#### Using the SEO Partial
|
|
209
|
+
|
|
210
|
+
If you followed [Step 1](#step-1-add-seo-columns) and [Step 3](#step-3-add-seo-fields-to-admin), your model already has `meta_description` and the admin form uses the SEO partial. Spree will automatically use `@brand.meta_description` for the page description.
|
|
211
|
+
|
|
212
|
+
#### Manual Override
|
|
213
|
+
|
|
214
|
+
For custom logic, set `@page_description` directly:
|
|
215
|
+
|
|
216
|
+
```ruby
|
|
217
|
+
def show
|
|
218
|
+
@page_description = "Shop #{@brand.name} - #{@brand.products.count} products available"
|
|
219
|
+
end
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Social Sharing Image
|
|
223
|
+
|
|
224
|
+
For Open Graph images, Spree checks:
|
|
225
|
+
|
|
226
|
+
1. `@page_image` instance variable (if set)
|
|
227
|
+
2. `@brand.image` (if the model responds to `image`)
|
|
228
|
+
3. `current_store.social_image` (fallback)
|
|
229
|
+
|
|
230
|
+
If your Brand model has `logo` but not `image`, add an alias:
|
|
231
|
+
|
|
232
|
+
```ruby app/models/spree/brand.rb
|
|
233
|
+
def image
|
|
234
|
+
logo
|
|
235
|
+
end
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Or set it manually:
|
|
239
|
+
|
|
240
|
+
```ruby
|
|
241
|
+
def show
|
|
242
|
+
@page_image = @brand.logo if @brand.logo.attached?
|
|
243
|
+
end
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Complete SEO-Optimized Model
|
|
247
|
+
|
|
248
|
+
Here's a complete Brand model with full SEO support:
|
|
249
|
+
|
|
250
|
+
```ruby app/models/spree/brand.rb
|
|
251
|
+
module Spree
|
|
252
|
+
class Brand < Spree::Base
|
|
253
|
+
extend FriendlyId
|
|
254
|
+
friendly_id :slug_candidates, use: %i[slugged]
|
|
255
|
+
|
|
256
|
+
has_many :products, class_name: 'Spree::Product', dependent: :nullify
|
|
257
|
+
|
|
258
|
+
has_one_attached :logo
|
|
259
|
+
has_rich_text :description
|
|
260
|
+
|
|
261
|
+
# Database columns: slug, meta_title, meta_description
|
|
262
|
+
|
|
263
|
+
validates :name, presence: true
|
|
264
|
+
validates :slug, presence: true, uniqueness: { scope: spree_base_uniqueness_scope }
|
|
265
|
+
|
|
266
|
+
# SEO: Use logo as Open Graph image
|
|
267
|
+
def image
|
|
268
|
+
logo
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Complete SEO-Optimized Controller
|
|
275
|
+
|
|
276
|
+
```ruby app/controllers/spree/brands_controller.rb
|
|
277
|
+
module Spree
|
|
278
|
+
class BrandsController < StoreController
|
|
279
|
+
before_action :load_brand, only: [:show]
|
|
280
|
+
|
|
281
|
+
def index
|
|
282
|
+
@brands = Spree::Brand.order(:name)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
def show
|
|
286
|
+
@products = @brand.products
|
|
287
|
+
.active(current_currency)
|
|
288
|
+
.includes(storefront_products_includes)
|
|
289
|
+
.page(params[:page])
|
|
290
|
+
.per(12)
|
|
291
|
+
|
|
292
|
+
# Optional: Custom page description
|
|
293
|
+
# @page_description = "Shop #{@brand.name} products"
|
|
294
|
+
|
|
295
|
+
# Optional: Custom social image
|
|
296
|
+
# @page_image = @brand.logo if @brand.logo.attached?
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
private
|
|
300
|
+
|
|
301
|
+
def load_brand
|
|
302
|
+
@brand = Spree::Brand.friendly.find(params[:id])
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def accurate_title
|
|
306
|
+
if @brand
|
|
307
|
+
@brand.meta_title.presence || @brand.name
|
|
308
|
+
else
|
|
309
|
+
Spree.t(:brands)
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## SEO Checklist
|
|
317
|
+
|
|
318
|
+
**Friendly URLs** - Use slugs instead of IDs (`/brands/nike` not `/brands/1`)
|
|
319
|
+
|
|
320
|
+
**Page Titles** - Override `accurate_title` for descriptive titles
|
|
321
|
+
|
|
322
|
+
**Meta Descriptions** - Add `meta_description` field or set `@page_description`
|
|
323
|
+
|
|
324
|
+
**Social Images** - Provide `image` method or set `@page_image` for Open Graph
|
|
325
|
+
|
|
326
|
+
**Slug History** - Enable FriendlyId history to handle URL changes gracefully
|
|
327
|
+
|
|
328
|
+
## Related Documentation
|
|
329
|
+
|
|
330
|
+
- [Storefront Tutorial](/developer/tutorial/storefront) - Building storefront pages
|
|
331
|
+
- [Model Tutorial](/developer/tutorial/model) - Creating the Brand model
|
|
332
|
+
- [Admin Tutorial](/developer/tutorial/admin) - Building the admin interface
|