Loading...
Loading...
Compare original and translation side by side
Config/product-types.php<?php
return [
'subscription' => [
'key' => 'subscription',
'name' => 'Subscription',
'class' => 'Webkul\SubscriptionProduct\Type\Subscription',
'sort' => 5,
],
];Config/product-types.php<?php
return [
'subscription' => [
'key' => 'subscription',
'name' => 'Subscription',
'class' => 'Webkul\SubscriptionProduct\Type\Subscription',
'sort' => 5,
],
];| 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) | |
namesortnamesortkeyclasskeyclasspublic function register(): void
{
$this->mergeConfigFrom(
dirname(__DIR__) . '/Config/product-types.php',
'product_types'
);
}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,
],
];<?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,
],
];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 = [];
}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(): boolisSaleable(): boolpublic function isSaleable(): bool
{
if (! parent::isSaleable()) {
return false;
}
// Add custom availability logic
return true;
}public function isSaleable(): bool
{
if (! parent::isSaleable()) {
return false;
}
// 添加自定义可用性逻辑
return true;
}haveSufficientQuantity(int $qty): boolhaveSufficientQuantity(int $qty): boolpublic function haveSufficientQuantity(int $qty): bool
{
return true; // Custom logic based on subscription slots
}public function haveSufficientQuantity(int $qty): bool
{
return true; // 基于订阅名额的自定义逻辑
}isStockable(): boolisStockable(): boolpublic function isStockable(): bool
{
return false; // Subscriptions don't use traditional inventory
}public function isStockable(): bool
{
return false; // 订阅产品不使用传统库存
}totalQuantity(): inttotalQuantity(): intpublic function totalQuantity(): int
{
return $this->product->subscription_slots ?? 0;
}public function totalQuantity(): int
{
return $this->product->subscription_slots ?? 0;
}showQuantityBox(): boolshowQuantityBox(): boolpublic function showQuantityBox(): bool
{
return true;
}public function showQuantityBox(): bool
{
return true;
}getProductPrices(): arraygetProductPrices(): arraypublic 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),
],
];
}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(): stringgetPriceHtml(): stringpublic function getPriceHtml(): string
{
return view('subscription::products.prices.subscription', [
'product' => $this->product,
'prices' => $this->getProductPrices(),
])->render();
}public function getPriceHtml(): string
{
return view('subscription::products.prices.subscription', [
'product' => $this->product,
'prices' => $this->getProductPrices(),
])->render();
}getTypeValidationRules(): arraygetTypeValidationRules(): arraypublic 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',
];
}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$additionalViewsprotected $additionalViews = [
'subscription::admin.catalog.products.edit.subscription-settings',
'subscription::admin.catalog.products.edit.subscription-pricing',
];protected $additionalViews = [
'subscription::admin.catalog.products.edit.subscription-settings',
'subscription::admin.catalog.products.edit.subscription-pricing',
];$skipAttributes$skipAttributesprotected $skipAttributes = [
'weight',
'dimensions',
];protected $skipAttributes = [
'weight',
'dimensions',
];prepareForCart(array $data): arrayprepareForCart(array $data): arraypublic 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;
}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
├── Config/
│ └── product-types.php
└── Providers/
└── SubscriptionServiceProvider.phppackages/Webkul/SubscriptionProduct/
└── src/
├── Type/
│ └── Subscription.php
├── Config/
│ └── product-types.php
└── Providers/
└── SubscriptionServiceProvider.phpmkdir -p packages/Webkul/SubscriptionProduct/src/{Type,Config,Providers}mkdir -p packages/Webkul/SubscriptionProduct/src/{Type,Config,Providers}packages/Webkul/SubscriptionProduct/src/Config/product-types.php<?php
return [
'subscription' => [
'key' => 'subscription',
'name' => 'Subscription',
'class' => 'Webkul\SubscriptionProduct\Type\Subscription',
'sort' => 5,
],
];packages/Webkul/SubscriptionProduct/src/Config/product-types.php<?php
return [
'subscription' => [
'key' => 'subscription',
'name' => 'Subscription',
'class' => 'Webkul\SubscriptionProduct\Type\Subscription',
'sort' => 5,
],
];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;
}
}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;
}
}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
{
//
}
}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
{
//
}
}{
"autoload": {
"psr-4": {
"Webkul\\SubscriptionProduct\\": "packages/Webkul/SubscriptionProduct/src"
}
}
}{
"autoload": {
"psr-4": {
"Webkul\\SubscriptionProduct\\": "packages/Webkul/SubscriptionProduct/src"
}
}
}composer dump-autoloadcomposer dump-autoloadbootstrap/providers.php<?php
return [
App\Providers\AppServiceProvider::class,
// ... other providers ...
Webkul\SubscriptionProduct\Providers\SubscriptionServiceProvider::class,
];bootstrap/providers.php<?php
return [
App\Providers\AppServiceProvider::class,
// ... 其他服务提供者 ...
Webkul\SubscriptionProduct\Providers\SubscriptionServiceProvider::class,
];php artisan optimize:clearphp artisan optimize:clearphp artisan tinkerphp artisan tinker$product = \Webkul\Product\Models\Product::where('type', 'subscription')->first() $subscription = $product->getTypeInstance()
$product = \Webkul\Product\Models\Product::where('type', 'subscription')->first() $subscription = $product->getTypeInstance()
$subscription->isStockable() // Should return false $subscription->showQuantityBox() // Should return true $subscription->isSaleable() // Should return true
$subscription->isStockable() // 应返回false $subscription->showQuantityBox() // 应返回true $subscription->isSaleable() // 应返回true
$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| 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 | 关联产品捆绑销售 | 套餐定价、组件选择 |
| File | Purpose |
|---|---|
| Product type registration |
| Product type class |
| Package registration |
| Base class |
| 文件 | 用途 |
|---|---|
| 产品类型注册 |
| 产品类型类 |
| 包注册 |
| 基类 |
$keybootstrap/providers.phpcomposer dump-autoloadparent::isSaleable()prepareForCart()$keybootstrap/providers.phpcomposer dump-autoloadisSaleable()parent::isSaleable()prepareForCart()