timezhero / enhanced-enums
Enhanced enums for Laravel
Requires
- php: ^8.5
- laravel/framework: ^13.0
Requires (Dev)
- laravel/pint: ^1.32
- orchestra/testbench: ^11.0
- pestphp/pest: ^5.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A trait and two attributes that add labels, select lists, safe conversion and comparisons to PHP enums in Laravel applications.
Requirements
- PHP 8.5+
- Laravel 13.0+
Installation
Install the package via Composer:
composer require timezhero/enhanced-enums
Usage
Add the Enhancement trait to any enum:
<?php namespace App\Enums; use TimeZHero\EnhancedEnums\Enums\Traits\Enhancement; enum OrderStatus: string { use Enhancement; case Pending = 'pending'; case Processing = 'processing'; case Shipped = 'shipped'; case Delivered = 'delivered'; case Cancelled = 'cancelled'; }
Available Methods
values()— Get all enum valuesnames()— Get all enum case namescollect()— Get cases as a Laravel CollectiongetLabel()— Get translated label for a casetoSelectList()— Generate key-value pairs for dropdownswrap()— Safely convert raw values to enum instancesis()— Check equality with another valueisNot()— Check inequality with another valueisIn()— Check if value is in a collectionisNotIn()— Check if value is not in a collection
values()
Get all enum values as an array:
OrderStatus::values(); // ['pending', 'processing', 'shipped', 'delivered', 'cancelled']
names()
Get all enum case names as an array:
OrderStatus::names(); // ['Pending', 'Processing', 'Shipped', 'Delivered', 'Cancelled']
collect()
Get all enum cases as a Laravel Collection:
OrderStatus::collect(); // Illuminate\Support\Collection containing all OrderStatus cases
Use the #[CollectedBy] attribute to specify a custom collection class:
<?php namespace App\Enums; use App\Collections\StatusCollection; use TimeZHero\EnhancedEnums\Enums\Attributes\CollectedBy; use TimeZHero\EnhancedEnums\Enums\Traits\Enhancement; #[CollectedBy(StatusCollection::class)] enum OrderStatus: string { use Enhancement; case Pending = 'pending'; case Processing = 'processing'; // ... }
OrderStatus::collect(); // Returns StatusCollection instance
getLabel()
Get a translated label for the enum case. Supports two translation strategies.
Keys strategy (default)
Translations are looked up at enums.{snake_case_class}.{value}:
// In lang/en/enums.php return [ 'order_status' => [ 'pending' => 'Awaiting Confirmation', 'processing' => 'Being Processed', ], ];
OrderStatus::Pending->getLabel(); // "Awaiting Confirmation" OrderStatus::Shipped->getLabel(); // "Shipped" (no translation: the case name, headlined)
A case with no translation falls back to its headlined name (InReview becomes "In Review"), so the trait works without any translation file.
JSON strategy
Use the #[TranslationStrategy] attribute to translate the value itself through the JSON translation files:
<?php namespace App\Enums; use TimeZHero\EnhancedEnums\Enums\Attributes\TranslationStrategy; use TimeZHero\EnhancedEnums\Enums\Traits\Enhancement; use TimeZHero\EnhancedEnums\Enums\Translation; #[TranslationStrategy(Translation::Json)] enum PaymentMethod: string { use Enhancement; case CreditCard = 'Credit Card'; case BankTransfer = 'Bank Transfer'; case PayPal = 'PayPal'; }
// In lang/it.json { "Credit Card": "Carta di Credito", "Bank Transfer": "Bonifico Bancario" }
PaymentMethod::CreditCard->getLabel(); // "Carta di Credito" (when locale is 'it') PaymentMethod::PayPal->getLabel(); // "PayPal" (no translation: the value itself)
toSelectList()
Get labels keyed by value, in declaration order, for dropdowns and form selects:
OrderStatus::toSelectList(); // Collection: ['pending' => 'Awaiting Confirmation', 'processing' => 'Being Processed', 'shipped' => 'Shipped', ...]
The result is a plain Collection, so reorder it with ->sort() or ->sortKeys() when needed.
wrap()
Convert raw values to enum instances safely:
OrderStatus::wrap('pending'); // OrderStatus::Pending OrderStatus::wrap(OrderStatus::Pending); // OrderStatus::Pending (unchanged) OrderStatus::wrap('invalid'); // null OrderStatus::wrap(null); // null
A raw value matches a case when their string forms are identical: 3 and '3' are interchangeable, while '3.0', ' 3' and '03' match nothing. An int-backed enum given a non-numeric string returns null rather than throwing.
is()
Check equality with another enum case or raw value (raw values are matched as in wrap()):
$status = OrderStatus::Pending; $status->is(OrderStatus::Pending); // true $status->is('pending'); // true $status->is(OrderStatus::Shipped); // false
isNot()
Check inequality with another enum case or raw value:
$status = OrderStatus::Pending; $status->isNot(OrderStatus::Shipped); // true $status->isNot('pending'); // false
isIn()
Check if the value is in a collection of cases or raw values:
$status = OrderStatus::Pending; $status->isIn([OrderStatus::Pending, OrderStatus::Processing]); // true $status->isIn(['shipped', 'delivered']); // false
isNotIn()
Check if the value is not in a collection of cases or raw values:
$status = OrderStatus::Pending; $status->isNotIn(['shipped', 'delivered']); // true $status->isNotIn([OrderStatus::Pending, OrderStatus::Processing]); // false
Pure enums
The trait also works on enums without a backing type. The case name stands in wherever a value is needed:
enum Stage { use Enhancement; case Draft; case InReview; } Stage::values(); // ['Draft', 'InReview'] Stage::wrap('InReview'); // Stage::InReview Stage::Draft->is('Draft'); // true Stage::InReview->getLabel(); // __('enums.stage.InReview'), or "In Review"
Development
composer test # run the tests composer test-coverage # run the tests and require 100% coverage composer lint # fix code style with Laravel Pint composer lint-check # check code style without fixing
License
MIT