product-type-development

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Product Type Development in Bagisto

Bagisto中的产品类型开发

Overview

概述

Creating custom product types in Bagisto allows you to define specialized product behaviors that match your business needs. Whether you need subscription products, rental items, digital services, or complex product variations, custom product types provide the flexibility to create exactly what your store requires.
在Bagisto中创建自定义产品类型,能够让你定义符合业务需求的专属产品行为。无论你需要订阅型产品、租赁物品、数字服务还是复杂的产品变体,自定义产品类型都能为你的店铺提供足够的灵活性,实现你所需的功能。

When to Apply

适用场景

Activate this skill when:
  • Creating new product types in Bagisto
  • Building subscription or service-based products
  • Implementing custom product behaviors
  • Adding type-specific validation and pricing
  • Modifying inventory/stock handling

在以下场景中使用该技能:
  • 在Bagisto中创建新的产品类型
  • 构建订阅型或服务类产品
  • 实现自定义产品行为
  • 添加类型专属的验证规则与定价逻辑
  • 修改库存/库存处理方式

@config: Product Type Configuration

@config: 产品类型配置

Basic Configuration Structure

基础配置结构

The
Config/product-types.php
file is a simple PHP array that registers your product type:
php
<?php

return [
    'subscription' => [
        'key'   => 'subscription',
        'name'  => 'Subscription',
        'class' => 'Webkul\SubscriptionProduct\Type\Subscription',
        'sort'  => 5,
    ],
];
Config/product-types.php
文件是一个简单的PHP数组,用于注册你的产品类型:
php
<?php

return [
    'subscription' => [
        'key'   => 'subscription',
        'name'  => 'Subscription',
        'class' => 'Webkul\SubscriptionProduct\Type\Subscription',
        'sort'  => 5,
    ],
];

Required Configuration Properties

必填配置属性

PropertyDescriptionExample
key
Unique identifier (matches array key)
'subscription'
name
Display name in admin dropdown
'Subscription'
class
Full namespace to your product type class
'Webkul\SubscriptionProduct\Type\Subscription'
sort
Order in dropdown (optional, default: 0)
5
属性说明示例
key
唯一标识符(需与数组键名一致)
'subscription'
name
后台下拉菜单中显示的名称
'Subscription'
class
产品类型类的完整命名空间
'Webkul\SubscriptionProduct\Type\Subscription'
sort
下拉菜单中的排序顺序(可选,默认值:0)
5

How Bagisto Uses This Configuration

Bagisto如何使用该配置

1. Admin Product Creation

1. 后台产品创建

  • Reads all registered product types from configuration
  • Shows them in the "Product Type" dropdown
  • Uses the
    name
    for display and
    sort
    for ordering
  • 从配置中读取所有已注册的产品类型
  • 在“产品类型”下拉菜单中展示
  • 使用
    name
    作为显示名称,
    sort
    控制排序

2. Product Type Instantiation

2. 产品类型实例化

  • Looks up the product's type using the
    key
  • Creates an instance of the
    class
  • Calls methods on that instance for product behavior
  • 通过
    key
    查找产品对应的类型
  • 创建
    class
    对应的实例
  • 调用该实例的方法来实现产品行为

3. Configuration Loading

3. 配置加载

Your service provider merges your configuration:
php
public function register(): void
{
    $this->mergeConfigFrom(
        dirname(__DIR__) . '/Config/product-types.php',
        'product_types'
    );
}
你的服务提供者需要合并配置:
php
public function register(): void
{
    $this->mergeConfigFrom(
        dirname(__DIR__) . '/Config/product-types.php',
        'product_types'
    );
}

Multiple Product Types

多产品类型配置

php
<?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,
    ],
];

php
<?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 Methods

@abstract: AbstractType方法

AbstractType Overview

AbstractType概述

Every product type in Bagisto extends the
AbstractType
class:
php
<?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 = [];
}
Bagisto中的每个产品类型都继承自
AbstractType
类:
php
<?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 = [];
}

Key Methods to Override

需重写的核心方法

Product Availability Control

产品可用性控制

isSaleable(): bool

isSaleable(): bool

Controls whether the product appears as purchasable:
php
public function isSaleable(): bool
{
    if (! parent::isSaleable()) {
        return false;
    }
    
    // Add custom availability logic
    return true;
}
控制产品是否显示为可购买状态:
php
public function isSaleable(): bool
{
    if (! parent::isSaleable()) {
        return false;
    }
    
    // 添加自定义可用性逻辑
    return true;
}

haveSufficientQuantity(int $qty): bool

haveSufficientQuantity(int $qty): bool

Checks if enough quantity is available:
php
public function haveSufficientQuantity(int $qty): bool
{
    return true; // Custom logic based on subscription slots
}
检查是否有足够的库存数量:
php
public function haveSufficientQuantity(int $qty): bool
{
    return true; // 基于订阅名额的自定义逻辑
}

Inventory and Stock Control

库存控制

isStockable(): bool

isStockable(): bool

Determines if the product uses inventory tracking:
php
public function isStockable(): bool
{
    return false; // Subscriptions don't use traditional inventory
}
判断产品是否启用库存追踪:
php
public function isStockable(): bool
{
    return false; // 订阅产品不使用传统库存
}

totalQuantity(): int

totalQuantity(): int

Returns total available quantity:
php
public function totalQuantity(): int
{
    return $this->product->subscription_slots ?? 0;
}
返回可用的总数量:
php
public function totalQuantity(): int
{
    return $this->product->subscription_slots ?? 0;
}

User Interface Control

用户界面控制

showQuantityBox(): bool

showQuantityBox(): bool

Controls whether quantity input appears:
php
public function showQuantityBox(): bool
{
    return true;
}
控制是否显示数量输入框:
php
public function showQuantityBox(): bool
{
    return true;
}

Pricing Methods

定价方法

getProductPrices(): array

getProductPrices(): array

Returns structured pricing data:
php
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),
        ],
    ];
}
返回结构化的定价数据:
php
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

getPriceHtml(): string

Generates price HTML for display:
php
public function getPriceHtml(): string
{
    return view('subscription::products.prices.subscription', [
        'product' => $this->product,
        'prices' => $this->getProductPrices(),
    ])->render();
}
生成用于展示的价格HTML:
php
public function getPriceHtml(): string
{
    return view('subscription::products.prices.subscription', [
        'product' => $this->product,
        'prices' => $this->getProductPrices(),
    ])->render();
}

Validation Methods

验证方法

getTypeValidationRules(): array

getTypeValidationRules(): array

Returns validation rules for product type specific fields:
php
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',
    ];
}
返回产品类型专属字段的验证规则:
php
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',
    ];
}

Admin Interface Customization

后台界面自定义

$additionalViews
Property

$additionalViews
属性

Specifies additional blade views in product edit page:
php
protected $additionalViews = [
    'subscription::admin.catalog.products.edit.subscription-settings',
    'subscription::admin.catalog.products.edit.subscription-pricing',
];
指定产品编辑页面中额外加载的Blade视图:
php
protected $additionalViews = [
    'subscription::admin.catalog.products.edit.subscription-settings',
    'subscription::admin.catalog.products.edit.subscription-pricing',
];

$skipAttributes
Property

$skipAttributes
属性

Specifies which attributes to skip:
php
protected $skipAttributes = [
    'weight',
    'dimensions',
];
指定需要跳过的属性:
php
protected $skipAttributes = [
    'weight',
    'dimensions',
];

Cart Integration

购物车集成

prepareForCart(array $data): array

prepareForCart(array $data): array

Processes product data before adding to cart:
php
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;
}

在添加到购物车前处理产品数据:
php
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: Complete Subscription Implementation

@build: 完整订阅功能实现

Package Structure

包结构

packages/Webkul/SubscriptionProduct/
└── src/
    ├── Type/
    │   └── Subscription.php
    ├── Config/
    │   └── product-types.php
    └── Providers/
        └── SubscriptionServiceProvider.php
packages/Webkul/SubscriptionProduct/
└── src/
    ├── Type/
    │   └── Subscription.php
    ├── Config/
    │   └── product-types.php
    └── Providers/
        └── SubscriptionServiceProvider.php

Step 1: Create Package Structure

步骤1:创建包结构

bash
mkdir -p packages/Webkul/SubscriptionProduct/src/{Type,Config,Providers}
bash
mkdir -p packages/Webkul/SubscriptionProduct/src/{Type,Config,Providers}

Step 2: Configure Product Type

步骤2:配置产品类型

File:
packages/Webkul/SubscriptionProduct/src/Config/product-types.php
php
<?php

return [
    'subscription' => [
        'key'   => 'subscription',
        'name'  => 'Subscription',
        'class' => 'Webkul\SubscriptionProduct\Type\Subscription',
        'sort'  => 5,
    ],
];
文件路径:
packages/Webkul/SubscriptionProduct/src/Config/product-types.php
php
<?php

return [
    'subscription' => [
        'key'   => 'subscription',
        'name'  => 'Subscription',
        'class' => 'Webkul\SubscriptionProduct\Type\Subscription',
        'sort'  => 5,
    ],
];

Step 3: Create Product Type Class

步骤3:创建产品类型类

File:
packages/Webkul/SubscriptionProduct/src/Type/Subscription.php
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;
    }
}
文件路径:
packages/Webkul/SubscriptionProduct/src/Type/Subscription.php
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;
    }
}

Step 4: Create Service Provider

步骤4:创建服务提供者

File:
packages/Webkul/SubscriptionProduct/src/Providers/SubscriptionServiceProvider.php
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
    {
        //
    }
}
文件路径:
packages/Webkul/SubscriptionProduct/src/Providers/SubscriptionServiceProvider.php
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
    {
        //
    }
}

Step 5: Register Your Package

步骤5:注册你的包

Update composer.json

更新composer.json

json
{
    "autoload": {
        "psr-4": {
            "Webkul\\SubscriptionProduct\\": "packages/Webkul/SubscriptionProduct/src"
        }
    }
}
json
{
    "autoload": {
        "psr-4": {
            "Webkul\\SubscriptionProduct\\": "packages/Webkul/SubscriptionProduct/src"
        }
    }
}

Update autoloader

更新自动加载器

bash
composer dump-autoload
bash
composer dump-autoload

Register service provider

注册服务提供者

In
bootstrap/providers.php
:
php
<?php

return [
    App\Providers\AppServiceProvider::class,
    
    // ... other providers ...
    
    Webkul\SubscriptionProduct\Providers\SubscriptionServiceProvider::class,
];
bootstrap/providers.php
中添加:
php
<?php

return [
    App\Providers\AppServiceProvider::class,
    
    // ... 其他服务提供者 ...
    
    Webkul\SubscriptionProduct\Providers\SubscriptionServiceProvider::class,
];

Clear cache

清除缓存

bash
php artisan optimize:clear
bash
php artisan optimize:clear

Testing

测试

bash
php artisan tinker
bash
php artisan tinker

Test product type

测试产品类型

$product = \Webkul\Product\Models\Product::where('type', 'subscription')->first() $subscription = $product->getTypeInstance()
$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
$subscription->isStockable() // 应返回false $subscription->showQuantityBox() // 应返回true $subscription->isSaleable() // 应返回true

Test cart preparation

测试购物车数据准备

$cartData = $subscription->prepareForCart(['quantity' => 2, 'subscription_frequency' => 'monthly']) $cartData[0]['additional'] // Should show subscription data
undefined
$cartData = $subscription->prepareForCart(['quantity' => 2, 'subscription_frequency' => 'monthly']) $cartData[0]['additional'] // 应显示订阅相关数据
undefined

Built-in Product Types Reference

内置产品类型参考

TypeUse CaseKey Features
SimpleBasic productsStandard pricing, inventory tracking
ConfigurableProducts with variationsVariant management, attribute-based pricing
VirtualNon-physical productsNo shipping required
GroupedRelated products sold togetherBundle pricing, component selection
类型适用场景核心特性
Simple基础产品标准定价、库存追踪
Configurable带变体的产品变体管理、基于属性的定价
Virtual非实体产品无需配送
Grouped关联产品捆绑销售套餐定价、组件选择

Key Files Reference

核心文件参考

FilePurpose
Config/product-types.php
Product type registration
Type/ProductType.php
Product type class
Providers/ServiceProvider.php
Package registration
packages/Webkul/Product/src/Type/AbstractType.php
Base class
文件用途
Config/product-types.php
产品类型注册
Type/ProductType.php
产品类型类
Providers/ServiceProvider.php
包注册
packages/Webkul/Product/src/Type/AbstractType.php
基类

Common Pitfalls

常见陷阱

  • Forgetting to merge config in service provider
  • Not matching
    $key
    with array key in configuration
  • Not registering service provider in
    bootstrap/providers.php
  • Forgetting to run
    composer dump-autoload
    after adding package
  • Not clearing cache after configuration changes
  • Forgetting to call
    parent::isSaleable()
    when overriding
  • Not handling cart data correctly in
    prepareForCart()
  • 忘记在服务提供者中合并配置
  • 配置中的
    $key
    与数组键名不匹配
  • 未在
    bootstrap/providers.php
    中注册服务提供者
  • 添加包后忘记执行
    composer dump-autoload
  • 配置变更后未清除缓存
  • 重写
    isSaleable()
    时忘记调用
    parent::isSaleable()
  • prepareForCart()
    中未正确处理购物车数据