Streamlining Subscription States with Enums in Laravel and Filament
Managing subscription statuses in an application, especially when integrating with external payment platforms like Stripe, can quickly become complex. In the landing project, we recently tackled this by implementing PHP Enums to robustly handle both external Stripe subscription states and our internal application-specific statuses. This approach significantly improves data integrity, developer experience, and the overall user interface within our Filament admin panel.
The Problem
Historically, managing subscription statuses often involved using raw strings or integer constants. This method, while functional, presents several challenges:
- Typographical Errors: Manual string entry is prone to mistakes, leading to invalid states.
- Lack of Validation: Without strict types, ensuring that only valid statuses are used throughout the application becomes difficult.
- Synchronization Issues: Keeping internal statuses aligned with external API states (like Stripe's numerous subscription states) requires careful mapping and can be fragile.
- Poor UX: In admin interfaces, displaying and selecting statuses can be cumbersome without clear, predefined options and visual cues.
The Solution: Enums to the Rescue
With PHP 8.1, Enums provide a powerful solution for defining a finite set of allowed values. For our project, we introduced two key enums:
StripeSubscriptionStatus: Mirrors the exact status values provided by the Stripe API.SubscriptionStatus: Represents the simplified, internal status used by our application, mapping to relevant Stripe statuses.
This dual approach allows us to precisely track Stripe's verbose states while maintaining a cleaner, more focused set of states for our application logic and UI.
Defining Subscription Statuses
We defined our internal SubscriptionStatus enum with string values, making it easy to persist in the database and interact with. We also added helper methods for UI enhancements, like getBadgeColor() for Filament:
namespace App\Enums;
enum SubscriptionStatus: string
{
case Active = 'active';
case PastDue = 'past_due';
case Canceled = 'canceled';
case Trialing = 'trialing';
public function getBadgeColor(): string
{
return match ($this) {
self::Active, self::Trialing => 'success',
self::PastDue => 'warning',
self::Canceled => 'danger',
};
}
public function getTranslatedLabel(): string
{
return match ($this) {
self::Active => __('Active'),
self::PastDue => __('Past Due'),
self::Canceled => __('Canceled'),
self::Trialing => __('Trialing'),
};
}
}
Integrating with Eloquent
Laravel's Eloquent ORM provides seamless integration with PHP Enums through model casting. By casting the status attribute to SubscriptionStatus::class, Eloquent automatically handles the serialization and deserialization between the enum instance and its raw database value.
namespace App\Models;
use App\Enums\SubscriptionStatus;
use Illuminate\Database\Eloquent\Model;
class Subscription extends Model
{
protected $casts = [
'status' => SubscriptionStatus::class,
];
}
Enhancing Filament Forms and Tables
Filament, our admin panel builder, beautifully leverages these enums to create a superior user experience. For forms, a Select component can be populated directly from the enum:
use Filament\Forms\Components\Select;
use App\Enums\SubscriptionStatus;
// In SubscriptionResource Form
Select::make('status')
->options(SubscriptionStatus::class)
->native(false)
->required(),
In tables, we can use BadgeColumn along with our enum's helper methods to display visually distinct statuses, making it easy to grasp subscription health at a glance:
use Filament\Tables\Columns\BadgeColumn;
use App\Enums\SubscriptionStatus;
// In SubscriptionResource Table
BadgeColumn::make('status')
->label('Status')
->colors(static fn (SubscriptionStatus $state): string => $state->getBadgeColor())
->formatStateUsing(static fn (SubscriptionStatus $state): string => $state->getTranslatedLabel()),
The Outcome
This refactoring significantly improved the robustness and maintainability of our subscription management system. Developers now benefit from type safety and IDE autocompletion, reducing potential errors. Users of the admin panel enjoy a clearer, more intuitive interface for managing subscriptions, complete with descriptive labels and color-coded badges for quick identification of status.
The Lesson
For any fixed set of states or categories within your application, especially those interacting with external APIs, embracing PHP Enums is a best practice. It not only streamlines development by providing type safety and clarity but also vastly improves the user experience in admin panels through intelligent UI components like those offered by Filament. Migrate your string-based states to enums to create more reliable, maintainable, and user-friendly applications.