skills/product-type-development/SKILL.md
Product type development in Bagisto. Activates when creating custom product types, defining product behaviors, or implementing specialized product logic. Use references: @config (product type configuration), @abstract (AbstractType methods), @build (complete subscription implementation).
npx skillsauth add bagisto/agent-skills product-type-developmentInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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.
Activate this skill when:
The Config/product-types.php file is a simple PHP array that registers your product type:
<?php
return [
'subscription' => [
'key' => 'subscription',
'name' => 'Subscription',
'class' => 'Webkul\SubscriptionProduct\Type\Subscription',
'sort' => 5,
],
];
| Property | Description | Example |
|----------|-------------|---------|
| 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 |
name for display and sort for orderingkeyclassYour service provider merges your configuration:
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,
],
];
Every product type in Bagisto extends the AbstractType class:
<?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(): boolControls whether the product appears as purchasable:
public function isSaleable(): bool
{
if (! parent::isSaleable()) {
return false;
}
// Add custom availability logic
return true;
}
haveSufficientQuantity(int $qty): boolChecks if enough quantity is available:
public function haveSufficientQuantity(int $qty): bool
{
return true; // Custom logic based on subscription slots
}
isStockable(): boolDetermines if the product uses inventory tracking:
public function isStockable(): bool
{
return false; // Subscriptions don't use traditional inventory
}
totalQuantity(): intReturns total available quantity:
public function totalQuantity(): int
{
return $this->product->subscription_slots ?? 0;
}
showQuantityBox(): boolControls whether quantity input appears:
public function showQuantityBox(): bool
{
return true;
}
getProductPrices(): arrayReturns structured pricing data:
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(): stringGenerates price HTML for display:
public function getPriceHtml(): string
{
return view('subscription::products.prices.subscription', [
'product' => $this->product,
'prices' => $this->getProductPrices(),
])->render();
}
getTypeValidationRules(): arrayReturns validation rules for product type specific fields:
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 PropertySpecifies additional blade views in product edit page:
protected $additionalViews = [
'subscription::admin.catalog.products.edit.subscription-settings',
'subscription::admin.catalog.products.edit.subscription-pricing',
];
$skipAttributes PropertySpecifies which attributes to skip:
protected $skipAttributes = [
'weight',
'dimensions',
];
prepareForCart(array $data): arrayProcesses product data before adding to cart:
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.php
mkdir -p packages/Webkul/SubscriptionProduct/src/{Type,Config,Providers}
File: packages/Webkul/SubscriptionProduct/src/Config/product-types.php
<?php
return [
'subscription' => [
'key' => 'subscription',
'name' => 'Subscription',
'class' => 'Webkul\SubscriptionProduct\Type\Subscription',
'sort' => 5,
],
];
File: 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;
}
}
File: 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"
}
}
}
composer dump-autoload
In 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
| 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 |
| File | Purpose |
|------|---------|
| 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 |
$key with array key in configurationbootstrap/providers.phpcomposer dump-autoload after adding packageparent::isSaleable() when overridingprepareForCart()development
Shop theme development in Bagisto. Activates when creating custom storefront themes, modifying shop layouts, building theme packages, or working with Vite-powered assets for the customer-facing side of the application.
development
Shipping method development in Bagisto. Activates when creating shipping methods, integrating shipping carriers like FedEx, UPS, DHL, or any third-party shipping provider; or when the user mentions shipping, shipping method, shipping carrier, delivery, or needs to add a new shipping option to checkout.
development
Tests applications using the Pest 3 PHP framework in Bagisto. Activates when writing tests, creating unit or feature tests, adding assertions, testing Livewire components, architecture testing, debugging test failures, working with datasets or mocking; or when the user mentions test, spec, TDD, expects, assertion, coverage, or needs to verify functionality works.
development
Payment gateway development in Bagisto. Activates when creating payment methods, integrating payment gateways like Stripe, PayPal, or any third-party payment processor; or when the user mentions payment, payment gateway, payment method, Stripe, PayPal, or needs to add a new payment option to the checkout.