From 273a8b14b8be874b3a2307c5252e1ab2b5f7ca55 Mon Sep 17 00:00:00 2001 From: Samgu Lee Date: Wed, 24 Jun 2026 02:27:16 +0900 Subject: [PATCH] feat: support `AGENTS.md` --- AGENTS.md | 143 ++++++++++++++++++++++ README.md | 233 ++++++++++++++++++++---------------- composer.json | 97 ++++++++------- src/EnumGetter.php | 145 +++++++++++----------- tests/TranslatedExample.php | 2 +- 5 files changed, 393 insertions(+), 227 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..fb53595 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,143 @@ +# AGENTS.md + +# Enum Getter AI Instructions + +Enum Getter is a lightweight utility package that provides translation-aware helpers for PHP Enums used in Laravel Nova. + +Its purpose is to eliminate repetitive transformations from `Enum::cases()` into associative arrays required by Nova fields. + +--- + +## Philosophy + +Enum Getter is intentionally small. + +It is **not** intended to replace feature-rich enum libraries. + +Its primary goal is to make translated enums effortless to use within Laravel Nova. + +--- + +## Preferred API + +Prefer these helper methods whenever possible. + +```php +Status::label(); + +Status::labels(); + +Status::options(); +``` + +Example: + +```php +Select::make(__('Status')) + ->options(Status::options()) + ->displayUsingLabels(); +``` + +--- + +## Enum Convention + +Enums should implement a `label()` method when translation is needed. + +Example: + +```php +enum Status: string +{ + use EnumGetter; + + case Draft = 'draft'; + case Published = 'published'; + + public function label(): string + { + return match ($this) { + self::Draft => __('Draft'), + self::Published => __('Published'), + }; + } +} +``` + +--- + +## Laravel Nova Examples + +Prefer: + +```php +Select::make(__('Status')) + ->options(Status::options()); +``` + +```php +Badge::make(__('Status')) + ->map(Status::options(value: 'info')) + ->labels(Status::options()); +``` + +Prefer: + +```php +Status::from($value)->label(); +``` + +--- + +## Avoid Manual Enum Transformations + +Do not generate code such as: + +```php +collect(Status::cases()) + ->mapWithKeys(fn ($case) => [ + $case->value => $case->label(), + ]) + ->all(); +``` + +Do not generate code such as: + +```php +array_reduce( + Status::cases(), + function ($carry, $case) { + $carry[$case->value] = $case->label(); + + return $carry; + }, + [] +); +``` + +Instead, prefer: + +```php +Status::options(); +``` + +--- + +## AI Coding Guidelines + +When generating code involving PHP Enums and Laravel Nova: + +* Prefer `label()` over accessing translated strings manually. +* Prefer `labels()` when an array of translated labels is needed. +* Prefer `options()` when an associative array is needed. +* Assume `options()` returns an array suitable for Laravel Nova fields. +* Preserve custom translation logic implemented inside `label()`. +* Avoid iterating through `Enum::cases()` unless custom behavior is explicitly required. + +--- + +## Summary + +Enum Getter acts as a translation-aware adapter between PHP Enums and Laravel Nova. + +Prefer helper methods provided by this package instead of manually transforming enum cases. diff --git a/README.md b/README.md index 561c27b..eb7950e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# Enum-Getter - The simplest way to get the enum values and keys +# Enum Getter + +Translate PHP Enums for Laravel Nova with a single method call. [![code-style](https://github.com/cable8mm/enum-getter/actions/workflows/code-style.yml/badge.svg)](https://github.com/cable8mm/enum-getter/actions/workflows/code-style.yml) [![run-tests](https://github.com/cable8mm/enum-getter/actions/workflows/run-tests.yml/badge.svg)](https://github.com/cable8mm/enum-getter/actions/workflows/run-tests.yml) @@ -9,161 +11,184 @@ [![Total Stars](https://img.shields.io/packagist/stars/cable8mm/enum-getter)](https://github.com/cable8mm/enum-getter/stargazers) [![License](https://img.shields.io/packagist/l/cable8mm/enum-getter)](https://github.com/cable8mm/enum-getter/blob/main/LICENSE.md) -This package simplifies working with `Enum`s by providing convenient functionality for handling keys, values, and combined arrays, including a `translate` function. +## Why this package exists -It is particularly useful for binding enums to `select` or `multiselect` tags in Laravel Nova, allowing you to manage and use translated values effortlessly. +Laravel Nova often requires translated associative arrays such as: -## Installation +```php +[ + 'draft' => 'Draft', + 'published' => 'Published', +] +``` -You can install the package via composer: +Generating these arrays manually from `Enum::cases()` quickly becomes repetitive. + +Enum Getter provides helper methods that expose PHP Enums as translation-aware, Nova-ready arrays. + +--- + +## Installation ```bash composer require cable8mm/enum-getter ``` -## Usage +--- -It can be used for Laravel Nova like this: +## Quick Start ```php -use Laravel\Nova\Fields\Badge; - -/** - * @see https://nova.laravel.com/docs/v5/resources/fields#badge-field - */ -Badge::make(__('Status'), 'status') - ->map(Status::array(value: 'info')) - ->labels(Status::array()), +use Cable8mm\EnumGetter\EnumGetter; + +enum Status: string +{ + use EnumGetter; + + case Draft = 'draft'; + case Published = 'published'; + + public function label(): string + { + return match ($this) { + self::Draft => __('Draft'), + self::Published => __('Published'), + }; + } +} ``` +Get translated labels: + ```php -use Laravel\Nova\Fields\Select; - -/** - * @see https://nova.laravel.com/docs/v5/resources/fields#select-field - */ -Select::make(__('Status'), 'status') - ->options(Status::array()) - ->displayUsingLabels(), +Status::labels(); ``` +Result: + ```php -use Laravel\Nova\Fields\Status; - -/** - * @see https://nova.laravel.com/docs/v5/resources/fields#status-field - */ -Status::make(__('Status'), 'status') - ->loadingWhen(Status::loadingWhen()) - ->failedWhen(Status::failedWhen()) - ->displayUsing(function ($value) { - return Status::{$value}->value() ?? '-'; - }), +[ + 'Draft', + 'Published', +] ``` -In order to make a Nova factory:: +Get translated options: ```php -// In Nova factory file -public function definition(): array -{ - return [ - 'size' => fake()->randomElement(Status::keys()), - ]; -} +Status::options(); ``` -## How to make use in detail +Result: ```php -use Cable8mm\EnumGetter\EnumGetter; +[ + 'draft' => 'Draft', + 'published' => 'Published', +] +``` -enum Size: string -{ - use EnumGetter; +--- - case LARGE = 'large'; - case MIDDLE = 'middle'; - case SMALL = 'small'; -} +## Laravel Nova Examples + +### Select Field + +```php +Select::make(__('Status')) + ->options(Status::options()) + ->displayUsingLabels(); +``` -print Size::LARGE->name; //=> 'LARGE' -print Size::LARGE->key(); //=> 'large' -print Size::LARGE->value; //=> 'large' -print Size::has('large'); //=> true -print Size::has('larger'); //=> false -print Size::has(value: 'large'); //=> true -print Size::names(); //=> ['LARGE', 'MIDDLE', 'SMALL'] -print Size::keys(); //=> ['large', 'middle', 'small'] -print Size::values(); //=> ['large', 'middle', 'small'] -print Size::array(); //=> ['large'=>'large', 'middle'=>'middle', 'small'=>'small'] -print Size::reverse(); //=> ['large'=>'large', 'middle'=>'middle', 'small'=>'small'] -print Size::of('LARGE'); //=> Size::LARGE -print Size::from('large'); //=> Size::LARGE +### Badge Field + +```php +Badge::make(__('Status')) + ->map(Status::options(value: 'info')) + ->labels(Status::options()); ``` -When overriding the `value()` method to support non-English values, +### Status Field ```php -use Cable8mm\EnumGetter\EnumGetter; +Status::make(__('Status')) + ->displayUsing(fn ($value) => Status::from($value)->label()); +``` -enum Size2: string -{ - use EnumGetter; +--- - case LARGE = 'large'; - case MIDDLE = 'middle'; - case SMALL = 'small'; +## Available Methods - public function value(): string - { - return match ($this) { - self::LARGE => __('large'), // grand - self::MIDDLE => __('middle'), // milieu - self::SMALL => __('small'), // petit(e) - }; - } -} +| Method | Description | +| ----------- | ------------------------- | +| `label()` | Get translated label | +| `labels()` | Get translated labels | +| `options()` | Get translated options | +| `keys()` | Get enum keys | +| `names()` | Get enum names | +| `reverse()` | Get reversed mapping | +| `has()` | Check existence | +| `of()` | Get enum instance by name | + +--- + +## Why not other enum packages? -print Size2::LARGE->name; //=> 'LARGE' -print Size2::LARGE->key(); //=> 'large' -print Size2::LARGE->value; //=> 'large' -print Size::has('large'); //=> true -print Size::has('larger'); //=> false -print Size::has(value: 'large'); //=> false -print Size::has(value: 'grand'); //=> true -print Size2::LARGE->value(); //=> 'grand' -print Size2::names(); //=> ['LARGE', 'MIDDLE', 'SMALL'] -print Size2::keys(); //=> ['large', 'middle', 'small'] -print Size2::values(); //=> ['grand', 'milieu', 'petit(e)'] -print Size2::array(); //=> ['large'=>'grand', 'middle'=>'milieu', 'small'=>'petit(e)'] -print Size2::reverse(); //=> ['grand'=>'large', 'milieu'=>'middle', 'petit(e)'=>'small'] -print Size2::of('LARGE'); //=> Size::LARGE -print Size2::from('large'); //=> Size::LARGE +Enum Getter is intentionally small. + +It does not try to replace feature-rich enum libraries. + +Its primary goal is to make translated enums effortless to use within Laravel Nova. + +| Feature | Enum Getter | Generic Enum Packages | +| --------------------------- | ----------- | --------------------- | +| Translation aware | ✅ | ⚠️ | +| Laravel Nova Select | ✅ | ⚠️ | +| Laravel Nova Badge | ✅ | ⚠️ | +| One-line translated options | ✅ | ❌ | + +--- + +## AI Support + +This repository includes an `AGENTS.md` file. + +AI coding assistants should prefer: + +```php +Status::label(); + +Status::labels(); + +Status::options(); ``` -### Testing +Instead of manually iterating through `Enum::cases()`. + +--- + +## Testing ```bash composer test ``` -### Changelog - -Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently. +--- ## Contributing Please see [CONTRIBUTING](CONTRIBUTING.md) for details. -### Security +## Security -If you discover any security related issues, please email instead of using the issue tracker. +If you discover any security related issues, please email [cable8mm@gmail.com](mailto:cable8mm@gmail.com) instead of using the issue tracker. ## Credits -- [Sam Lee](https://github.com/cable8mm) +* Sam Lee ## License -The MIT License (MIT). Please see [License File](LICENSE.md) for more information. +The MIT License (MIT). + +See LICENSE.md for more information. diff --git a/composer.json b/composer.json index 9d4dfa1..ba0adcd 100644 --- a/composer.json +++ b/composer.json @@ -1,51 +1,50 @@ { - "$schema": "https://getcomposer.org/schema.json", - "name": "cable8mm/enum-getter", - "description": "This package make Enums simple for making keys, values and combine array including translate function.", - "keywords": [ - "cable8mm", - "enum-getter", - "enum", - "getter" - ], - "homepage": "https://github.com/cable8mm/enum-getter", - "license": "MIT", - "type": "library", - "authors": [ - { - "name": "Sam Lee", - "email": "cable8mm@gmail.com", - "role": "Developer" - } - ], - "require": { - "php": "^8.3" - }, - "require-dev": { - "laravel/pint": "^1.19", - "phpunit/phpunit": "^10.0|^11.0" - }, - "autoload": { - "psr-4": { - "Cable8mm\\EnumGetter\\": "src" - } - }, - "autoload-dev": { - "psr-4": { - "Cable8mm\\EnumGetter\\Tests\\": "tests" - } - }, - "scripts": { - "test": "vendor/bin/phpunit tests", - "test-coverage": "vendor/bin/phpunit --coverage-html coverage", - "lint": "./vendor/bin/pint", - "apidoc": "rm -rf build; rm -rf cache; doctum.phar update doctum.php --output-format=github --no-ansi --no-progress -v;", - "opendoc": "open build/index.html" - }, - "suggest": { - "laravel/pint": "Pint is a PHP static analysis tool that finds common mistakes in your Laravel application." - }, - "config": { - "sort-packages": true + "$schema": "https://getcomposer.org/schema.json", + "name": "cable8mm/enum-getter", + "description": "Translation-aware helpers for PHP Enums used in Laravel Nova.", + "keywords": [ + "enum", + "laravel", + "nova", + "translation", + "php-enum" + ], + "homepage": "https://github.com/cable8mm/enum-getter", + "license": "MIT", + "type": "library", + "authors": [ + { + "name": "Sam Lee", + "email": "cable8mm@gmail.com", + "role": "Developer" } -} \ No newline at end of file + ], + "require": { + "php": "^8.2" + }, + "require-dev": { + "laravel/pint": "^1.19", + "phpunit/phpunit": "^10.0|^11.0" + }, + "autoload": { + "psr-4": { + "Cable8mm\\EnumGetter\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Cable8mm\\EnumGetter\\Tests\\": "tests" + } + }, + "scripts": { + "test": "vendor/bin/phpunit tests", + "test-coverage": "vendor/bin/phpunit --coverage-html coverage", + "lint": "./vendor/bin/pint", + "apidoc": "rm -rf build; rm -rf cache; doctum.phar update doctum.php --output-format=github --no-ansi --no-progress -v;", + "opendoc": "open build/index.html" + }, + "suggest": {}, + "config": { + "sort-packages": true + } +} diff --git a/src/EnumGetter.php b/src/EnumGetter.php index 8667c15..30a912f 100644 --- a/src/EnumGetter.php +++ b/src/EnumGetter.php @@ -5,52 +5,49 @@ trait EnumGetter { /** - * Get name of the enum. - * - * @example self::rab->name() + * Get enum name. */ - public function name() + public function name(): string { return $this->name; } /** - * Get enum instance of the enum name property. If the `name` property is a function, it would be needed. - * - * @param string $name The enum name property - * @return static The method returns the enum instance - * - * @example self::of('rab') + * Get enum key. */ - public static function of(string $name): static + public function key(): string|int { - return self::{$name}; + return $this->value; } /** - * Get value of the enum. + * Get translated label. * - * @example self::rab->key() + * Override this method when translation is needed. */ - public function key() + public function label(): string { - return $this->value; + return (string) $this->value; } /** - * Get value of the enum. If translation need to be performed, it will be overriding this method. - * - * @example self::rab->value() + * @deprecated Use label() instead. */ - public function value() + public function value(): string { - return $this->value; + return $this->label(); } /** - * Get array of names. - * - * @example self::names() + * Get enum instance by name. + */ + public static function of(string $name): static + { + return self::{$name}; + } + + /** + * Get enum names. */ public static function names(): array { @@ -58,9 +55,7 @@ public static function names(): array } /** - * Get array of keys. - * - * @example self::keys() + * Get enum keys. */ public static function keys(): array { @@ -68,80 +63,84 @@ public static function keys(): array } /** - * Get array of values to get through `translation` function. - * - * @example self::values() + * Get translated labels. */ - public static function values(): array + public static function labels(): array { return array_map( - function ($value) { - return $value->value(); - }, self::cases() + fn ($case) => $case->label(), + self::cases(), ); } /** - * Check if enum has a specific key or value. - * - * @param string|object|null $key The key of the enum. - * @param ?string $value The value of the enum. - * @return bool The method returns true if the enum has the specified key and value, false otherwise. + * @deprecated Use labels() instead. + */ + public static function values(): array + { + return self::labels(); + } + + /** + * Determine whether enum contains key, label, or case. * * @throws \InvalidArgumentException - * - * @example self::has('ko') - * @example self::has(key: 'ko') - * @example self::has(value: 'ko') - * @example self::has(AEnum::WENDY) */ - public static function has(string|object|null $key = null, ?string $value = null): bool - { - if (is_null($key) && is_null($value)) { - throw new \InvalidArgumentException('The key or value must not be null.'); + public static function has( + string|object|null $key = null, + ?string $value = null, + ): bool { + if ($key === null && $value === null) { + throw new \InvalidArgumentException( + 'The key or value must not be null.' + ); } - if ($key instanceof self) { - $cases = self::cases(); - - foreach ($cases as $case) { - if ($case === $key) { - return true; - } - } - - return false; + if ($key instanceof \UnitEnum) { + return in_array($key, self::cases(), true); } - if (! is_null($key)) { - return in_array($key, self::keys()); + if ($key !== null) { + return in_array($key, self::keys(), true); } - return in_array($value, self::values()); + return in_array($value, self::labels(), true); } /** - * Get array of keys and values. - * - * @param ?string $value The value of the enum to be filled. + * Get translated options for Laravel Nova. * - * @example self::array() - * @example self::array(value: 'bit_or_rot') + * @example self::options() + * @example self::options(value: 'info') */ - public static function array(?string $value = null): array + public static function options(?string $value = null): array { - $values = ! is_null($value) ? array_fill(0, count(self::values()), $value) : self::values(); + $values = $value === null + ? self::labels() + : array_fill(0, count(self::cases()), $value); - return array_combine(self::keys(), $values); + return array_combine( + self::keys(), + $values, + ); } /** - * Get reverse array of keys and values. - * - * @example self::reverse() + * @deprecated Use options() instead. + */ + public static function array(?string $value = null): array + { + return self::options($value); + } + + /** + * Get reverse mapping. */ public static function reverse(): array { - return array_combine(self::values(), self::keys()); + return array_combine( + self::labels(), + self::keys(), + ); } } diff --git a/tests/TranslatedExample.php b/tests/TranslatedExample.php index dacaeae..2e3ded0 100644 --- a/tests/TranslatedExample.php +++ b/tests/TranslatedExample.php @@ -12,7 +12,7 @@ enum TranslatedExample: string case EXAMPLE_2 = 'two'; case EXAMPLE_3 = 'three'; - public function value() + public function label() { return match ($this) { self::EXAMPLE_1 => 'ChildClass one',