diff --git a/README.md b/README.md index 4a54fe0..93deb8d 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ composer require archtechx/enums ### InvokableCases -This helper lets you get the value of a backed enum by "invoking" it — either statically (`MyEnum::FOO()` instead of `MyEnum::FOO`), or as an instance (`$enum()`). +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: ```php @@ -58,6 +58,15 @@ enum TaskStatus: int case COMPLETED = 1; case CANCELED = 2; } + +enum Role +{ + use InvokableCases; + + case ADMINISTRATOR; + case SUBSCRIBER; + case GUEST; +} ``` #### Use static calls to get the primitive value @@ -65,13 +74,16 @@ enum TaskStatus: int 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 ```php -public function updateStatus(TaskStatus $status) +public function updateStatus(TaskStatus $status, Role $role) { - $this->record->setStatus($status()); + $this->record->setStatus($status(), $role()); } ``` @@ -91,16 +103,26 @@ enum TaskStatus: int case COMPLETED = 1; case CANCELED = 2; } + +enum Role +{ + use Names; + + case ADMINISTRATOR; + case SUBSCRIBER; + case GUEST; +} ``` #### Use the `names()` method ```php TaskStatus::names(); // ['INCOMPLETE', 'COMPLETED', 'CANCELED'] +Role::names(); // ['ADMINISTRATOR', 'SUBSCRIBER', 'GUEST'] ``` ### Values -This helper returns a list of case *values* in the enum. +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()`](#names) for pure Enums) #### Apply the trait on your enum ```php @@ -114,16 +136,26 @@ enum TaskStatus: int case COMPLETED = 1; case CANCELED = 2; } + +enum Role +{ + use Values; + + case ADMINISTRATOR; + case SUBSCRIBER; + case GUEST; +} ``` #### Use the `values()` method ```php TaskStatus::values(); // [0, 1, 2] +Role::values(); // ['ADMINISTRATOR', 'SUBSCRIBER', 'GUEST'] ``` ### Options -This helper returns an associative array of case names and values. +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()`](#names) for pure Enums). #### Apply the trait on your enum ```php @@ -137,11 +169,21 @@ enum TaskStatus: int case COMPLETED = 1; case CANCELED = 2; } + +enum Role +{ + use Options; + + case ADMINISTRATOR; + case SUBSCRIBER; + case GUEST; +} ``` #### Use the `options()` method ```php TaskStatus::options(); // ['INCOMPLETE' => 0, 'COMPLETED' => 1, 'CANCELED' => 2] +Role::options(); // ['ADMINISTRATOR', 'SUBSCRIBER', 'GUEST'] ``` ## Development diff --git a/src/InvokableCases.php b/src/InvokableCases.php index 54f5426..b5a2150 100644 --- a/src/InvokableCases.php +++ b/src/InvokableCases.php @@ -4,22 +4,24 @@ declare(strict_types=1); namespace ArchTech\Enums; +use BackedEnum; + trait InvokableCases { /** Return the enum's value when it's $invoked(). */ public function __invoke() { - return $this->value; + return $this instanceof BackedEnum ? $this->value : $this->name; } - /** Return the enum's value when it's called ::STATICALLY(). */ + /** Return the enum's value or name when it's called ::STATICALLY(). */ public static function __callStatic($name, $args) { $cases = static::cases(); foreach ($cases as $case) { if ($case->name === $name) { - return $case->value; + return $case instanceof BackedEnum ? $case->value : $case->name; } } diff --git a/src/Options.php b/src/Options.php index 2cd620d..3e55328 100644 --- a/src/Options.php +++ b/src/Options.php @@ -4,11 +4,17 @@ declare(strict_types=1); namespace ArchTech\Enums; +use BackedEnum; + trait Options { /** Get an associative array of [case name => case value]. */ public static function options(): array { - return array_column(static::cases(), 'value', 'name'); + $cases = static::cases(); + + return isset($cases[0]) && $cases[0] instanceof BackedEnum + ? array_column($cases, 'value', 'name') + : array_column($cases, 'name'); } } diff --git a/src/Values.php b/src/Values.php index db6d63e..03a1436 100644 --- a/src/Values.php +++ b/src/Values.php @@ -4,11 +4,17 @@ declare(strict_types=1); namespace ArchTech\Enums; +use BackedEnum; + trait Values { /** Get an array of case values. */ public static function values(): array { - return array_column(static::cases(), 'value'); + $cases = static::cases(); + + return isset($cases[0]) && $cases[0] instanceof BackedEnum + ? array_column($cases, 'value') + : array_column($cases, 'name'); } } diff --git a/tests/Pest.php b/tests/Pest.php index 35b60c5..4e535cc 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -56,3 +56,11 @@ enum Status: int case PENDING = 0; case DONE = 1; } + +enum Role +{ + use InvokableCases, Options, Names, Values; + + case ADMIN; + case GUEST; +} diff --git a/tests/Pest/InvokableCasesTest.php b/tests/Pest/InvokableCasesTest.php index ba23579..6dd5b8a 100644 --- a/tests/Pest/InvokableCasesTest.php +++ b/tests/Pest/InvokableCasesTest.php @@ -2,18 +2,33 @@ use ArchTech\Enums\Exceptions\UndefinedCaseError; -it('can be used as a static method', function () { +it('can be used as a static method with backed enums', function () { expect(Status::PENDING())->toBe(0); expect(Status::DONE())->toBe(1); }); -it('can be invoked as an instance', function () { +it('can be used as a static method with pure enums', function () { + expect(Role::ADMIN())->toBe('ADMIN'); + expect(Role::GUEST())->toBe('GUEST'); +}); + +it('can be invoked as an instance as a backed enum', function () { $status = Status::PENDING; expect($status())->toBe(0); expect($status())->toBe($status->value); }); -it('throws an error when a nonexistent case is being used', function () { +it('can be invoked as an instance as a pure enum', function () { + $role = Role::ADMIN; + + expect($role())->toBe('ADMIN'); +}); + +it('throws an error when a nonexistent case is being used for backed enums', function () { Status::INVALID(); })->expectException(UndefinedCaseError::class); + +it('throws an error when a nonexistent case is being used for pure enums', function () { + Role::INVALID(); +})->expectException(UndefinedCaseError::class); diff --git a/tests/Pest/NamesTest.php b/tests/Pest/NamesTest.php index 1ea3eea..ec39e3f 100644 --- a/tests/Pest/NamesTest.php +++ b/tests/Pest/NamesTest.php @@ -1,5 +1,9 @@ expect(Status::names()) ->toBe(['PENDING', 'DONE']); + +it('can return an array of case names from pure enums') + ->expect(Role::names()) + ->toBe(['ADMIN', 'GUEST']); diff --git a/tests/Pest/OptionsTest.php b/tests/Pest/OptionsTest.php index 1498efd..b581223 100644 --- a/tests/Pest/OptionsTest.php +++ b/tests/Pest/OptionsTest.php @@ -1,7 +1,13 @@ expect(Status::options())->toBe([ 'PENDING' => 0, 'DONE' => 1, ]); + +it('can return an indexed array of options from a pure enum') + ->expect(Role::options())->toBe([ + 0 => 'ADMIN', + 1 => 'GUEST', + ]); diff --git a/tests/Pest/ValuesTest.php b/tests/Pest/ValuesTest.php index 89f8f86..9aea523 100644 --- a/tests/Pest/ValuesTest.php +++ b/tests/Pest/ValuesTest.php @@ -1,5 +1,9 @@ expect(Status::values()) ->toBe([0, 1]); + +it('can return an array of case names from a pure enum') + ->expect(Role::values()) + ->toBe(['ADMIN', 'GUEST']);