@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,161 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Storefront Themes"
|
|
3
|
+
sidebarTitle: "Themes"
|
|
4
|
+
description: "Learn how to develop themes for your Spree storefront"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Themes are the main building blocks of the storefront. They hold styles, settings, and templates for the storefront.
|
|
8
|
+
|
|
9
|
+
## Default Theme
|
|
10
|
+
|
|
11
|
+
Spree comes with a default theme called `default`. It is used as a base for all the themes and contains the core styles and scripts. This theme is included with the Spree Commerce core and is always available.
|
|
12
|
+
|
|
13
|
+
## Creating a New Theme
|
|
14
|
+
|
|
15
|
+
You can create your custom themes by following the steps below.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
bin/rails g spree:storefront:theme MyTheme
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
This command will create a new theme called `my_theme` in the `app/views/themes` directory and copy the default theme files to the new theme.
|
|
22
|
+
|
|
23
|
+
It will also add a line in your `config/initializers/spree.rb` file to load the new theme.
|
|
24
|
+
|
|
25
|
+
```ruby config/initializers/spree.rb
|
|
26
|
+
Spree.page_builder.themes << Spree::Themes::MyTheme
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Now you can go to **Admin Dashboard -> Storefront -> Themes**, you should see a new section **Add new theme** with your theme name. Click **Add** button to activate the theme.
|
|
30
|
+
|
|
31
|
+
### Theme Templates
|
|
32
|
+
|
|
33
|
+
Theme has the following template structure:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
├── account
|
|
37
|
+
│ ├── store_credits
|
|
38
|
+
│ ├── profile
|
|
39
|
+
│ ├── orders
|
|
40
|
+
│ ├── newsletter
|
|
41
|
+
│ └── addresses
|
|
42
|
+
├── checkout
|
|
43
|
+
├── contacts
|
|
44
|
+
├── orders
|
|
45
|
+
├── page_sections
|
|
46
|
+
├── policies
|
|
47
|
+
├── posts
|
|
48
|
+
├── products
|
|
49
|
+
├── search
|
|
50
|
+
├── settings
|
|
51
|
+
├── shared
|
|
52
|
+
└── wishlists
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Each directory contains standard [Ruby on Rails ERB templates](https://guides.rubyonrails.org/action_view_overview.html) for the corresponding page.
|
|
56
|
+
|
|
57
|
+
The `page_sections` directory contains sections that are used globally across the storefront.
|
|
58
|
+
|
|
59
|
+
The `shared` directory contains shared components that are used across the storefront.
|
|
60
|
+
|
|
61
|
+
### Theme Settings
|
|
62
|
+
|
|
63
|
+
The theme settings are stored in the `app/models/spree/themes/my_theme.rb` file.
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
module Spree
|
|
67
|
+
module Themes
|
|
68
|
+
class MyTheme < Spree::Theme
|
|
69
|
+
def self.metadata
|
|
70
|
+
{
|
|
71
|
+
authors: ['Spree Commerce'], # replace with your name
|
|
72
|
+
license: 'MIT' # replace with your license
|
|
73
|
+
}
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# COLORS
|
|
77
|
+
# main colors
|
|
78
|
+
preference :primary_color, :string, default: '#000000'
|
|
79
|
+
preference :accent_color, :string, default: '#F0EFE9'
|
|
80
|
+
preference :danger_color, :string, default: '#C73528'
|
|
81
|
+
preference :neutral_color, :string, default: '#999999'
|
|
82
|
+
preference :background_color, :string, default: '#FFFFFF'
|
|
83
|
+
preference :text_color, :string, default: '#000000'
|
|
84
|
+
preference :success_color, :string, default: '#00C773'
|
|
85
|
+
|
|
86
|
+
# buttons
|
|
87
|
+
preference :button_background_color, :string
|
|
88
|
+
preference :button_text_color, :string, default: '#ffffff'
|
|
89
|
+
preference :button_hover_background_color, :string
|
|
90
|
+
preference :button_hover_text_color, :string
|
|
91
|
+
preference :button_border_color, :string
|
|
92
|
+
|
|
93
|
+
# borders
|
|
94
|
+
preference :border_color, :string, default: '#E9E7DC'
|
|
95
|
+
preference :sidebar_border_color, :string
|
|
96
|
+
|
|
97
|
+
preference :secondary_button_background_color, :string
|
|
98
|
+
preference :secondary_button_text_color, :string
|
|
99
|
+
preference :secondary_button_hover_background_color, :string
|
|
100
|
+
preference :secondary_button_hover_text_color, :string
|
|
101
|
+
|
|
102
|
+
# inputs
|
|
103
|
+
preference :input_text_color, :string, default: '#6b7280'
|
|
104
|
+
preference :input_background_color, :string, default: '#ffffff'
|
|
105
|
+
preference :input_border_color, :string
|
|
106
|
+
preference :input_focus_border_color, :string
|
|
107
|
+
preference :input_focus_background_color, :string
|
|
108
|
+
preference :input_focus_text_color, :string
|
|
109
|
+
|
|
110
|
+
# sidebar (checkout)
|
|
111
|
+
preference :checkout_sidebar_background_color, :string, default: '#f3f4f6'
|
|
112
|
+
preference :checkout_divider_background_color, :string
|
|
113
|
+
preference :checkout_sidebar_text_color, :string
|
|
114
|
+
|
|
115
|
+
# TYPOGRAPHY
|
|
116
|
+
preference :custom_font_code, :string, default: nil
|
|
117
|
+
# body
|
|
118
|
+
preference :font_family, :string, default: 'Inter'
|
|
119
|
+
preference :font_size_scale, :integer, default: 100
|
|
120
|
+
# headers
|
|
121
|
+
preference :header_font_family, :string, default: 'Inter'
|
|
122
|
+
preference :header_font_size_scale, :integer, default: 100
|
|
123
|
+
preference :headings_uppercase, :boolean, default: true
|
|
124
|
+
|
|
125
|
+
# BUTTONS
|
|
126
|
+
preference :button_border_thickness, :integer, default: 1
|
|
127
|
+
preference :button_border_opacity, :integer, default: 100
|
|
128
|
+
preference :button_border_radius, :integer, default: 100
|
|
129
|
+
preference :button_shadow_opacity, :integer, default: 0
|
|
130
|
+
preference :button_shadow_horizontal_offset, :integer, default: 0
|
|
131
|
+
preference :button_shadow_vertical_offset, :integer, default: 4
|
|
132
|
+
preference :button_shadow_blur, :integer, default: 5
|
|
133
|
+
|
|
134
|
+
# INPUTS
|
|
135
|
+
preference :input_border_thickness, :integer, default: 1
|
|
136
|
+
preference :input_border_opacity, :integer, default: 100
|
|
137
|
+
preference :input_border_radius, :integer, default: 8
|
|
138
|
+
preference :input_shadow_opacity, :integer, default: 0
|
|
139
|
+
preference :input_shadow_horizontal_offset, :integer, default: 0
|
|
140
|
+
preference :input_shadow_vertical_offset, :integer, default: 4
|
|
141
|
+
preference :input_shadow_blur, :integer, default: 5
|
|
142
|
+
|
|
143
|
+
# BORDERS
|
|
144
|
+
preference :border_width, :integer, default: 1
|
|
145
|
+
preference :border_radius, :integer, default: 6
|
|
146
|
+
preference :border_shadow_opacity, :integer, default: 0
|
|
147
|
+
preference :border_shadow_horizontal_offset, :integer, default: 0
|
|
148
|
+
preference :border_shadow_vertical_offset, :integer, default: 4
|
|
149
|
+
preference :border_shadow_blur, :integer, default: 5
|
|
150
|
+
|
|
151
|
+
# PRODUCT IMAGES
|
|
152
|
+
preference :product_listing_image_height, :integer, default: 300
|
|
153
|
+
preference :product_listing_image_width, :integer, default: 300
|
|
154
|
+
preference :product_listing_image_height_mobile, :integer, default: 190
|
|
155
|
+
preference :product_listing_image_width_mobile, :integer, default: 190
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
These are standard [Spree preferences](/developer/customization/model-preferences) that are configurable via Page Builder.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Admin Dashboard
|
|
3
|
+
description: Learn how to create Admin Dashboard UI to manage Brands
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Now that we've created a complete "Brand" model, let's create an Admin Dashboard interface so our admins can manage the brands.
|
|
7
|
+
|
|
8
|
+
## Step 1: Create the boilerplate Admin Dashboard UI
|
|
9
|
+
|
|
10
|
+
Admin Scaffold generator is a tool that helps us create a complete Admin Dashboard UI for our new resource.
|
|
11
|
+
|
|
12
|
+
Run the following command in our Spree application root directory:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
bin/rails g spree:admin:scaffold Spree::Brand
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
This will create the following files:
|
|
19
|
+
|
|
20
|
+
| File Type | Path | Description |
|
|
21
|
+
|-----------|------|-------------|
|
|
22
|
+
| Controller | `app/controllers/spree/admin/brands_controller.rb` | Handles the logic for the brands resource. |
|
|
23
|
+
| View | `app/views/spree/admin/brands/index.html.erb` | Displays the list of brands using the new tables system. |
|
|
24
|
+
| View | `app/views/spree/admin/brands/new.html.erb` | Displays the new brand form. |
|
|
25
|
+
| View | `app/views/spree/admin/brands/edit.html.erb` | Displays the edit brand form. |
|
|
26
|
+
| Partial | `app/views/spree/admin/brands/_form.html.erb` | The form partial used in new and edit views. |
|
|
27
|
+
| Initializer | `config/initializers/spree_admin_brands_table.rb` | Registers the table with columns for the index view. |
|
|
28
|
+
| Initializer | `config/initializers/spree_admin_brands_navigation.rb` | Adds navigation to the admin sidebar. |
|
|
29
|
+
|
|
30
|
+
It will also automatically add the following routes to your `config/routes.rb` file:
|
|
31
|
+
|
|
32
|
+
```ruby config/routes.rb
|
|
33
|
+
namespace :admin do
|
|
34
|
+
resources :brands
|
|
35
|
+
end
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
You will now be able to access the brands resource at `http://localhost:3000/admin/brands` in your browser. To reference this route in your code, you can use the `spree.admin_brands_path` helper.
|
|
39
|
+
|
|
40
|
+
## Step 2: Customize the Table Columns
|
|
41
|
+
|
|
42
|
+
The scaffold generator creates a table initializer at `config/initializers/spree_admin_brands_table.rb` with default columns:
|
|
43
|
+
|
|
44
|
+
```ruby config/initializers/spree_admin_brands_table.rb
|
|
45
|
+
Rails.application.config.after_initialize do
|
|
46
|
+
Spree.admin.tables.register(:brands, model_class: Spree::Brand, search_param: :name_cont)
|
|
47
|
+
|
|
48
|
+
Spree.admin.tables.brands.add :name,
|
|
49
|
+
label: :name,
|
|
50
|
+
type: :link,
|
|
51
|
+
sortable: true,
|
|
52
|
+
filterable: true,
|
|
53
|
+
default: true,
|
|
54
|
+
position: 10
|
|
55
|
+
|
|
56
|
+
Spree.admin.tables.brands.add :created_at,
|
|
57
|
+
label: :created_at,
|
|
58
|
+
type: :datetime,
|
|
59
|
+
sortable: true,
|
|
60
|
+
filterable: true,
|
|
61
|
+
default: true,
|
|
62
|
+
position: 20
|
|
63
|
+
|
|
64
|
+
Spree.admin.tables.brands.add :updated_at,
|
|
65
|
+
label: :updated_at,
|
|
66
|
+
type: :datetime,
|
|
67
|
+
sortable: true,
|
|
68
|
+
filterable: true,
|
|
69
|
+
default: false,
|
|
70
|
+
position: 30
|
|
71
|
+
end
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
You can customize this file to add more columns based on your model's attributes. For example, to add a description column:
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
Spree.admin.tables.brands.add :description,
|
|
78
|
+
label: :description,
|
|
79
|
+
type: :string,
|
|
80
|
+
sortable: false,
|
|
81
|
+
filterable: true,
|
|
82
|
+
default: true,
|
|
83
|
+
position: 15
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
> **INFO:** For complete table customization options including column types, filtering, sorting, and bulk actions, see the [Admin Tables](/developer/admin/tables) guide.
|
|
87
|
+
|
|
88
|
+
## Step 3: Customize Navigation
|
|
89
|
+
|
|
90
|
+
The scaffold generator also creates a navigation initializer at `config/initializers/spree_admin_brands_navigation.rb`:
|
|
91
|
+
|
|
92
|
+
```ruby config/initializers/spree_admin_brands_navigation.rb
|
|
93
|
+
Rails.application.config.after_initialize do
|
|
94
|
+
Spree.admin.navigation.sidebar.add :brands,
|
|
95
|
+
label: :brands,
|
|
96
|
+
url: :admin_brands_path,
|
|
97
|
+
icon: 'list',
|
|
98
|
+
position: 55,
|
|
99
|
+
active: -> { controller_name == 'brands' },
|
|
100
|
+
if: -> { can?(:manage, Spree::Brand) }
|
|
101
|
+
end
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
You can customize the icon and position to fit your needs. For example, to use the "award" icon and place it between Products (30) and Customers (40):
|
|
105
|
+
|
|
106
|
+
```ruby
|
|
107
|
+
Spree.admin.navigation.sidebar.add :brands,
|
|
108
|
+
label: :brands,
|
|
109
|
+
url: :admin_brands_path,
|
|
110
|
+
icon: 'award',
|
|
111
|
+
position: 35,
|
|
112
|
+
active: -> { controller_name == 'brands' },
|
|
113
|
+
if: -> { can?(:manage, Spree::Brand) }
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Adding to an Existing Submenu
|
|
117
|
+
|
|
118
|
+
To add "Brands" to the Products submenu instead, use the `parent` option:
|
|
119
|
+
|
|
120
|
+
```ruby config/initializers/spree_admin_brands_navigation.rb
|
|
121
|
+
Rails.application.config.after_initialize do
|
|
122
|
+
Spree.admin.navigation.sidebar.add :brands,
|
|
123
|
+
label: :brands,
|
|
124
|
+
url: :admin_brands_path,
|
|
125
|
+
position: 50,
|
|
126
|
+
parent: :products,
|
|
127
|
+
active: -> { controller_name == 'brands' },
|
|
128
|
+
if: -> { can?(:manage, Spree::Brand) }
|
|
129
|
+
end
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
After restarting your server, you'll see the new "Brands" navigation link in the admin sidebar!
|
|
133
|
+
|
|
134
|
+
> **INFO:** For complete navigation API documentation including all available options, submenu creation, badges, and more, see the [Admin Navigation](/developer/admin/navigation) guide.
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Extending Core Models
|
|
3
|
+
description: Learn how to extend Spree's core models to connect Brands with Products
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
In this tutorial, we'll connect our custom Brand model with Spree's core Product model. This is a common pattern when building features that need to integrate with existing Spree functionality.
|
|
7
|
+
|
|
8
|
+
> **INFO:** This guide assumes you've completed the [Model](/developer/tutorial/model), [Admin](/developer/tutorial/admin), and [File Uploads](/developer/tutorial/file-uploads) tutorials.
|
|
9
|
+
|
|
10
|
+
## What We're Building
|
|
11
|
+
|
|
12
|
+
By the end of this tutorial, you'll have:
|
|
13
|
+
|
|
14
|
+
- Products associated with Brands
|
|
15
|
+
- A brand selector in the Product admin form
|
|
16
|
+
- Understanding of how to safely extend Spree core models
|
|
17
|
+
|
|
18
|
+
## Choosing the Right Approach
|
|
19
|
+
|
|
20
|
+
Before extending Spree models, consider which approach fits your needs best:
|
|
21
|
+
|
|
22
|
+
| What you need | Recommended approach |
|
|
23
|
+
|---------------|---------------------|
|
|
24
|
+
| Add association (belongs_to, has_many) | **Decorator** (this tutorial) |
|
|
25
|
+
| Add validation or scope | **Decorator** |
|
|
26
|
+
| Add new instance/class method | **Decorator** |
|
|
27
|
+
| React to model changes (after save, etc.) | [Events subscriber](/developer/core-concepts/events) |
|
|
28
|
+
| Sync with external service on changes | [Events](/developer/core-concepts/events) or [Webhooks](/developer/core-concepts/webhooks) |
|
|
29
|
+
| Add searchable/filterable field | [Ransack configuration](/developer/core-concepts/search-filtering#extending-ransackable-configuration) |
|
|
30
|
+
| Add admin UI elements | [Admin Partials](/developer/admin/extending-ui) |
|
|
31
|
+
|
|
32
|
+
> **INFO:** This tutorial uses **decorators** because we're adding a structural association between models. For behavioral changes like callbacks, prefer [Events](/developer/core-concepts/events) instead - they're easier to test and maintain.
|
|
33
|
+
|
|
34
|
+
## Understanding Decorators
|
|
35
|
+
|
|
36
|
+
When working with Spree, you'll often need to add functionality to existing models like `Spree::Product` or `Spree::Order`. However, you shouldn't modify these files directly because:
|
|
37
|
+
|
|
38
|
+
1. **Upgrades** - Your changes would be lost when updating Spree
|
|
39
|
+
2. **Maintainability** - It's hard to track what you've customized
|
|
40
|
+
3. **Conflicts** - Direct modifications can conflict with Spree's code
|
|
41
|
+
|
|
42
|
+
Instead, we use **decorators** - a Ruby pattern that lets you add or modify behavior of existing classes without changing their original source code.
|
|
43
|
+
|
|
44
|
+
### How Decorators Work
|
|
45
|
+
|
|
46
|
+
In Ruby, classes are "open" - you can add methods to them at any time. Decorators leverage this by:
|
|
47
|
+
|
|
48
|
+
1. Creating a module with your new methods
|
|
49
|
+
2. Using `Module#prepend` to inject your module into the class's inheritance chain
|
|
50
|
+
3. Your methods run first, and can call `super` to invoke the original method
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
# This is the basic pattern
|
|
54
|
+
module Spree
|
|
55
|
+
module ProductDecorator
|
|
56
|
+
# Add a new method
|
|
57
|
+
def my_new_method
|
|
58
|
+
"Hello from decorator!"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Override an existing method
|
|
62
|
+
def existing_method
|
|
63
|
+
# Do something before
|
|
64
|
+
result = super # Call the original method
|
|
65
|
+
# Do something after
|
|
66
|
+
result
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
Product.prepend(ProductDecorator)
|
|
71
|
+
end
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The key line is `Product.prepend(ProductDecorator)` - this inserts your module at the beginning of the method lookup chain, so your methods are found first.
|
|
75
|
+
|
|
76
|
+
## Step 1: Create the Migration
|
|
77
|
+
|
|
78
|
+
First, add a `brand_id` column to the products table:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
bin/rails g migration AddBrandIdToSpreeProducts brand_id:integer:index
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Edit the migration to add an index (but no foreign key constraint, keeping it optional):
|
|
85
|
+
|
|
86
|
+
```ruby db/migrate/XXXXXXXXXXXXXX_add_brand_id_to_spree_products.rb
|
|
87
|
+
class AddBrandIdToSpreeProducts < ActiveRecord::Migration[8.0]
|
|
88
|
+
def change
|
|
89
|
+
add_column :spree_products, :brand_id, :integer
|
|
90
|
+
add_index :spree_products, :brand_id
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Run the migration:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
bin/rails db:migrate
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
> **INFO:** We intentionally don't add a foreign key constraint. This keeps the association optional and avoids issues if brands are deleted. Spree follows this pattern for flexibility.
|
|
102
|
+
|
|
103
|
+
## Step 2: Generate the Product Decorator
|
|
104
|
+
|
|
105
|
+
Spree provides a generator to create decorator files with the correct structure:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
bin/rails g spree:model_decorator Spree::Product
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
This creates `app/models/spree/product_decorator.rb`:
|
|
112
|
+
|
|
113
|
+
```ruby app/models/spree/product_decorator.rb
|
|
114
|
+
module Spree
|
|
115
|
+
module ProductDecorator
|
|
116
|
+
def self.prepended(base)
|
|
117
|
+
# Class-level configurations go here
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
Product.prepend(ProductDecorator)
|
|
122
|
+
end
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Step 3: Add the Brand Association to Product
|
|
126
|
+
|
|
127
|
+
Update the decorator to add the `belongs_to` association:
|
|
128
|
+
|
|
129
|
+
```ruby app/models/spree/product_decorator.rb {4}
|
|
130
|
+
module Spree
|
|
131
|
+
module ProductDecorator
|
|
132
|
+
def self.prepended(base)
|
|
133
|
+
base.belongs_to :brand, class_name: 'Spree::Brand', optional: true
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
Product.prepend(ProductDecorator)
|
|
138
|
+
end
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Understanding the Code
|
|
142
|
+
|
|
143
|
+
- `self.prepended(base)` - This callback runs when the module is prepended to a class. The `base` parameter is the class being decorated (`Spree::Product`)
|
|
144
|
+
- `base.belongs_to` - We call class methods on `base` to add associations, validations, scopes, etc.
|
|
145
|
+
- `optional: true` - Products don't require a brand (the `brand_id` can be `NULL`)
|
|
146
|
+
|
|
147
|
+
## Step 4: Add Products Association to Brand
|
|
148
|
+
|
|
149
|
+
Now update your Brand model to define the reverse association:
|
|
150
|
+
|
|
151
|
+
```ruby app/models/spree/brand.rb {5}
|
|
152
|
+
module Spree
|
|
153
|
+
class Brand < Spree::Base
|
|
154
|
+
# ... existing code ...
|
|
155
|
+
|
|
156
|
+
has_many :products, class_name: 'Spree::Product', dependent: :nullify
|
|
157
|
+
|
|
158
|
+
# ... rest of your model ...
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
> **INFO:** We use `dependent: :nullify` instead of `dependent: :destroy`. When a brand is deleted, products will have their `brand_id` set to `NULL` rather than being deleted. This is safer for e-commerce data.
|
|
164
|
+
|
|
165
|
+
## Step 5: Permit the Brand Parameter
|
|
166
|
+
|
|
167
|
+
For the admin form to save the brand association, we need to permit the `brand_id` parameter.
|
|
168
|
+
|
|
169
|
+
Add to your Spree initializer:
|
|
170
|
+
|
|
171
|
+
```ruby config/initializers/spree.rb {3}
|
|
172
|
+
# .. other code ..
|
|
173
|
+
|
|
174
|
+
Spree::PermittedAttributes.product_attributes << :brand_id
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Step 6: Add Brand Selector to Product Admin Form
|
|
178
|
+
|
|
179
|
+
Create a partial to inject the brand selector into the product form. Spree's admin product form has injection points for customization.
|
|
180
|
+
|
|
181
|
+
Create the partial:
|
|
182
|
+
|
|
183
|
+
```erb app/views/spree/admin/products/_brand_field.html.erb
|
|
184
|
+
<div class="card mb-6">
|
|
185
|
+
<div class="card-header">
|
|
186
|
+
<h5 class="card-title"><%= Spree.t(:brand) %></h5>
|
|
187
|
+
</div>
|
|
188
|
+
<div class="card-body">
|
|
189
|
+
<%= f.spree_select :brand_id,
|
|
190
|
+
Spree::Brand.order(:name).pluck(:name, :id),
|
|
191
|
+
{ include_blank: true, label: Spree.t(:brand) } %>
|
|
192
|
+
</div>
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Register this partial to appear in the product form. Add to your initializer:
|
|
196
|
+
|
|
197
|
+
**Spree 5.2+:**
|
|
198
|
+
|
|
199
|
+
```ruby config/initializers/spree.rb
|
|
200
|
+
Rails.application.config.after_initialize do
|
|
201
|
+
Spree.admin.partials.product_form_sidebar << 'spree/admin/products/brand_field'
|
|
202
|
+
end
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Spree 5.1 and below:**
|
|
206
|
+
|
|
207
|
+
```ruby config/initializers/spree.rb
|
|
208
|
+
Rails.application.config.spree_admin.product_form_sidebar_partials << 'spree/admin/products/brand_field'
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
## Step 7: Add Translation
|
|
213
|
+
|
|
214
|
+
Add the translation for the brand label:
|
|
215
|
+
|
|
216
|
+
```yaml config/locales/en.yml
|
|
217
|
+
en:
|
|
218
|
+
spree:
|
|
219
|
+
brand: Brand
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Testing the Association
|
|
223
|
+
|
|
224
|
+
Verify everything works in the Rails console:
|
|
225
|
+
|
|
226
|
+
```ruby
|
|
227
|
+
# Create a brand and product
|
|
228
|
+
brand = Spree::Brand.create!(name: 'Nike')
|
|
229
|
+
product = Spree::Product.first
|
|
230
|
+
product.update!(brand: brand)
|
|
231
|
+
|
|
232
|
+
# Test associations
|
|
233
|
+
product.brand # => #<Spree::Brand id: 1, name: "Nike"...>
|
|
234
|
+
brand.products # => [#<Spree::Product...>]
|
|
235
|
+
brand.products.count # => 1
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Decorator Best Practices
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
- **Use prepended callback** — Always use `self.prepended(base)` for class-level additions like associations, validations, and scopes.
|
|
242
|
+
|
|
243
|
+
- **Keep decorators focused** — Each decorator should have a single responsibility. Create multiple decorators if needed.
|
|
244
|
+
|
|
245
|
+
- **Call super when overriding** — When overriding methods, call `super` to preserve original behavior unless you intentionally want to replace it.
|
|
246
|
+
|
|
247
|
+
- **Test decorated behavior** — Write tests specifically for your decorated functionality to catch regressions.
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
### Common Decorator Patterns
|
|
251
|
+
|
|
252
|
+
#### Adding Validations
|
|
253
|
+
|
|
254
|
+
```ruby
|
|
255
|
+
def self.prepended(base)
|
|
256
|
+
base.validates :custom_field, presence: true
|
|
257
|
+
end
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
#### Adding Scopes
|
|
261
|
+
|
|
262
|
+
```ruby
|
|
263
|
+
def self.prepended(base)
|
|
264
|
+
base.scope :featured, -> { where(featured: true) }
|
|
265
|
+
end
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
#### Adding Callbacks
|
|
269
|
+
|
|
270
|
+
> **WARNING:** For callbacks that trigger side effects (syncing to external services, sending notifications, etc.), use [Events subscribers](/developer/core-concepts/events) instead of decorator callbacks. Events are easier to test and won't break during Spree upgrades.
|
|
271
|
+
|
|
272
|
+
**Decorator approach** (use only for simple, internal logic):
|
|
273
|
+
|
|
274
|
+
```ruby
|
|
275
|
+
def self.prepended(base)
|
|
276
|
+
base.before_save :normalize_name
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def normalize_name
|
|
280
|
+
self.name = name.strip.titleize if name.present?
|
|
281
|
+
end
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Events approach** (recommended for side effects):
|
|
285
|
+
|
|
286
|
+
```ruby app/subscribers/my_app/product_sync_subscriber.rb
|
|
287
|
+
module MyApp
|
|
288
|
+
class ProductSyncSubscriber < Spree::Subscriber
|
|
289
|
+
subscribes_to 'product.updated'
|
|
290
|
+
|
|
291
|
+
def handle(event)
|
|
292
|
+
product = Spree::Product.find_by(id: event.payload['id'])
|
|
293
|
+
return unless product
|
|
294
|
+
|
|
295
|
+
ExternalSyncService.sync(product)
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
#### Adding Class Methods
|
|
302
|
+
|
|
303
|
+
```ruby
|
|
304
|
+
def self.prepended(base)
|
|
305
|
+
base.extend ClassMethods
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
module ClassMethods
|
|
309
|
+
def my_class_method
|
|
310
|
+
# Class method logic
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## Complete Files
|
|
316
|
+
|
|
317
|
+
### Product Decorator
|
|
318
|
+
|
|
319
|
+
```ruby app/models/spree/product_decorator.rb
|
|
320
|
+
module Spree
|
|
321
|
+
module ProductDecorator
|
|
322
|
+
def self.prepended(base)
|
|
323
|
+
base.belongs_to :brand, class_name: 'Spree::Brand', optional: true
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
Product.prepend(ProductDecorator)
|
|
328
|
+
end
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Brand Model (Updated)
|
|
332
|
+
|
|
333
|
+
```ruby app/models/spree/brand.rb
|
|
334
|
+
module Spree
|
|
335
|
+
class Brand < Spree::Base
|
|
336
|
+
has_many :products, class_name: 'Spree::Product', dependent: :nullify
|
|
337
|
+
|
|
338
|
+
has_one_attached :logo
|
|
339
|
+
has_rich_text :description
|
|
340
|
+
|
|
341
|
+
validates :name, presence: true
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
> **INFO:** SEO features like slugs, meta titles, and FriendlyId are covered in the [SEO](/developer/tutorial/seo) tutorial.
|
|
347
|
+
|
|
348
|
+
### Spree Initializer Additions
|
|
349
|
+
|
|
350
|
+
```ruby config/initializers/spree.rb
|
|
351
|
+
# Permit brand_id in product params
|
|
352
|
+
Spree::PermittedAttributes.product_attributes << :brand_id
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**Spree 5.2+:**
|
|
356
|
+
|
|
357
|
+
```ruby config/initializers/spree.rb
|
|
358
|
+
Rails.application.config.after_initialize do
|
|
359
|
+
# Add brand field to product form
|
|
360
|
+
Spree.admin.partials.product_form << 'spree/admin/products/brand_field'
|
|
361
|
+
end
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
**Spree 5.1 and below:**
|
|
365
|
+
|
|
366
|
+
```ruby config/initializers/spree.rb
|
|
367
|
+
# Add brand field to product form
|
|
368
|
+
Rails.application.config.spree_admin.product_form_partials << 'spree/admin/products/brand_field'
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
## Related Documentation
|
|
373
|
+
|
|
374
|
+
- [Model Tutorial](/developer/tutorial/model) - Creating the Brand model
|
|
375
|
+
- [Admin Tutorial](/developer/tutorial/admin) - Building the admin interface
|
|
376
|
+
- [Events](/developer/core-concepts/events) - Subscribe to model changes without decorators
|
|
377
|
+
- [Webhooks](/developer/core-concepts/webhooks) - HTTP callbacks for external integrations
|
|
378
|
+
- [Decorators](/developer/customization/decorators) - Full decorator reference
|
|
379
|
+
- [Customization Overview](/developer/customization/quickstart) - More customization patterns
|
|
380
|
+
- [Products](/developer/core-concepts/products) - Product model documentation
|