mcpField
P
product-type-development

product-type-development

github.com/bagisto開発13221 回インストール2 週間前に更新
リポジトリ

product-type-development について

Bagisto 開発者向けの製品タイプ開発ガイドです。カスタムタイプの登録と動作の拡張方法を説明し、販売可否、在庫、価格、検証、管理画面、カート処理を扱います。

主な機能

  1. 01カスタム製品タイプの設定と登録
  2. 02AbstractType による製品動作の拡張
  3. 03販売可否と在庫ルールの設定
  4. 04価格とタイプ別バリデーションの定義
  5. 05管理画面とカートデータのカスタマイズ

活用シーン

  1. 01Bagisto でサブスクリプション製品を実装
  2. 02サービス製品に専用価格と検証を追加
  3. 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() でカートデータを正しく処理できていない

コメント