product-type-development について
Bagisto 開発者向けの製品タイプ開発ガイドです。カスタムタイプの登録と動作の拡張方法を説明し、販売可否、在庫、価格、検証、管理画面、カート処理を扱います。
主な機能
- 01カスタム製品タイプの設定と登録
- 02AbstractType による製品動作の拡張
- 03販売可否と在庫ルールの設定
- 04価格とタイプ別バリデーションの定義
- 05管理画面とカートデータのカスタマイズ
活用シーン
- 01Bagisto でサブスクリプション製品を実装
- 02サービス製品に専用価格と検証を追加
- 03カスタム製品の在庫やカート処理を調整
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ bagisto/ agent-skills --skill product-type-developmentインストール後、Claude Code で/product-type-developmentを入力して読み込まれたか確認します。
詳細
Bagisto における商品タイプの開発
概要
Bagisto でカスタム商品タイプを作成すると、ビジネスニーズに合わせた独自の商品動作を定義できます。サブスクリプション商品、レンタル商品、デジタルサービス、複雑な商品のバリエーションなどが必要な場合、カスタム商品タイプを使えば、ストアに必要なものを柔軟に作成できます。
適用するタイミング
以下の場合にこのスキルを有効にします:
- Bagisto で新しい商品タイプを作成する
- サブスクリプション商品やサービスベースの商品を構築する
- カスタム商品動作を実装する
- タイプ固有の検証や価格設定を追加する
- 在庫の処理を変更する
@config: 商品タイプの設定
基本的な設定構造
Config/product-types.php ファイルは、商品タイプを登録するシンプルな PHP 配列です:
<?php
return [
'subscription' => [
'key' => 'subscription',
'name' => 'Subscription',
'class' => 'Webkul\SubscriptionProduct\Type\Subscription',
'sort' => 5,
],
];
必須の設定プロパティ
| プロパティ | 説明 | 例 |
|---|---|---|
key | 一意の識別子(配列キーと一致) | 'subscription' |
name | 管理画面のドロップダウンに表示する名前 | 'Subscription' |
class | 商品タイプクラスの完全な名前空間 | 'Webkul\SubscriptionProduct\Type\Subscription' |
sort | ドロップダウンでの順序(任意、デフォルト:0) | 5 |
Bagisto におけるこの設定の使用方法
1. 管理画面での商品作成
- 設定から登録済みの商品タイプをすべて読み込みます
- 「商品タイプ」ドロップダウンに表示します
- 表示には
nameを、並べ替えにはsortを使用します
2. 商品タイプのインスタンス化
keyを使って商品のタイプを検索しますclassのインスタンスを作成します- 商品の動作に関するメソッドをそのインスタンスで呼び出します
3. 設定の読み込み
サービスプロバイダーで設定をマージします:
public function register(): void
{
$this->mergeConfigFrom(
dirname(__DIR__) . '/Config/product-types.php',
'product_types'
);
}
複数の商品タイプ
<?php
return [
'subscription' => [
'key' => 'subscription',
'name' => 'Subscription',
'class' => 'Webkul\SubscriptionProduct\Type\Subscription',
'sort' => 5,
],
'rental' => [
'key' => 'rental',
'name' => 'Rental Product',
'class' => 'Webkul\RentalProduct\Type\Rental',
'sort' => 6,
],
];
@abstract: AbstractType のメソッド
AbstractType の概要
Bagisto のすべての商品タイプは AbstractType クラスを継承します:
<?php
namespace Webkul\Product\Type;
abstract class AbstractType
{
protected $product;
protected $isStockable = true;
protected $showQuantityBox = false;
protected $haveSufficientQuantity = true;
protected $canBeMovedFromWishlistToCart = true;
protected $additionalViews = [];
protected $skipAttributes = [];
}
オーバーライドする主なメソッド
商品の販売可否の制御
isSaleable(): bool
商品を購入可能な状態で表示するかどうかを制御します:
public function isSaleable(): bool
{
if (! parent::isSaleable()) {
return false;
}
// Add custom availability logic
return true;
}
haveSufficientQuantity(int $qty): bool
十分な数量があるかどうかを確認します:
public function haveSufficientQuantity(int $qty): bool
{
return true; // Custom logic based on subscription slots
}
在庫と数量の制御
isStockable(): bool
商品に在庫管理を適用するかどうかを判定します:
public function isStockable(): bool
{
return false; // Subscriptions don't use traditional inventory
}
totalQuantity(): int
利用可能な数量の合計を返します:
public function totalQuantity(): int
{
return $this->product->subscription_slots ?? 0;
}
ユーザーインターフェースの制御
showQuantityBox(): bool
数量入力欄を表示するかどうかを制御します:
public function showQuantityBox(): bool
{
return true;
}
価格設定メソッド
getProductPrices(): array
構造化された価格データを返します:
public function getProductPrices(): array
{
$basePrice = $this->product->price;
$discount = $this->product->subscription_discount ?? 0;
$finalPrice = $basePrice - ($basePrice * $discount / 100);
return [
'regular' => [
'price' => core()->convertPrice($basePrice),
'formatted_price' => core()->currency($basePrice),
],
'final' => [
'price' => core()->convertPrice($finalPrice),
'formatted_price' => core()->currency($finalPrice),
],
];
}
getPriceHtml(): string
表示用の価格 HTML を生成します:
public function getPriceHtml(): string
{
return view('subscription::products.prices.subscription', [
'product' => $this->product,
'prices' => $this->getProductPrices(),
])->render();
}
検証メソッド
getTypeValidationRules(): array
商品タイプ固有のフィールドに対する検証ルールを返します:
public function getTypeValidationRules(): array
{
return [
'subscription_frequency' => 'required|in:weekly,monthly,quarterly,yearly',
'subscription_discount' => 'nullable|numeric|min:0|max:100',
'subscription_duration' => 'nullable|integer|min:1',
'subscription_slots' => 'required|integer|min:1',
];
}
管理画面のカスタマイズ
$additionalViews プロパティ
商品編集ページに追加する Blade ビューを指定します:
protected $additionalViews = [
'subscription::admin.catalog.products.edit.subscription-settings',
'subscription::admin.catalog.products.edit.subscription-pricing',
];
$skipAttributes プロパティ
スキップする属性を指定します:
protected $skipAttributes = [
'weight',
'dimensions',
];
カートとの連携
prepareForCart(array $data): array
商品データをカートに追加する前に処理します:
public function prepareForCart(array $data): array
{
if (empty($data['subscription_frequency'])) {
return 'Please select subscription frequency.';
}
$cartData = parent::prepareForCart($data);
$cartData[0]['additional']['subscription_frequency'] = $data['subscription_frequency'];
$cartData[0]['additional']['subscription_start_date'] = $data['start_date'] ?? now()->addDays(1)->format('Y-m-d');
return $cartData;
}
@build: サブスクリプションの実装例
パッケージ構成
packages/Webkul/SubscriptionProduct/
└── src/
├── Type/
│ └── Subscription.php
├── Config/
│ └── product-types.php
└── Providers/
└── SubscriptionServiceProvider.php
ステップ 1:パッケージ構成を作成する
mkdir -p packages/Webkul/SubscriptionProduct/src/{Type,Config,Providers}
ステップ 2:商品タイプを設定する
ファイル: packages/Webkul/SubscriptionProduct/src/Config/product-types.php
<?php
return [
'subscription' => [
'key' => 'subscription',
'name' => 'Subscription',
'class' => 'Webkul\SubscriptionProduct\Type\Subscription',
'sort' => 5,
],
];
ステップ 3:商品タイプクラスを作成する
ファイル: packages/Webkul/SubscriptionProduct/src/Type/Subscription.php
<?php
namespace Webkul\SubscriptionProduct\Type;
use Webkul\Product\Helpers\Indexers\Price\Simple as SimpleIndexer;
use Webkul\Product\Type\AbstractType;
class Subscription extends AbstractType
{
public function getPriceIndexer()
{
return app(SimpleIndexer::class);
}
public function isStockable(): bool
{
return false;
}
public function showQuantityBox(): bool
{
return true;
}
public function isSaleable(): bool
{
if (! parent::isSaleable()) {
return false;
}
return true;
}
public function haveSufficientQuantity(int $qty): bool
{
return true;
}
public function totalQuantity(): int
{
return $this->product->subscription_slots ?? 0;
}
public function prepareForCart(array $data): array
{
if (empty($data['subscription_frequency'])) {
return 'Please select subscription frequency.';
}
$cartData = parent::prepareForCart($data);
$cartData[0]['additional']['subscription_frequency'] = $data['subscription_frequency'];
$cartData[0]['additional']['subscription_start_date'] = $data['start_date'] ?? now()->addDays(1)->format('Y-m-d');
return $cartData;
}
}
ステップ 4:サービスプロバイダーを作成する
ファイル: packages/Webkul/SubscriptionProduct/src/Providers/SubscriptionServiceProvider.php
<?php
namespace Webkul\SubscriptionProduct\Providers;
use Illuminate\Support\ServiceProvider;
class SubscriptionServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->mergeConfigFrom(
dirname(__DIR__) . '/Config/product-types.php',
'product_types'
);
}
public function boot(): void
{
//
}
}
ステップ 5:パッケージを登録する
composer.json を更新する
{
"autoload": {
"psr-4": {
"Webkul\\SubscriptionProduct\\": "packages/Webkul/SubscriptionProduct/src"
}
}
}
オートローダーを更新する
composer dump-autoload
サービスプロバイダーを登録する
bootstrap/providers.php 内で:
<?php
return [
App\Providers\AppServiceProvider::class,
// ... other providers ...
Webkul\SubscriptionProduct\Providers\SubscriptionServiceProvider::class,
];
キャッシュをクリアする
php artisan optimize:clear
テスト
php artisan tinker
# Test product type
>>> $product = \Webkul\Product\Models\Product::where('type', 'subscription')->first()
>>> $subscription = $product->getTypeInstance()
# Test methods
>>> $subscription->isStockable() // Should return false
>>> $subscription->showQuantityBox() // Should return true
>>> $subscription->isSaleable() // Should return true
# Test cart preparation
>>> $cartData = $subscription->prepareForCart(['quantity' => 2, 'subscription_frequency' => 'monthly'])
>>> $cartData[0]['additional'] // Should show subscription data
組み込み商品タイプのリファレンス
| タイプ | 用途 | 主な機能 |
|---|---|---|
| Simple | 基本的な商品 | 標準価格設定、在庫追跡 |
| Configurable | バリエーションのある商品 | バリエーション管理、属性ベースの価格設定 |
| Virtual | 物理的な実体のない商品 | 配送不要 |
| Grouped | 関連商品をまとめて販売 | バンドル価格設定、構成商品の選択 |
主要ファイルのリファレンス
| ファイル | 用途 |
|---|---|
Config/product-types.php | 商品タイプの登録 |
Type/ProductType.php | 商品タイプクラス |
Providers/ServiceProvider.php | パッケージの登録 |
packages/Webkul/Product/src/Type/AbstractType.php | 基底クラス |
よくある落とし穴
- サービスプロバイダーで設定をマージし忘れる
- 設定内の配列キーと
$keyが一致していない bootstrap/providers.phpでサービスプロバイダーを登録し忘れる- パッケージを追加した後に
composer dump-autoloadを実行し忘れる - 設定変更後にキャッシュをクリアし忘れる
- オーバーライド時に
parent::isSaleable()の呼び出しを忘れる prepareForCart()でカートデータを正しく処理できていない