diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..d90b29a --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: sakanjo diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml new file mode 100644 index 0000000..b374bfb --- /dev/null +++ b/.github/workflows/phpstan.yml @@ -0,0 +1,25 @@ +name: PHPStan + +on: + push: + pull_request: + +jobs: + phpstan: + name: phpstan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + coverage: none + + - name: Install composer dependencies + uses: ramsey/composer-install@v2 + + - name: Run PHPStan + run: ./vendor/bin/phpstan --error-format=github + diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..83a1830 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,31 @@ +name: Tests + +on: + push: + pull_request: + +jobs: + ci: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + tools: composer:v2 + extensions: mbstring, pdo, pdo_sqlite + coverage: xdebug + + - name: Install Dependencies + run: composer install --no-interaction --prefer-dist --optimize-autoloader + + - name: Run pest + run: ./vendor/bin/pest + + - name: Run Pint + run: ./vendor/bin/pint --test + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8cab567 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/vendor +/composer.lock +.idea diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5d42396 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Salah Kanjo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2d06760 --- /dev/null +++ b/README.md @@ -0,0 +1,240 @@ +

đŸ”Ĩ Easy enum

+ +

+ Workflow status + Laravel v11.x + PHP 8.2 +

+ +

Easily work with enums.

+ +> ✨ Help support the maintenance of this package by [sponsoring me](https://github.com/sponsors/sakanjo). + +> Designed to work with **Laravel**, **Filament**, and more. + +![Preview](./art/preview.png) + +Table of Contents +================= + +* [Install](#-install) +* [Usage](#-usage) + * [1. Create enum](#1-create-enum) + * [2. Create lang file](#2-create-lang-file) +* [Methods](#-methods) + * [getLabel](#getlabel) + * [is](#is) + * [isNot](#isnot) + * [tryFromName](#tryfromname) + * [fromName](#fromname) + * [names](#names) + * [values](#values) + * [options](#options) + * [toHtml](#tohtml) +* [Practical examples](#-practical-examples) + * [Filamentphp](#filamentphp) + * [Enum](#enum) + * [Resource](#resource) + * [Laravel blade](#laravel-blade) +* [Support the development](#-support-the-development) +* [Credits](#%EF%B8%8F-credits) +* [License](#-license) + +## đŸ“Ļ Install + +``` +composer require sakanjo/laravel-easy-enum +``` + +## đŸĻ„ Usage + +### 1. Create enum + +```php + [ + Enums\ExampleEnum::NOPE->name => 'Nope', + // ... + ], + + // ... +]; +``` + +That's it! + +## 📚 Methods + +### getLabel + +Returns the label of the enum value. + +```php +Status::Active->getLabel(); // Active +``` + +### is + +Checks if the enum is equal to another one. + +```php +$enum1->is($enum2); // boolean +``` + +### isNot + +Checks if the enum is not equal to another one. + +```php +$enum1->isNot($enum2); // boolean +``` + +### tryFromName + +Safely converts a string to its corresponding enum value (returns null if not found). + +```php +Status::tryFromName('Active'); // Status::Active +Status::tryFromName('Oops'); // null +``` + +Converts a string to its corresponding enum value (throws exception if not found). + +### fromName + +```php +Status::fromName('Active'); // Status::Active +Status::fromName('Oops'); // Throws ValueError exception +``` + +### names + +Returns a list of case names. + +```php +Status::names(); // ['Active', 'NOPE'] +``` + +### values + +Returns a list of case values . + +```php +Status::values(); // [0, 1] +``` + +### options + +Returns an associative array of case names and values. + +```php +Status::options(); // ['Active' => 0, 'NOPE' => 1] +Status::options(true); // ['Active' => 0, 'Nope' => 1] +``` + +### toHtml + +alias for `getLabel`, useful in blade. + +```php +Status::Active->toHtml(); // Active +``` + +## đŸ”Ĩ Practical examples + +#### Filamentphp + +##### Enum + +```php +options(Enums\Status::class); +``` + +#### Laravel blade + +```php + + Current status: {{ auth()->user()->status }} + +``` + +## 💖 Support the development + +**Do you like this project? Support it by donating** + +Click the ["💖 Sponsor"](https://github.com/sponsors/sakanjo) at the top of this repo. + +## Šī¸ Credits + +- [Salah Kanjo](https://github.com/sakanjo) +- [All Contributors](../../contributors) + +## 📄 License + +[MIT License](https://github.com/sakanjo/laravel-easy-metrics/blob/master/LICENSE) Š 2023-PRESENT [Salah Kanjo](https://github.com/sakanjo) diff --git a/art/preview.png b/art/preview.png new file mode 100644 index 0000000..99183db Binary files /dev/null and b/art/preview.png differ diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..7e768cc --- /dev/null +++ b/composer.json @@ -0,0 +1,50 @@ +{ + "name": "sakanjo/laravel-easy-enum", + "description": "Easily work with enum.", + "license": "MIT", + "keywords": [ + "laravel", + "enums", + "laravel-easy" + ], + "authors": [ + { + "name": "Salah Kanjo", + "email": "dev.salah.kanjo@gmail.com" + } + ], + "require": { + "php": "^8.2", + "illuminate/support": "^10.0 || ^11.0" + }, + "require-dev": { + "laravel/pint": "^1.1", + "pestphp/pest": "^2.3", + "phpstan/phpstan": "^1.1", + "orchestra/testbench": "^9.0" + }, + "autoload": { + "psr-4": { + "SaKanjo\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "SaKanjo\\EasyEnum\\Tests\\": "tests/src/" + } + }, + "config": { + "allow-plugins": { + "pestphp/pest-plugin": true + } + }, + "scripts": { + "test": [ + "@php vendor/bin/pint --test", + "@php vendor/bin/phpstan", + "@php vendor/bin/pest --parallel" + ] + }, + "minimum-stability": "stable", + "prefer-stable": true +} diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..91e6453 --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,4 @@ +parameters: + level: 5 + paths: + - src diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..0c2ef2d --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,22 @@ + + + + + ./tests + + + + + + ./src + + + + + + + diff --git a/pint.json b/pint.json new file mode 100644 index 0000000..93061b6 --- /dev/null +++ b/pint.json @@ -0,0 +1,3 @@ +{ + "preset": "laravel" +} diff --git a/src/EasyEnum.php b/src/EasyEnum.php new file mode 100644 index 0000000..cadb2e4 --- /dev/null +++ b/src/EasyEnum.php @@ -0,0 +1,101 @@ +name"; + } + + private function defaultLabel(): string + { + return Str::of($this->name) + ->headline() + ->lower() + ->ucfirst(); + } + + private function translated(): string + { + return Lang::get($this->getNamespace()); + } + + public function getLabel(): string + { + return Lang::has($this->getNamespace()) + ? $this->translated() + : $this->defaultLabel(); + } + + // --- Equality --- + + public function is(mixed $value): bool + { + return $this === $value; + } + + public function isNot(mixed $value): bool + { + return ! $this->is($value); + } + + // --- Serialization --- + + public static function tryFromName(string $name): ?static + { + foreach (self::cases() as $case) { + if ($case->name === $name) { + return $case; + } + } + + return null; + } + + public static function fromName(string $name): static + { + $case = self::tryFromName($name); + + return $case + ?: throw new ValueError($name.' is not a valid case for enum '.self::class); + } + + // --- cases --- + + public static function names(): array + { + return array_column(static::cases(), 'name'); + } + + public static function values(): array + { + return array_column(self::cases(), 'value'); + } + + public static function options(bool $translated = false): array + { + return $translated + ? Arr::mapWithKeys(self::cases(), fn (self $case) => [ + $case->getLabel() => $case->value, + ]) + : array_column(self::cases(), 'value', 'name'); + } + + // --- Htmlable --- + + public function toHtml(): string + { + return $this->getLabel(); + } +} diff --git a/tests/Pest.php b/tests/Pest.php new file mode 100644 index 0000000..fae8372 --- /dev/null +++ b/tests/Pest.php @@ -0,0 +1,6 @@ +in(__DIR__.'/src'); diff --git a/tests/src/Enums/ExampleEnum.php b/tests/src/Enums/ExampleEnum.php new file mode 100644 index 0000000..80b9452 --- /dev/null +++ b/tests/src/Enums/ExampleEnum.php @@ -0,0 +1,14 @@ +getLabel(), $expected); +}) + ->with([ + [ExampleEnum::USD, 'USD'], + [ExampleEnum::EURO, 'EURO'], + [ExampleEnum::NOPE, 'N o p e'], + ]); + +it('compares correctly using `is`', function (ExampleEnum $a, ExampleEnum $b, bool $expected) { + assertEquals($a->is($b), $expected); +}) + ->with([ + [ExampleEnum::USD, ExampleEnum::USD, true], + [ExampleEnum::USD, ExampleEnum::EURO, false], + [ExampleEnum::USD, ExampleEnum::NOPE, false], + ]); + +it('compares correctly using `isNot`', function (ExampleEnum $a, ExampleEnum $b, bool $expected) { + assertEquals($a->isNot($b), $expected); +}) + ->with([ + [ExampleEnum::USD, ExampleEnum::USD, false], + [ExampleEnum::USD, ExampleEnum::EURO, true], + [ExampleEnum::USD, ExampleEnum::NOPE, true], + ]); + +it('returns correct case using `tryFromName`', function (string $case, ?ExampleEnum $expected) { + assertEquals(ExampleEnum::tryFromName($case), $expected); +}) + ->with([ + ['USD', ExampleEnum::USD], + ['Oops', null], + ['EURO', ExampleEnum::EURO], + ]); + +it('returns correct case using `fromName` and throws error if not', function (string $case, ?ExampleEnum $expected) { + try { + assertEquals(ExampleEnum::fromName($case), $expected); + } catch (ValueError $_) { + assertEquals($expected, null); + } +}) + ->with([ + ['USD', ExampleEnum::USD], + ['Oops', null], + ['EURO', ExampleEnum::EURO], + ]); + +it('returns correct names using `names`', function () { + assertEquals(ExampleEnum::names(), [ + 'USD', + 'EURO', + 'NOPE', + ]); +}); + +it('returns correct values using `values`', function () { + assertEquals(ExampleEnum::values(), [ + 0, + 1, + 2, + ]); +}); + +it('returns correct options using `options`', function (bool $translated, array $expected) { + assertEquals(ExampleEnum::options($translated), $expected); +}) + ->with([ + [false, [ + 'USD' => 0, + 'EURO' => 1, + 'NOPE' => 2, + ]], + [true, [ + 'USD' => 0, + 'EURO' => 1, + 'N o p e' => 2, + ]], + ]); diff --git a/tests/src/TestCase.php b/tests/src/TestCase.php new file mode 100644 index 0000000..d22d7bd --- /dev/null +++ b/tests/src/TestCase.php @@ -0,0 +1,25 @@ +loadLangFiles(); + } + + private function loadLangFiles(): void + { + $enums = require __DIR__.'/lang/en/enums.php'; + $lines = Arr::mapWithKeys($enums, fn ($value, $key) => [ + 'enums.'.$key => $value, + ]); + + Lang::addLines($lines, 'en'); + } +} diff --git a/tests/src/lang/en/enums.php b/tests/src/lang/en/enums.php new file mode 100644 index 0000000..fa42f9e --- /dev/null +++ b/tests/src/lang/en/enums.php @@ -0,0 +1,10 @@ + [ + Enums\ExampleEnum::USD->name => 'USD', + Enums\ExampleEnum::EURO->name => 'EURO', + ], +];