@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.
Files changed (183) hide show
  1. package/README.md +54 -0
  2. package/dist/api-reference/platform/authentication.md +38 -0
  3. package/dist/api-reference/store-api/authentication.md +188 -0
  4. package/dist/api-reference/store-api/errors.md +277 -0
  5. package/dist/api-reference/store-api/idempotency.md +129 -0
  6. package/dist/api-reference/store-api/introduction.md +34 -0
  7. package/dist/api-reference/store-api/localization.md +279 -0
  8. package/dist/api-reference/store-api/metadata.md +160 -0
  9. package/dist/api-reference/store-api/monetary-amounts.md +65 -0
  10. package/dist/api-reference/store-api/querying.md +399 -0
  11. package/dist/api-reference/store-api/rate-limitting.md +103 -0
  12. package/dist/api-reference/store-api/relations.md +185 -0
  13. package/dist/api-reference/storefront/authentication.md +88 -0
  14. package/dist/api-reference/tutorials/adyen-integration-guide-for-android.md +165 -0
  15. package/dist/api-reference/tutorials/adyen-integration-guide-for-ios.md +194 -0
  16. package/dist/api-reference/tutorials/quick-checkout-with-stripe.md +248 -0
  17. package/dist/api-reference/v2/fetching-multiple-resources.md +26 -0
  18. package/dist/api-reference/v2/filtering-and-sorting.md +53 -0
  19. package/dist/api-reference/v2/introduction.md +22 -0
  20. package/dist/api-reference/v2/pagination.md +37 -0
  21. package/dist/api-reference/webhooks-events.md +883 -0
  22. package/dist/developer/admin/admin.md +205 -0
  23. package/dist/developer/admin/authentication.md +59 -0
  24. package/dist/developer/admin/components.md +711 -0
  25. package/dist/developer/admin/custom-css.md +243 -0
  26. package/dist/developer/admin/custom-javascript.md +116 -0
  27. package/dist/developer/admin/extending-ui.md +1964 -0
  28. package/dist/developer/admin/form-builder.md +444 -0
  29. package/dist/developer/admin/helper-methods.md +531 -0
  30. package/dist/developer/admin/navigation.md +805 -0
  31. package/dist/developer/admin/tables.md +491 -0
  32. package/dist/developer/advanced/adding_spree_to_rails_app.md +106 -0
  33. package/dist/developer/cli/quickstart.md +137 -0
  34. package/dist/developer/contributing/creating-an-extension.md +258 -0
  35. package/dist/developer/contributing/developing-spree.md +339 -0
  36. package/dist/developer/contributing/quickstart.md +32 -0
  37. package/dist/developer/contributing/updating-extensions.md +67 -0
  38. package/dist/developer/core-concepts/addresses.md +265 -0
  39. package/dist/developer/core-concepts/adjustments.md +107 -0
  40. package/dist/developer/core-concepts/architecture.md +177 -0
  41. package/dist/developer/core-concepts/calculators.md +323 -0
  42. package/dist/developer/core-concepts/customers.md +230 -0
  43. package/dist/developer/core-concepts/events.md +624 -0
  44. package/dist/developer/core-concepts/imports-exports.md +698 -0
  45. package/dist/developer/core-concepts/inventory.md +191 -0
  46. package/dist/developer/core-concepts/markets.md +250 -0
  47. package/dist/developer/core-concepts/media.md +167 -0
  48. package/dist/developer/core-concepts/metafields.md +187 -0
  49. package/dist/developer/core-concepts/orders.md +328 -0
  50. package/dist/developer/core-concepts/payments.md +710 -0
  51. package/dist/developer/core-concepts/pricing.md +163 -0
  52. package/dist/developer/core-concepts/products.md +360 -0
  53. package/dist/developer/core-concepts/promotions.md +322 -0
  54. package/dist/developer/core-concepts/reports.md +206 -0
  55. package/dist/developer/core-concepts/search-filtering.md +237 -0
  56. package/dist/developer/core-concepts/shipments.md +212 -0
  57. package/dist/developer/core-concepts/slugs.md +111 -0
  58. package/dist/developer/core-concepts/staff-roles.md +123 -0
  59. package/dist/developer/core-concepts/store-credits-gift-cards.md +317 -0
  60. package/dist/developer/core-concepts/stores.md +117 -0
  61. package/dist/developer/core-concepts/taxes.md +135 -0
  62. package/dist/developer/core-concepts/translations.md +120 -0
  63. package/dist/developer/core-concepts/users.md +299 -0
  64. package/dist/developer/core-concepts/webhooks.md +378 -0
  65. package/dist/developer/create-spree-app/quickstart.md +158 -0
  66. package/dist/developer/customization/api.md +93 -0
  67. package/dist/developer/customization/authentication.md +88 -0
  68. package/dist/developer/customization/checkout.md +204 -0
  69. package/dist/developer/customization/configuration.md +55 -0
  70. package/dist/developer/customization/decorators.md +523 -0
  71. package/dist/developer/customization/dependencies.md +232 -0
  72. package/dist/developer/customization/emails.md +21 -0
  73. package/dist/developer/customization/extensions.md +92 -0
  74. package/dist/developer/customization/metadata.md +236 -0
  75. package/dist/developer/customization/model-preferences.md +130 -0
  76. package/dist/developer/customization/permissions.md +265 -0
  77. package/dist/developer/customization/quickstart.md +229 -0
  78. package/dist/developer/customization/routes.md +24 -0
  79. package/dist/developer/customization/v4/admin-panel.md +78 -0
  80. package/dist/developer/customization/v4/authentication.md +210 -0
  81. package/dist/developer/customization/v4/checkout.md +212 -0
  82. package/dist/developer/customization/v4/deface.md +251 -0
  83. package/dist/developer/customization/v4/images.md +86 -0
  84. package/dist/developer/customization/v4/storefront.md +450 -0
  85. package/dist/developer/deployment/assets.md +87 -0
  86. package/dist/developer/deployment/aws.md +335 -0
  87. package/dist/developer/deployment/caching.md +27 -0
  88. package/dist/developer/deployment/cdn.md +39 -0
  89. package/dist/developer/deployment/database.md +155 -0
  90. package/dist/developer/deployment/docker.md +128 -0
  91. package/dist/developer/deployment/emails.md +77 -0
  92. package/dist/developer/deployment/environment_variables.md +111 -0
  93. package/dist/developer/deployment/heroku.md +51 -0
  94. package/dist/developer/deployment/render.md +95 -0
  95. package/dist/developer/getting-started/quickstart.md +82 -0
  96. package/dist/developer/how-to/custom-payment-method.md +374 -0
  97. package/dist/developer/how-to/custom-promotion.md +373 -0
  98. package/dist/developer/how-to/custom-report.md +387 -0
  99. package/dist/developer/how-to/custom-search-provider.md +230 -0
  100. package/dist/developer/multi-store/quickstart.md +71 -0
  101. package/dist/developer/multi-store/setup.md +38 -0
  102. package/dist/developer/multi-tenant/configuration.md +41 -0
  103. package/dist/developer/multi-tenant/core-concepts.md +75 -0
  104. package/dist/developer/multi-tenant/installation.md +96 -0
  105. package/dist/developer/multi-tenant/quickstart.md +20 -0
  106. package/dist/developer/multi-vendor/installation.md +45 -0
  107. package/dist/developer/multi-vendor/quickstart.md +17 -0
  108. package/dist/developer/sdk/admin/quickstart.md +22 -0
  109. package/dist/developer/sdk/authentication.md +89 -0
  110. package/dist/developer/sdk/configuration.md +225 -0
  111. package/dist/developer/sdk/quickstart.md +82 -0
  112. package/dist/developer/sdk/store/account.md +67 -0
  113. package/dist/developer/sdk/store/cart-checkout.md +140 -0
  114. package/dist/developer/sdk/store/markets.md +151 -0
  115. package/dist/developer/sdk/store/payments.md +96 -0
  116. package/dist/developer/sdk/store/products.md +149 -0
  117. package/dist/developer/sdk/store/wishlists.md +52 -0
  118. package/dist/developer/security/pci_compliance.md +15 -0
  119. package/dist/developer/security/security_policy.md +68 -0
  120. package/dist/developer/storefront/blocks.md +285 -0
  121. package/dist/developer/storefront/custom-css.md +260 -0
  122. package/dist/developer/storefront/custom-javascript.md +166 -0
  123. package/dist/developer/storefront/helper-methods.md +1288 -0
  124. package/dist/developer/storefront/links.md +298 -0
  125. package/dist/developer/storefront/nextjs/architecture.md +150 -0
  126. package/dist/developer/storefront/nextjs/customization.md +141 -0
  127. package/dist/developer/storefront/nextjs/deployment.md +180 -0
  128. package/dist/developer/storefront/nextjs/quickstart.md +92 -0
  129. package/dist/developer/storefront/nextjs/spree-next-package.md +314 -0
  130. package/dist/developer/storefront/pages.md +163 -0
  131. package/dist/developer/storefront/sections.md +569 -0
  132. package/dist/developer/storefront/storefront.md +56 -0
  133. package/dist/developer/storefront/themes.md +161 -0
  134. package/dist/developer/tutorial/admin.md +134 -0
  135. package/dist/developer/tutorial/extending-models.md +380 -0
  136. package/dist/developer/tutorial/file-uploads.md +121 -0
  137. package/dist/developer/tutorial/introduction.md +33 -0
  138. package/dist/developer/tutorial/model.md +41 -0
  139. package/dist/developer/tutorial/page-builder.md +487 -0
  140. package/dist/developer/tutorial/rich-text.md +73 -0
  141. package/dist/developer/tutorial/seo.md +332 -0
  142. package/dist/developer/tutorial/storefront.md +352 -0
  143. package/dist/developer/tutorial/testing.md +558 -0
  144. package/dist/developer/upgrades/2.0-to-2.1.md +46 -0
  145. package/dist/developer/upgrades/2.1-to-2.2.md +59 -0
  146. package/dist/developer/upgrades/2.2-to-2.3.md +44 -0
  147. package/dist/developer/upgrades/2.3-to-2.4.md +42 -0
  148. package/dist/developer/upgrades/3.0-to-3.1.md +47 -0
  149. package/dist/developer/upgrades/3.1-to-3.2.md +34 -0
  150. package/dist/developer/upgrades/3.2-to-3.3.md +70 -0
  151. package/dist/developer/upgrades/3.3-to-3.4.md +36 -0
  152. package/dist/developer/upgrades/3.4-to-3.5.md +44 -0
  153. package/dist/developer/upgrades/3.5-to-3.6.md +40 -0
  154. package/dist/developer/upgrades/3.6-to-3.7.md +62 -0
  155. package/dist/developer/upgrades/3.7-to-4.0.md +152 -0
  156. package/dist/developer/upgrades/4.0-to-4.1.md +92 -0
  157. package/dist/developer/upgrades/4.1-to-4.2.md +109 -0
  158. package/dist/developer/upgrades/4.10-to-5.0.md +129 -0
  159. package/dist/developer/upgrades/4.2-to-4.3.md +100 -0
  160. package/dist/developer/upgrades/4.3-to-4.4.md +125 -0
  161. package/dist/developer/upgrades/4.4-to-4.5.md +94 -0
  162. package/dist/developer/upgrades/4.5-to-4.6.md +119 -0
  163. package/dist/developer/upgrades/4.6-to-4.7.md +39 -0
  164. package/dist/developer/upgrades/4.8-to-4.9.md +24 -0
  165. package/dist/developer/upgrades/4.9-to-4.10.md +24 -0
  166. package/dist/developer/upgrades/4.x-to-4.8.md +52 -0
  167. package/dist/developer/upgrades/5.0-to-5.1.md +28 -0
  168. package/dist/developer/upgrades/5.1-to-5.2.md +127 -0
  169. package/dist/developer/upgrades/5.2-to-5.3.md +338 -0
  170. package/dist/developer/upgrades/5.3-to-5.4.md +248 -0
  171. package/dist/developer/upgrades/quickstart.md +36 -0
  172. package/dist/integrations/analytics/google-analytics.md +64 -0
  173. package/dist/integrations/analytics/google-tag-manager.md +78 -0
  174. package/dist/integrations/integrations.md +39 -0
  175. package/dist/integrations/marketing/klaviyo.md +99 -0
  176. package/dist/integrations/payments/adyen.md +90 -0
  177. package/dist/integrations/payments/paypal.md +41 -0
  178. package/dist/integrations/payments/razorpay.md +45 -0
  179. package/dist/integrations/payments/stripe.md +109 -0
  180. package/dist/integrations/search/meilisearch.md +236 -0
  181. package/dist/integrations/sso-mfa-social-login/admin-dashboard.md +57 -0
  182. package/dist/integrations/sso-mfa-social-login/storefront.md +56 -0
  183. 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