Weave Code
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

Enums Laravel Package

archtechx/enums

Modern PHP enum helpers: casting, validation, labels, and convenience methods for native enums. Add common patterns like options arrays, random values, and comparisons to make enums easier to use across Laravel apps and APIs.

View on GitHub
Deep Wiki
Context7

Enums

A collection of enum helpers for PHP.

You can read more about the original idea on Twitter.

Installation

PHP 8.1+ is required.

composer require archtechx/enums

Usage

InvokableCases

This helper lets you get the value of a backed enum, or the name of a pure enum, by "invoking" it — either statically (MyEnum::FOO() instead of MyEnum::FOO), or as an instance ($enum()).

That way, you can use enums as array keys:

'statuses' => [
    TaskStatus::INCOMPLETE() => ['some configuration'],
    TaskStatus::COMPLETED() => ['some configuration'],
],

Or access the underlying primitives for any other use cases:

public function updateStatus(int $status): void;

$task->updateStatus(TaskStatus::COMPLETED());

The main point: this is all without having to append ->value to everything.

This approach also has decent IDE support. You get autosuggestions while typing, and then you just append ():

MyEnum::FOO; // => MyEnum instance
MyEnum::FOO(); // => 1

Apply the trait on your enum

use ArchTech\Enums\InvokableCases;

enum TaskStatus: int
{
    use InvokableCases;

    case INCOMPLETE = 0;
    case COMPLETED = 1;
    case CANCELED = 2;
}

enum Role
{
    use InvokableCases;

    case ADMINISTRATOR;
    case SUBSCRIBER;
    case GUEST;
}

Use static calls to get the primitive value

TaskStatus::INCOMPLETE(); // 0
TaskStatus::COMPLETED(); // 1
TaskStatus::CANCELED(); // 2
Role::ADMINISTRATOR(); // 'ADMINISTRATOR'
Role::SUBSCRIBER(); // 'SUBSCRIBER'
Role::GUEST(); // 'GUEST'

Invoke instances to get the primitive value

public function updateStatus(TaskStatus $status, Role $role)
{
    $this->record->setStatus($status(), $role());
}

Names

This helper returns a list of case names in the enum.

Apply the trait on your enum

use ArchTech\Enums\Names;

enum TaskStatus: int
{
    use Names;

    case INCOMPLETE = 0;
    case COMPLETED = 1;
    case CANCELED = 2;
}

enum Role
{
    use Names;

    case ADMINISTRATOR;
    case SUBSCRIBER;
    case GUEST;
}

Use the names() method

TaskStatus::names(); // ['INCOMPLETE', 'COMPLETED', 'CANCELED']
Role::names(); // ['ADMINISTRATOR', 'SUBSCRIBER', 'GUEST']

Values

This helper returns a list of case values for backed enums, or a list of case names for pure enums (making this functionally equivalent to ::names() for pure Enums)

Apply the trait on your enum

use ArchTech\Enums\Values;

enum TaskStatus: int
{
    use Values;

    case INCOMPLETE = 0;
    case COMPLETED = 1;
    case CANCELED = 2;
}

enum Role
{
    use Values;

    case ADMINISTRATOR;
    case SUBSCRIBER;
    case GUEST;
}

Use the values() method

TaskStatus::values(); // [0, 1, 2]
Role::values(); // ['ADMINISTRATOR', 'SUBSCRIBER', 'GUEST']

Options

This helper returns an associative array of case names and values for backed enums, or a list of names for pure enums (making this functionally equivalent to ::names() for pure Enums).

Apply the trait on your enum

use ArchTech\Enums\Options;

enum TaskStatus: int
{
    use Options;

    case INCOMPLETE = 0;
    case COMPLETED = 1;
    case CANCELED = 2;
}

enum Role
{
    use Options;

    case ADMINISTRATOR;
    case SUBSCRIBER;
    case GUEST;
}

Use the options() method

TaskStatus::options(); // ['INCOMPLETE' => 0, 'COMPLETED' => 1, 'CANCELED' => 2]
Role::options(); // ['ADMINISTRATOR', 'SUBSCRIBER', 'GUEST']

stringOptions()

The trait also adds the stringOptions() method that can be used for generating convenient string representations of your enum options:

// First argument is the callback, second argument is glue
// returns "INCOMPLETE => 0, COMPLETED => 1, CANCELED => 2"
TaskStatus::stringOptions(fn ($name, $value) => "$name => $value", ', ');

For pure enums (non-backed), the name is used in place of $value (meaning that both $name and $value are the same).

Both arguments for this method are optional, the glue defaults to \n and the callback defaults to generating HTML <option> tags:

// <option value="0">Incomplete</option>
// <option value="1">Completed</option>
// <option value="2">Canceled</option>
TaskStatus::stringOptions(); // backed enum

// <option value="ADMINISTRATOR">Administrator</option>
// <option value="Subscriber">Subscriber</option>
// <option value="GUEST">Guest</option>
Role::stringOptions(); // pure enum

From

This helper adds from() and tryFrom() to pure enums, and adds fromName() and tryFromName() to all enums.

Important Notes:

  • BackedEnum instances already implement their own from() and tryFrom() methods, which will not be overridden by this trait. Attempting to override those methods in a BackedEnum causes a fatal error.
  • Pure enums only have named cases and not values, so the from() and tryFrom() methods are functionally equivalent to fromName() and tryFromName()

Apply the trait on your enum

use ArchTech\Enums\From;

enum TaskStatus: int
{
    use From;

    case INCOMPLETE = 0;
    case COMPLETED = 1;
    case CANCELED = 2;
}

enum Role
{
    use From;

    case ADMINISTRATOR;
    case SUBSCRIBER;
    case GUEST;
}

Use the from() method

Role::from('ADMINISTRATOR'); // Role::ADMINISTRATOR
Role::from('NOBODY'); // Error: ValueError

Use the tryFrom() method

Role::tryFrom('GUEST'); // Role::GUEST
Role::tryFrom('NEVER'); // null

Use the fromName() method

TaskStatus::fromName('INCOMPLETE'); // TaskStatus::INCOMPLETE
TaskStatus::fromName('MISSING'); // Error: ValueError
Role::fromName('SUBSCRIBER'); // Role::SUBSCRIBER
Role::fromName('HACKER'); // Error: ValueError

Use the tryFromName() method

TaskStatus::tryFromName('COMPLETED'); // TaskStatus::COMPLETED
TaskStatus::tryFromName('NOTHING'); // null
Role::tryFromName('GUEST'); // Role::GUEST
Role::tryFromName('TESTER'); // null

Metadata

This trait lets you add metadata to enum cases.

Apply the trait on your enum

use ArchTech\Enums\Metadata;
use ArchTech\Enums\Meta\Meta;
use App\Enums\MetaProperties\{Description, Color};

#[Meta(Description::class, Color::class)]
enum TaskStatus: int
{
    use Metadata;

    #[Description('Incomplete Task')] #[Color('red')]
    case INCOMPLETE = 0;

    #[Description('Completed Task')] #[Color('green')]
    case COMPLETED = 1;

    #[Description('Canceled Task')] #[Color('gray')]
    case CANCELED = 2;
}

Explanation:

  • Description and Color are userland class attributes — meta properties
  • The #[Meta] call enables those two meta properties on the enum
  • Each case must have a defined description & color (in this example)

Access the metadata

TaskStatus::INCOMPLETE->description(); // 'Incomplete Task'
TaskStatus::COMPLETED->color(); // 'green'

Creating meta properties

Each meta property (= attribute used on a case) needs to exist as a class.

#[Attribute]
class Color extends MetaProperty {}

#[Attribute]
class Description extends MetaProperty {}

Inside the class, you can customize a few things. For instance, you may want to use a different method name than the one derived from the class name (Description becomes description() by default). To do that, override the method() method on the meta property:

#[Attribute]
class Description extends MetaProperty
{
    public static function method(): string
    {
        return 'note';
    }
}

With the code above, the description of a case will be accessible as TaskStatus::INCOMPLETE->note().

Another thing you can customize is the passed value. For instance, to wrap a color name like text-{$color}-500, you'd add the following transform() method:

#[Attribute]
class Color extends MetaProperty
{
    protected function transform(mixed $value): mixed
    {
        return "text-{$value}-500";
    }
}

And now the returned color will be correctly transformed:

TaskStatus::COMPLETED->color(); // 'text-green-500'

You can also add a defaultValue() method to specify the value a case should have if it doesn't use the meta property. That way you can apply the attribute only on some cases and still get a configurable default value on all other cases.

Use the fromMeta() method

TaskStatus::fromMeta(Color::make('green')); // TaskStatus::COMPLETED
TaskStatus::fromMeta(Color::make('blue')); // Error: ValueError

Use the tryFromMeta() method

TaskStatus::tryFromMeta(Color::make('green')); // TaskStatus::COMPLETED
TaskStatus::tryFromMeta(Color::make('blue')); // null

Recommendation: use annotations and traits

If you'd like to add better IDE support for the metadata getter methods, you can use @method annotations:

/**
 * @method string description()
 * @method string color()
 */
#[Meta(Description::class, Color::class)]
enum TaskStatus: int
{
    use Metadata;

    #[Description('Incomplete Task')] #[Color('red')]
    case INCOMPLETE = 0;

    #[Description('Completed Task')] #[Color('green')]
    case COMPLETED = 1;

    #[Description('Canceled Task')] #[Color('gray')]
    case CANCELED = 2;
}

And if you're using the same meta property in multiple enums, you can create a dedicated trait that includes this @method annotation.

Comparable

This trait lets you compare enums using is(), isNot(), in() and notIn().

Apply the trait on your enum

use ArchTech\Enums\Comparable;

enum TaskStatus: int
{
    use Comparable;

    case INCOMPLETE = 0;
    case COMPLETED = 1;
    case CANCELED = 2;
}

enum Role
{
    use Comparable;

    case ADMINISTRATOR;
    case SUBSCRIBER;
    case GUEST;
}

Use the is() method

TaskStatus::INCOMPLETE->is(TaskStatus::INCOMPLETE); // true
TaskStatus::INCOMPLETE->is(TaskStatus::COMPLETED); // false
Role::ADMINISTRATOR->is(Role::ADMINISTRATOR); // true
Role::ADMINISTRATOR->is(Role::NOBODY); // false

Use the isNot() method

TaskStatus::INCOMPLETE->isNot(TaskStatus::INCOMPLETE); // false
TaskStatus::INCOMPLETE->isNot(TaskStatus::COMPLETED); // true
Role::ADMINISTRATOR->isNot(Role::ADMINISTRATOR); // false
Role::ADMINISTRATOR->isNot(Role::NOBODY); // true

Use the in() method

TaskStatus::INCOMPLETE->in([TaskStatus::INCOMPLETE, TaskStatus::COMPLETED]); // true
TaskStatus::INCOMPLETE->in([TaskStatus::COMPLETED, TaskStatus::CANCELED]); // false
Role::ADMINISTRATOR->in([Role::ADMINISTRATOR, Role::GUEST]); // true
Role::ADMINISTRATOR->in([Role::SUBSCRIBER, Role::GUEST]); // false

Use the notIn() method

TaskStatus::INCOMPLETE->notIn([TaskStatus::INCOMPLETE, TaskStatus::COMPLETED]); // false
TaskStatus::INCOMPLETE->notIn([TaskStatus::COMPLETED, TaskStatus::CANCELED]); // true
Role::ADMINISTRATOR->notIn([Role::ADMINISTRATOR, Role::GUEST]); // false
Role::ADMINISTRATOR->notIn([Role::SUBSCRIBER, Role::GUEST]); // true

PHPStan

To assist PHPStan when using invokable cases, you can include the PHPStan extensions into your own phpstan.neon file:

includes:
  - ./vendor/archtechx/enums/extension.neon

Note: If you have installed phpstan/extension-installer, the extension is automatically included.

Development

Run all checks locally:

./check

Code style will be automatically fixed by php-cs-fixer.

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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport