- Create 7 new models with full relationships and business logic:
* PlanFeature: Define available features with categories and types
* PlanFeatureLimit: Manage usage limits per plan with trial overrides
* PlanPermission: Granular permissions system for features
* PlanProvider: Multi-provider payment configuration
* PlanTier: Hierarchical plan structure with upgrade paths
* PlanUsage: Real-time usage tracking and analytics
* TrialConfiguration: Advanced trial settings per plan
- Enhance Plan model with 25+ new methods:
* Feature checking: hasFeature(), canUseFeature(), getRemainingUsage()
* Permission system: hasPermission() with trial support
* Payment providers: getAllowedProviders(), supportsProvider()
* Trial management: hasTrial(), getTrialConfig()
* Upgrade paths: isUpgradeFrom(), getUpgradePath()
* Utility methods: getBillingCycleDisplay(), metadata handling
- Completely redesign PlanResource with tabbed interface:
* Basic Info: Core plan configuration with dynamic billing cycles
* Features & Limits: Dynamic feature management with trial overrides
* Payment Providers: Multi-provider configuration (Stripe, Lemon Squeezy, etc.)
* Trial Settings: Advanced trial configuration with always-visible toggle
- Create new Filament resources:
* PlanFeatureResource: Manage available features by category
* PlanTierResource: Hierarchical tier management with parent-child relationships
- Implement comprehensive data migration:
* Migrate legacy plan data to new enhanced system
* Create default features (mailbox accounts, email forwarding, etc.)
* Preserve existing payment provider configurations
* Set up trial configurations (disabled for legacy plans)
* Handle duplicate data gracefully with rollback support
- Add proper database constraints and indexes:
* Unique constraints on plan-feature relationships
* Foreign key constraints with cascade deletes
* Performance indexes for common queries
* JSON metadata columns for flexible configuration
- Fix trial configuration form handling:
* Add required validation for numeric fields
* Implement proper null handling with defaults
* Add type casting for all numeric fields
* Ensure database constraint compliance
117 lines
2.3 KiB
PHP
117 lines
2.3 KiB
PHP
<?php
|
|
|
|
namespace App\Models;
|
|
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
|
|
class PlanFeature extends Model
|
|
{
|
|
use HasFactory;
|
|
|
|
protected $fillable = [
|
|
'name',
|
|
'display_name',
|
|
'description',
|
|
'category',
|
|
'type',
|
|
'metadata',
|
|
'is_active',
|
|
'sort_order',
|
|
];
|
|
|
|
protected $casts = [
|
|
'metadata' => 'array',
|
|
'is_active' => 'boolean',
|
|
];
|
|
|
|
/**
|
|
* Feature categories
|
|
*/
|
|
const CATEGORY_CORE = 'core';
|
|
|
|
const CATEGORY_ADVANCED = 'advanced';
|
|
|
|
const CATEGORY_PREMIUM = 'premium';
|
|
|
|
/**
|
|
* Feature types
|
|
*/
|
|
const TYPE_BOOLEAN = 'boolean';
|
|
|
|
const TYPE_NUMERIC = 'numeric';
|
|
|
|
const TYPE_TOGGLE = 'toggle';
|
|
|
|
/**
|
|
* Get plan feature limits for this feature
|
|
*/
|
|
public function planFeatureLimits(): HasMany
|
|
{
|
|
return $this->hasMany(PlanFeatureLimit::class);
|
|
}
|
|
|
|
/**
|
|
* Get plan permissions for this feature
|
|
*/
|
|
public function planPermissions(): HasMany
|
|
{
|
|
return $this->hasMany(PlanPermission::class);
|
|
}
|
|
|
|
/**
|
|
* Get usage tracking for this feature
|
|
*/
|
|
public function planUsages(): HasMany
|
|
{
|
|
return $this->hasMany(PlanUsage::class);
|
|
}
|
|
|
|
/**
|
|
* Scope: Active features
|
|
*/
|
|
public function scopeActive($query)
|
|
{
|
|
return $query->where('is_active', true);
|
|
}
|
|
|
|
/**
|
|
* Scope: By category
|
|
*/
|
|
public function scopeByCategory($query, string $category)
|
|
{
|
|
return $query->where('category', $category);
|
|
}
|
|
|
|
/**
|
|
* Scope: Ordered by sort order
|
|
*/
|
|
public function scopeOrdered($query)
|
|
{
|
|
return $query->orderBy('sort_order')->orderBy('display_name');
|
|
}
|
|
|
|
/**
|
|
* Get feature metadata value
|
|
*/
|
|
public function getMetadata(?string $key = null, $default = null)
|
|
{
|
|
if ($key) {
|
|
return data_get($this->metadata, $key, $default);
|
|
}
|
|
|
|
return $this->metadata;
|
|
}
|
|
|
|
/**
|
|
* Set feature metadata value
|
|
*/
|
|
public function setMetadata(string $key, $value): void
|
|
{
|
|
$data = $this->metadata ?? [];
|
|
data_set($data, $key, $value);
|
|
$this->metadata = $data;
|
|
}
|
|
}
|