Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Enum Translatable Laravel Package

osama-98/laravel-enum-translatable

A Laravel package for adding translations to PHP enums. Define localized labels and descriptions for enum cases using Laravel’s translation system, making it easy to display human-readable enum values across multiple languages.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install the package, then apply the EnumWrappable trait to the backed enum you want to hydrate from untrusted input (request payloads, query strings, DB columns, queue payloads).
  • Read the trait itself first (src/EnumWrappable.php or equivalent) — wrap() is the only surface you'll use day to day; everything else is implementation detail.
  • Read the README section on foreign-enum resolution before you ship anything that passes an enum instance into wrap(). 2.4 changed that behavior, and it's the part most likely to bite an existing codebase.
  • First use case: turning a raw request value into a typed enum safely.
use App\Enums\Status;
use Illuminate\Http\Request;

$status = Status::wrap($request->input('status')); // ?Status

The return type is now ?static, so static analysis and your IDE both know Status::wrap(...) yields ?Status — no instanceof guard needed before using its methods.

Implementation Patterns

Form requests / controllers. wrap() is the natural replacement for Status::tryFrom($value) ?: null guards and for try/catch around from(). Resolve once at the boundary and pass the typed enum inward:

final class UpdateOrderRequest extends FormRequest
{
    public function rules(): array
    {
        return ['status' => ['required', 'string']];
    }

    public function status(): Status
    {
        return Status::wrap($this->input('status'))
            ?? throw ValidationException::withMessages([
                'status' => 'Unknown status.',
            ]);
    }
}

Strict vs non-strict. Use non-strict (null on no-match) at HTTP boundaries so you can convert failures into validation errors with your own message. Use strict mode (throws ValueError) in seeders, config resolvers, migrations, and anywhere a bad value indicates a programming error rather than user input. Keep that split deliberate — do not let strict mode leak into request handling, where it turns a 422 into a 500.

Foreign-enum interop. Since 2.4 you can safely bridge two enums that share a backing vocabulary. The value is unwrapped and re-resolved against the calling enum:

// Role::ADMIN is backed by 'admin'; Status also has a case backed by 'admin'
$status = Status::wrap(Role::ADMIN); // ?Status — Status::Active, not Role::ADMIN

This is the pattern to use when a domain event carries an enum from one bounded context and a consumer needs it expressed in its own vocabulary. Write a small dedicated method (e.g. Status::fromRole()) as the named entry point rather than sprinkling Status::wrap($role) calls around — it documents the mapping and gives you one place to change when the vocabularies drift.

Collection / batch hydration. array_map(Status::wrap(...), $values) works cleanly now that the return type is ?static, but filter with !== null rather than truthiness (see gotchas).

Testing. The package's own suite covers the interesting edges — foreign enum matching/non-matching under both modes, and '0'-backed cases. Mirror those cases in your app tests; they are the regressions that actually happen.

Gotchas and Tips

  • Breaking-ish change for foreign enums. Before 2.4, passing a foreign backed enum returned it unchanged. Now it is unwrapped and re-resolved, so Status::wrap(Role::ADMIN) returns a Status (or throws, or null) instead of a Role. Any code doing $result === Role::ADMIN will now fail, and any code relying on the pass-through for type detection is broken. Grep for wrap( calls that receive something other than a string|int|null.
  • Silent cross-domain collisions. Because re-resolution matches on backing values only, two unrelated enums that happen to share a value will happily convert into each other. Status::wrap(Role::ADMIN) returning Status::Active may be exactly right or a nasty domain bug — the package can't tell. If the vocabularies aren't genuinely shared, reject foreign enum instances at the edge and only accept scalars.
  • '0' is no longer falsy. The emptiness check now only short-circuits on null and ''; the old empty() call treated the string '0' as absent and returned null for a case legitimately backed by '0'. If you had a workaround for that, remove it. But audit your own code too: if (!$status) and ?: chains have the same '0' blind spot at the call site. Test with $status === null.
  • ValueError vs null is a policy decision, not a detail. The same call can either throw or return null depending on mode. Centralize the choice — a config value or a single resolver — otherwise you get inconsistent failure modes for the same bad input across endpoints.
  • Narrowed return type is source-compatible but analysis-visible. ?static versus ?BackedEnum can change PHPStan/Psalm results: previously-narrowed unions may now resolve differently, and any @var BackedEnum annotations you added to paper over the old signature should be deleted. Re-run your static analysis after the bump — the fixes are usually deletions.
  • Debugging a null result. Trace in order: was the input null or '' (short-circuits to null by design)? Was it a foreign enum whose value doesn't exist in the calling enum (non-strict → null)? Was it a scalar that simply doesn't match any case? Those three paths look identical from the call site, so log the raw input, not just the result.
  • Extension points. The trait is thin by design; if you need normalization (trimming, lowercasing, legacy aliases like 'active' → 'enabled'), wrap wrap() in your own static factory rather than forking the trait. That keeps you on the upgrade path for fixes like the '0' one.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator