product-type-development
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseProduct 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 file is a simple PHP array that registers your product type:
Config/product-types.phpphp
<?php
return [
'subscription' => [
'key' => 'subscription',
'name' => 'Subscription',
'class' => 'Webkul\SubscriptionProduct\Type\Subscription',
'sort' => 5,
],
];Config/product-types.phpphp
<?php
return [
'subscription' => [
'key' => 'subscription',
'name' => 'Subscription',
'class' => 'Webkul\SubscriptionProduct\Type\Subscription',
'sort' => 5,
],
];Required Configuration Properties
必填配置属性
| Property | Description | Example |
|---|---|---|
| Unique identifier (matches array key) | |
| Display name in admin dropdown | |
| Full namespace to your product type class | |
| Order in dropdown (optional, default: 0) | |
| 属性 | 说明 | 示例 |
|---|---|---|
| 唯一标识符(需与数组键名一致) | |
| 后台下拉菜单中显示的名称 | |
| 产品类型类的完整命名空间 | |
| 下拉菜单中的排序顺序(可选,默认值:0) | |
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 for display and
namefor orderingsort
- 从配置中读取所有已注册的产品类型
- 在“产品类型”下拉菜单中展示
- 使用作为显示名称,
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 class:
AbstractTypephp
<?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中的每个产品类型都继承自类:
AbstractTypephp
<?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(): boolisSaleable(): bool
isSaleable(): boolControls 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): boolhaveSufficientQuantity(int $qty): bool
haveSufficientQuantity(int $qty): boolChecks 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(): boolisStockable(): bool
isStockable(): boolDetermines 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(): inttotalQuantity(): int
totalQuantity(): intReturns 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(): boolshowQuantityBox(): bool
showQuantityBox(): boolControls whether quantity input appears:
php
public function showQuantityBox(): bool
{
return true;
}控制是否显示数量输入框:
php
public function showQuantityBox(): bool
{
return true;
}Pricing Methods
定价方法
getProductPrices(): array
getProductPrices(): arraygetProductPrices(): array
getProductPrices(): arrayReturns 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(): stringgetPriceHtml(): string
getPriceHtml(): stringGenerates 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(): arraygetTypeValidationRules(): array
getTypeValidationRules(): arrayReturns 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$additionalViews
属性
$additionalViewsSpecifies 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$skipAttributes
属性
$skipAttributesSpecifies which attributes to skip:
php
protected $skipAttributes = [
'weight',
'dimensions',
];指定需要跳过的属性:
php
protected $skipAttributes = [
'weight',
'dimensions',
];Cart Integration
购物车集成
prepareForCart(array $data): array
prepareForCart(array $data): arrayprepareForCart(array $data): array
prepareForCart(array $data): arrayProcesses 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.phppackages/Webkul/SubscriptionProduct/
└── src/
├── Type/
│ └── Subscription.php
├── Config/
│ └── product-types.php
└── Providers/
└── SubscriptionServiceProvider.phpStep 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.phpphp
<?php
return [
'subscription' => [
'key' => 'subscription',
'name' => 'Subscription',
'class' => 'Webkul\SubscriptionProduct\Type\Subscription',
'sort' => 5,
],
];文件路径:
packages/Webkul/SubscriptionProduct/src/Config/product-types.phpphp
<?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.phpphp
<?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.phpphp
<?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.phpphp
<?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.phpphp
<?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-autoloadbash
composer dump-autoloadRegister service provider
注册服务提供者
In :
bootstrap/providers.phpphp
<?php
return [
App\Providers\AppServiceProvider::class,
// ... other providers ...
Webkul\SubscriptionProduct\Providers\SubscriptionServiceProvider::class,
];在中添加:
bootstrap/providers.phpphp
<?php
return [
App\Providers\AppServiceProvider::class,
// ... 其他服务提供者 ...
Webkul\SubscriptionProduct\Providers\SubscriptionServiceProvider::class,
];Clear cache
清除缓存
bash
php artisan optimize:clearbash
php artisan optimize:clearTesting
测试
bash
php artisan tinkerbash
php artisan tinkerTest 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'] // 应显示订阅相关数据
undefinedBuilt-in Product Types Reference
内置产品类型参考
| Type | Use Case | Key Features |
|---|---|---|
| Simple | Basic products | Standard pricing, inventory tracking |
| Configurable | Products with variations | Variant management, attribute-based pricing |
| Virtual | Non-physical products | No shipping required |
| Grouped | Related products sold together | Bundle pricing, component selection |
| 类型 | 适用场景 | 核心特性 |
|---|---|---|
| Simple | 基础产品 | 标准定价、库存追踪 |
| Configurable | 带变体的产品 | 变体管理、基于属性的定价 |
| Virtual | 非实体产品 | 无需配送 |
| Grouped | 关联产品捆绑销售 | 套餐定价、组件选择 |
Key Files Reference
核心文件参考
| File | Purpose |
|---|---|
| Product type registration |
| Product type class |
| Package registration |
| Base class |
| 文件 | 用途 |
|---|---|
| 产品类型注册 |
| 产品类型类 |
| 包注册 |
| 基类 |
Common Pitfalls
常见陷阱
- Forgetting to merge config in service provider
- Not matching with array key in configuration
$key - Not registering service provider in
bootstrap/providers.php - Forgetting to run after adding package
composer dump-autoload - Not clearing cache after configuration changes
- Forgetting to call when overriding
parent::isSaleable() - Not handling cart data correctly in
prepareForCart()
- 忘记在服务提供者中合并配置
- 配置中的与数组键名不匹配
$key - 未在中注册服务提供者
bootstrap/providers.php - 添加包后忘记执行
composer dump-autoload - 配置变更后未清除缓存
- 重写时忘记调用
isSaleable()parent::isSaleable() - 在中未正确处理购物车数据
prepareForCart()