mcpField
S
spree-customization

spree-customization

github.com/spree開発10230 回インストール4 週間前に更新
リポジトリ

spree-customization について

spree-customizationは、コアをフォークせずに拡張する方法を選ぶためのSpree 6開発者向けスキルです。設定、フック、イベント、プロバイダー、APIリソースなどのカスタマイズ手段を用途別に示します。

主な機能

  1. 01変更に適した拡張ポイントを選択
  2. 02ワークフローフックで処理を検証・変更
  3. 03サブスクライバーやWebhookでイベントに対応
  4. 04プロバイダーと認証戦略を設定
  5. 05リソース、ダッシュボード、モデルを拡張

活用シーン

  1. 01チェックアウト中にポリシーを適用
  2. 02注文イベントを外部システムへ送信
  3. 03APIリソースやダッシュボード機能を追加
  4. 04コアワークフローを独自の処理に置換

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/spree/agent-skills --skill spree-customization

インストール後、Claude Code で/spree-customizationを入力して読み込まれたか確認します。

詳細

Spree のカスタマイズ — コードはどこに配置すればよいですか?

Spree 6 は、フォークせずに拡張できるように構築されています。難しいのは、どのパターンを選ぶかです。表を上から順に確認してください。上位の選択肢はデータや設定で、実装コストが低く、アップグレードの影響を受けません。下位の選択肢は Spree の内部実装との結びつきが強くなります。

コマンドは spree CLI 形式を使用しています。従来の Rails アプリでは、アプリのルートから bin/rails … を使用し、server/ のパスプレフィックスを省いてください。詳細は spree-project を参照してください。

判断フロー

実現したいこと…使用する機能スキル
通貨、市場、言語、配送地域/配送方法、税率、決済方法、注文番号の採番を変更するストア設定 — ダッシュボード → Settings、または Admin API。データの変更であり、デプロイは不要ですspree-dashboard、spree-api-v3
インストール全体に適用される上限を調整する(パスワードの長さ、JWT の有効期限、レート制限、カートの有効期限、Webhook の有効/無効)SPREE_* 環境変数(起動時に型チェックと検証を実施)。Spree.config { |c| … } も引き続き config/initializers/spree.rb で使用できます—(ドキュメント:customization/configuration)
レコードタイプごとに型付き設定を持たせる(Brand の「featured」フラグ、計算機のレートなど)モデルの設定項目 — preferences 列を持つモデルに preference :featured, :boolean, default: false を追加しますspree-resource
操作を拒否する(購入上限、地域ポリシー、B2B の利用資格)ワークフローの validate フック — Spree.hooks.register('carts.add_item.validate', 'MyApp::CheckLimit') を登録し、ハンドラーで workflow.reject! を呼び出しますspree-workflows
コアフローの内部でコードを実行する(確定後、キャンセル前、価格/プロバイダーのコンテキストを渡す)ワークフローのライフサイクル/コンテキストフック — carts.complete.after_finalize、orders.cancel.before_cancel、set_*_contextspree-workflows
アプリ内で何かが起きた後に処理を行う(インデックス作成、同期、メール送信、監査)イベントサブスクライバー — order.placed、product.updated などを Spree::Subscriber で処理しますspree-events-webhooks
Ruby を使わずに、外部システムへイベントを通知するWebhook エンドポイント — ダッシュボード → Settings → Webhooks、または Admin APIspree-events-webhooks
チェックアウト完了前に何かを必須にする(PO 番号、電話番号、年齢確認)、またはチェックアウトのステップを追加するSpree::Checkout::Registry.add_requirement / register_step — Cart API の requirements[] に反映されますspree-checkout
税金、配送レート、フルフィルメント、検索、価格設定、在庫、売上金の支払い、デジタル商品の配送、決済の仕組みを置き換えるプロバイダー — クラスを登録し(Spree.tax_providers、Spree.delivery_rate_providers、Spree.search_provider = …、Spree.payment_methods など)、ストア/市場/方法に設定しますspree-providers、spree-taxes、spree-payments
独自の IdP またはトークンで顧客/スタッフを認証する認証ストラテジー — Spree.store_authentication_strategies.add(:idp, MyApp::Auth::IdpStrategy)(admin_/seller_ も使用可能)spree-auth-permissions
販売者向けの追加データを保存する(素材、お手入れ方法、ギフトメッセージ)カスタムフィールド — CustomFieldDefinition と record.set_custom_field('custom.material', 'Cotton') を使用します。型付きで、フィルタリング可能、storefront_visible に対応していますspree-catalog
連携/システム用データを保存する(ERP ID、UTM ソース、同期状態)metadata — スキーマレス JSON。マージ方式で、Store API では書き込み専用、Admin API では読み取り可能ですspree-api-v3
q[...] で属性をフィルタリング/ソート可能にするSpree.ransack.add_attribute / add_association / add_scopespree-api-v3
住所ルールを追加/削除する(私書箱、電話番号形式)Spree.validators.addresses.register / unregister(config.to_prepare から使用)spree-customization(下記)
新しい列を Admin API 経由で書き込み可能にするSpree::Product.additional_permitted_attributes += [:brand_id]spree-resource
スタッフのロール/API キーのスコープで新しいリソースへのアクセスを制限するSpree.permissions.register_scope(:brands, group: :catalog, resources: -> { [Spree::Brand] }) → read_brands / write_brandsspree-auth-permissions
新しいモデルと Admin/Store エンドポイント一式を追加するspree generate api_resource Brand …spree-resource
コアモデルにステータスを追加するSpree::Return.add_status('on_hold', after: 'approved') + レコードをそのステータスに移行する独自のワークフローspree-workflows
コアフローの根本的な動作を変更する依存関係の差し替え — Spree::Dependencies.cart_add_item_workflow = 'MyApp::Carts::AddItem'spree-dependencies
コアモデルに関連付け、スコープ、ヘルパーメソッドを追加するデコレーター(spree generate model_decorator Spree::Product)— 最終手段spree-decorators
管理画面、フィールド、列、ナビゲーション項目を追加するapps/dashboard/src/plugins.ts のダッシュボードプラグイン(defineDashboardPlugin)spree-dashboard、spree-dashboard-plugins
上記の機能をアプリ間で共有する拡張 gem(Rails エンジン)+任意でダッシュボードプラグイン(spree plugin new)spree-extensions
フック、イベント、それとも依存関係?
  1. 処理をブロックしたり、結果を変更したりする必要がありますか? → ワークフローフックです。フローを停止できるのは validate フック(および before_cancel フック)のみで、いずれもお金が動く前に実行されます。
  2. 発生したことを把握するだけでよいですか? → イベントサブスクライバー(アプリ内)または Webhook(外部)です。疎結合で、デフォルトでは非同期に動作し、アップグレード後も変更せずに使えます。
  3. フローを根本的に変える必要がありますか? → 依存関係の差し替えです。そのクラスをアップグレードのたびに自分で管理することになります。

実例

地域ごとにチェックアウトをブロックする — validate フック
# server/config/initializers/spree.rb
Spree.hooks.register('carts.complete.validate', 'MyApp::RegionPolicy')

# server/app/services/my_app/region_policy.rb
module MyApp
  class RegionPolicy
    def call(workflow)
      return unless workflow.cart.ship_address&.country_code == 'KP'

      workflow.reject!('We cannot deliver to this region')
    end
  end
end

フックキーは起動時に検証されます(タイプミスがあると Spree::Hooks::UnknownHookError が発生します)。ハンドラーはクラス名の文字列として保存されるため、再読み込み後も安全に登録されます。複数のハンドラーでキーを共有でき、最初に実行された reject! が優先されます。

注文確定を ERP に送信する — イベントサブスクライバー
spree generate subscriber ErpOrderSync order.placed   # creates the class AND registers it
# server/app/subscribers/erp_order_sync_subscriber.rb
class ErpOrderSyncSubscriber < Spree::Subscriber
  subscribes_to 'order.placed'

  def handle(event)
    order = Spree::Order.find_by_prefix_id(event.payload['id'])
    MyApp::Erp.push(order) if order
  end
end

サブスクライバーは自動検出されません。ジェネレーターを使わない場合は、Spree.subscribers << ErpOrderSyncSubscriber を自分で追加してください。

B2B カートで PO 番号を必須にする — チェックアウト要件
Spree::Checkout::Registry.add_requirement(
  step: :payment,
  field: :po_number,
  message: 'PO number is required',
  satisfied: ->(cart) { cart.metadata['po_number'].present? },
  applicable: ->(cart) { cart.company.present? }
)

この要件は Cart API の requirements 配列に表示されるため、ストアフロントではルールを重複させずに描画できます。また、要件が満たされるまでは Carts::Complete による完了処理が拒否されます。

商品に brand_id を追加する — リソース+許可属性+デコレーター
spree generate api_resource Brand name:string
spree generate migration AddBrandIdToSpreeProducts brand_id:bigint:index
spree migrate
# server/config/initializers/spree.rb
Rails.application.config.to_prepare do
  Spree::Product.additional_permitted_attributes += [:brand_id]   # += — the default is frozen
  Spree::Product.storefront_ransackable_associations |= %w[brand]  # Store API filters follow only this list
end
Spree.ransack.add_association(Spree::Product, :brand)            # Admin API filters
Spree.permissions.register_scope(:brands, group: :catalog, resources: -> { [Spree::Brand] })

# server/app/models/spree/product_decorator.rb — structural addition, the one legit decorator job
module Spree
  module ProductDecorator
    def self.prepended(base)
      base.belongs_to :brand, class_name: 'Spree::Brand', optional: true
    end
  end
  Product.prepend(ProductDecorator)
end

データをマーチャントが編集でき、独自のテーブルが不要であれば、上記の方法よりカスタムフィールドのほうが手間がかかりません。

出荷先住所のルールを緩和する — バリデーターレジストリー
# server/config/initializers/spree.rb
Rails.application.config.to_prepare do
  Spree.validators.addresses.register(MyApp::PostBoxValidator)          # an ActiveModel::Validator
  Spree.validators.addresses.unregister(Spree::Addresses::PhoneValidator)
end

いくつかのルールは、単なるストア設定でもあります(address_requires_phone、company_field_enabled、disable_sku_validation)。まずこれらを確認してください。その他のモデル向けに汎用的なバリデーターレジストリーはありません。モデルバリデーションをデコレーターで追加し、操作を拒否するには validate フックを使ってください。

カートへの商品追加処理を置き換える — 依存関係の差し替え
Spree::Dependencies.cart_add_item_workflow = 'MyApp::Carts::AddItem'   # subclass Spree::Carts::AddItem

ワークフローのキーは _workflow で終わります(carts_complete_workflow、order_cancel_workflow、cart_recalculate_totals_workflow、fulfillment_update_workflow など)。通常のサービスのキーは _service で終わります(order_update_statuses_service)。動作を追加するだけであれば、フックを優先してください。

アンチパターン

  • デコレーターで finalize!、after_finalize、キャンセルなどのライフサイクルメソッドをオーバーライドする → フックを登録してください(carts.complete.after_finalize、orders.cancel.after_cancel)。フック可能なステートマシンはもうありません。state_machine ブロック、after_transition、checkout_flow は存在しません。
  • 副作用を起こすために Spree モデルへ after_save/after_commit コールバックを追加する → イベントサブスクライバーを使ってください。
  • clear_validators! → 他の拡張機能のものも含め、モデルのすべてのバリデーションが消去されます。ストア設定、Spree.validators.addresses.unregister、または対象を絞ったデコレーターを使ってください。
  • Spree::Product.additional_permitted_attributes << :brand_id → FrozenError が発生します。必ず += を使ってください。
  • 従来の *_service 依存関係キー(cart_add_item_service、carts_complete_service)を書く → 書き込みは保留され、警告とともに無視されます。*_workflow キーを使ってください。
  • payment_status/fulfillment_status を直接設定する → これらは Spree::Orders::UpdateStatuses によって算出されます。代わりに支払い/フルフィルメントを操作してください。
  • 新しいステータスフィールドに enum やステートマシンを使う → include Spree::HasStatus; has_status :a, :b, default: :a を使い、ワークフロー内で遷移を定義してください。
  • Rails のビュー/パーシャルを使って管理画面を変更する → 管理画面は React ダッシュボードです。ダッシュボードプラグインを使ってください。
  • 1つのアプリ用に非公開の拡張 gem を作る → コードは server/app/ に置いてください。gem は複数のアプリで共有するためのものです。
  • Spree をフォークする → まずこの表を確認してください。ほぼ間違いなく適切な拡張ポイントがあります。

さらに詳しく読むには

  • node_modules/@spree/docs/dist/developer/customization/quickstart.md — 推奨される標準的な順序
  • …/customization/{configuration,workflows,dependencies,decorators,validations,metadata,model-preferences,permissions,checkout,api}.md
  • 各プロバイダーの仕様については、…/providers/overview.md と …/how-to/custom-*.md
  • 管理者向け UI については、…/dashboard/customization/quickstart.md
  • オンライン: https://spreecommerce.org/docs/developer/customization/quickstart

コメント