onursimsek/laravel-extended
Laravel Extended adds developer-friendly extensions to Laravel, including practical helpers, macros, and model/query conveniences that reduce boilerplate for common tasks. It stays lightweight and framework-native, making it easy to drop into existing apps.
Install the package:
composer require onursimsek/laravel-extended
Laravel's package auto-discovery should register the service provider — confirm in bootstrap/providers.php (Laravel 11+) or config/app.php if you're on an older skeleton and the provider isn't listed.
Read the package README first, then the test suite. For the 1.3.0 change specifically, PR #16 (Enum attributes can use as a function) is the authoritative source for the new call syntax — release notes are one line, so the tests are where the real contract lives.
First use case: annotate enum cases with a PHP attribute carrying display metadata (label, colour, icon, description) and read it without hand-rolled ReflectionEnum code:
enum Status: string
{
#[Label('Draft')]
case Draft = 'draft';
#[Label('Published')]
case Published = 'published';
}
Before adopting the new callable form, run it against one enum in a scratch test to confirm the exact invocation shape in 1.3.0 — the release note does not pin down whether it's a method on the case, a static helper, or a trait-provided accessor.
Attribute class definition. Keep attribute classes small and typed (public function __construct(public string $label) {}). Mark them #[Attribute(Attribute::TARGET_CLASS_CONSTANT)] so they attach to enum cases rather than enum classes.
Single accessor, not scattered reflection. Wrap the read in one place — a trait on the enum or a small resolver service — so the 1.3.0 callable syntax is isolated. If the API shifts in 1.4, you change one file instead of twenty.
Eloquent casts. Combine with native enum casting so the attribute metadata travels with the model value:
protected $casts = ['status' => Status::class];
Then $model->status yields the case and the attribute lookup is a single call away — useful in Blade ({{ $model->status->label() }}) and API resources.
Validation and form options. Derive <select> options and Rule::enum() friendly lists from the same attributes instead of a parallel config array. One definition, one source of truth.
Filament / Nova / admin panels. This is the strongest day-to-day win: colour and label metadata per case, resolved through the attribute layer, keeps admin config declarative.
Testing. Data-provider the enum cases and assert the attribute resolves for every case. A missing attribute on a newly added case is the most likely production bug; a test that iterates Status::cases() catches it at CI time.
Fallbacks. Always code a default when an attribute is absent (e.g. ?? Str::headline($case->name)), so adding a case without metadata degrades rather than throws.
__(), no config(), no service container access inside an attribute. If you need translations, store a translation key in the attribute and resolve it at read time.TARGET_CLASS_CONSTANT vs TARGET_CLASS. Getting the target wrong means the attribute silently never appears where you expect. If lookups return nothing, check the #[Attribute] flags before anything else.Attribute::IS_REPEATABLE; otherwise PHP throws at attribute-instantiation time, not at declaration time.newInstance() on attributes is not free. If you resolve metadata in a loop over large collections, memoise per-case results in a static array or a small cache.Label and your enum already has a label() method, the callable form can shadow or be shadowed depending on resolution order. Prefer distinct names (#[DisplayLabel]) or suffix the accessor.@method annotations or a stub on the trait to keep autocompletion and level-8 analysis clean.json_encode of an enum. Resolve to a plain array/scalar in your API resource layer rather than passing enum cases around.composer.json and read the diff — not just the summary — before each upgrade.(new ReflectionEnum(Status::class))->getCase('Draft')->getAttributes() and inspect raw reflection output; that immediately separates "attribute not attached" from "package resolver not finding it".How can I help you explore Laravel packages today?