├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── config └── backed-enums.php ├── ide.json ├── src ├── BackedEnum.php ├── Casts │ └── AsFullEnumCollection.php ├── IsBackedEnum.php ├── LaravelBackedEnumMakeCommand.php └── LaravelBackedEnumsServiceProvider.php └── stubs └── laravel-backed-enum.stub /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `laravel-backed-enums` will be documented in this file. 4 | 5 | ## v2.6.1 - 2025-03-05 6 | 7 | ### What's Changed 8 | 9 | * ide.json: one code generation instead of two by @adelf in https://github.com/webfox/laravel-backed-enums/pull/45 10 | 11 | ### New Contributors 12 | 13 | * @adelf made their first contribution in https://github.com/webfox/laravel-backed-enums/pull/45 14 | 15 | **Full Changelog**: https://github.com/webfox/laravel-backed-enums/compare/v2.6.0...v2.6.1 16 | 17 | ## v2.6.0 - 2025-03-02 18 | 19 | ### What's Changed 20 | 21 | * Add Laravel Idea support. by @hailwood in https://github.com/webfox/laravel-backed-enums/pull/44 22 | 23 | **Full Changelog**: https://github.com/webfox/laravel-backed-enums/compare/v2.5.0...v2.6.0 24 | 25 | ## v2.5.0 - 2025-02-25 26 | 27 | ### What's Changed 28 | 29 | * Laravel 12 Support by @hailwood in https://github.com/webfox/laravel-backed-enums/pull/43 30 | 31 | **Full Changelog**: https://github.com/webfox/laravel-backed-enums/compare/v2.4.0...v2.5.0 32 | 33 | ## v2.4.0 - 2025-01-21 34 | 35 | ### What's Changed 36 | 37 | * Add make command by @Jim-Webfox in https://github.com/webfox/laravel-backed-enums/pull/40 38 | 39 | **Full Changelog**: https://github.com/webfox/laravel-backed-enums/compare/v2.3.1...v2.4.0 40 | 41 | ## v2.3.1 - 2024-03-15 42 | 43 | ### What's Changed 44 | 45 | * Add unit/feature tests by @hailwood in https://github.com/webfox/laravel-backed-enums/pull/25 46 | * Fix `::labels()` method by @hailwood in https://github.com/webfox/laravel-backed-enums/pull/25 47 | * Add `AsFullEnumCollection::of(MyEnum::class)` for casts by @hailwood in https://github.com/webfox/laravel-backed-enums/pull/25 48 | * Fix Issue #22: Replace 'self' with 'static' by @vikas020807 in https://github.com/webfox/laravel-backed-enums/pull/24 49 | 50 | ### New Contributors 51 | 52 | * @vikas020807 made their first contribution in https://github.com/webfox/laravel-backed-enums/pull/24 53 | 54 | **Full Changelog**: https://github.com/webfox/laravel-backed-enums/compare/v2.3.0...v2.3.1 55 | 56 | ## v2.3.0 - 2024-03-12 57 | 58 | ### What's Changed 59 | 60 | * Bumping to version 11 of the Laravel framework (ahead of launch tomorrow) by @csoutham in https://github.com/webfox/laravel-backed-enums/pull/23 61 | * Bump stefanzweifel/git-auto-commit-action from 4 to 5 by @dependabot in https://github.com/webfox/laravel-backed-enums/pull/19 62 | 63 | ### New Contributors 64 | 65 | * @csoutham made their first contribution in https://github.com/webfox/laravel-backed-enums/pull/23 66 | 67 | **Full Changelog**: https://github.com/webfox/laravel-backed-enums/compare/v2.2.0...v2.3.0 68 | 69 | ## v2.2.0 - 2023-10-07 70 | 71 | ### What's Changed 72 | 73 | - Add EnumCollection support by @hailwood in https://github.com/webfox/laravel-backed-enums/pull/17 74 | Fix toJson method to actually return a json string instead of an array. 75 | Add new `AsFullEnumCollection` cast - See the readme for usage and a description of why this is useful. 76 | 77 | **Full Changelog**: https://github.com/webfox/laravel-backed-enums/compare/v2.1.1...v2.2.0 78 | 79 | ## v2.1.1 - 2023-10-04 80 | 81 | **Full Changelog**: https://github.com/webfox/laravel-backed-enums/compare/v2.1.0...v2.1.1 82 | 83 | ## v2.1.0 - 2023-10-05 84 | 85 | - Add static `rule()` method as a shortcut for the laravel validation rule. 86 | 87 | ## v2.0.0 - 2023-08-28 88 | 89 | ### What's Changed 90 | 91 | - Use value in `map()` method 92 | 93 | ## v1.2.3 - 2023-03-02 94 | 95 | ### What's Changed 96 | 97 | - Add additional comparison methods 98 | 99 | **Full Changelog**: https://github.com/webfox/laravel-backed-enums/compare/v1.1.0...v1.2.3 100 | 101 | ## v1.2.2 - 2023-02-28 102 | 103 | ### What's Changed 104 | 105 | - Add support for direct value comparisons 106 | 107 | **Full Changelog**: https://github.com/webfox/laravel-backed-enums/compare/v1.2.1...v1.2.2 108 | 109 | ## v1.2.1 - 2023-02-22 110 | 111 | ### What's Changed 112 | 113 | - Add support for laravel 10 114 | 115 | **Full Changelog**: https://github.com/webfox/laravel-backed-enums/compare/v1.1.0...v1.2.1 116 | 117 | ## v1.1.0 - 2022-10-04 118 | 119 | ### What's Changed 120 | 121 | - Add support for rendering enums in blade templates 122 | 123 | **Full Changelog**: https://github.com/webfox/laravel-backed-enums/compare/v1.0.1...v1.1.0 124 | 125 | ## Added new fields to toArray, minor bug fixes - 2022-09-21 126 | 127 | Added new fields to toArray, minor bug fixes 128 | 129 | ## v1.0.0 - 2022-09-19 130 | 131 | Initial release 132 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) webfox 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Banner Image](https://banners.beyondco.de/Laravel%20Backed%20Enums.png?theme=light&packageManager=composer+require&packageName=webfox%2Flaravel-backed-enums&pattern=architect&style=style_1&description=Supercharge+your+PHP8+backed+enums+in+Laravel.&md=1&showWatermark=0&fontSize=125px&images=https%3A%2F%2Flaravel.com%2Fimg%2Flogomark.min.svg) 2 | 3 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/webfox/laravel-backed-enums.svg?style=flat-square)](https://packagist.org/packages/webfox/laravel-backed-enums) 4 | [![Total Downloads](https://img.shields.io/packagist/dt/webfox/laravel-backed-enums.svg?style=flat-square)](https://packagist.org/packages/webfox/laravel-backed-enums) 5 | 6 | This package supercharges your PHP8 backed enums with superpowers like localization support and fluent comparison methods. 7 | 8 | ## Installation 9 | 10 | ```bash 11 | composer require webfox/laravel-backed-enums 12 | ``` 13 | 14 | ## Usage 15 | 16 | ### Make Command 17 | 18 | Creating a new Laravel Backed Enum is easy with the make:enum command. 19 | 20 | #### Command: 21 | 22 | ```Bash 23 | php artisan make:enum {name} --string # or --int 24 | ``` 25 | 26 | #### Arguments: 27 | 28 | * `{name}`: The name of the enum class to be created (e.g., OrderStatus). The command will automatically append "Enum" to the name (e.g., OrderStatusEnum). 29 | * `{type?}`: The underlying data type for the enum. Can be either --int --string or if not specified it will be a pure enum. 30 | * `{--force}`: Overwrite the enum if it already exists. 31 | Example Usage: 32 | 33 | To create an enum named OrderStatusEnum backed by integers: 34 | 35 | ``` Bash 36 | php artisan make:enum OrderStatus --int 37 | ``` 38 | 39 | To create an enum named OrderStatusEnum backed by strings: 40 | 41 | ``` Bash 42 | php artisan make:enum OrderStatus --string 43 | ``` 44 | 45 | To create a pure enum named OrderStatusEnum: 46 | 47 | ``` Bash 48 | php artisan make:enum OrderStatus 49 | 50 | ``` 51 | 52 | This will generate an OrderStatusEnums in the `app/Enums` directory. 53 | 54 | ### Upgrade your existing enums 55 | 56 | The enum you create must implement the `BackedEnum` interface and also use the `IsBackedEnum` trait. 57 | The interface is required for Laravel to cast your enum correctly and the trait is what gives your enum its superpowers. 58 | 59 | ```php 60 | use Webfox\LaravelBackedEnums\BackedEnum; 61 | use Webfox\LaravelBackedEnums\IsBackedEnum; 62 | 63 | enum VolumeUnitEnum: string implements BackedEnum 64 | { 65 | use IsBackedEnum; 66 | 67 | case MILLIGRAMS = "milligrams"; 68 | case GRAMS = "grams"; 69 | case KILOGRAMS = "kilograms"; 70 | case TONNE = "tonne"; 71 | } 72 | ``` 73 | 74 | ### Enum value labels (Localization) 75 | 76 | Create enums.php lang file and create labels for your enum values. 77 | 78 | ```php 79 | // resources/lang/en/enums.php 80 | 81 | return [ 82 | VolumeUnitEnum::class => [ 83 | VolumeUnitEnum::MILLIGRAMS->value => "mg", 84 | VolumeUnitEnum::GRAMS->value => "g", 85 | VolumeUnitEnum::KILOGRAMS->value => "kg", 86 | VolumeUnitEnum::TONNE->value => "t" 87 | ] 88 | ]; 89 | ``` 90 | 91 | You may then access these localized values using the `->label()` or `::labelFor()` methods. 92 | Additionally rendering the enum in a blade template will render the label. 93 | 94 | ```php 95 | VolumeUnitEnum::MILLIGRAMS->label(); // "mg" 96 | VolumeUnitEnum::labelFor(VolumeUnitEnum::TONNE); // "t" 97 | // in blade 98 | {{ VolumeUnitEnum::KILOGRAMS }} // "kg" 99 | ``` 100 | 101 | If you do not specify a label in the lang file these methods will return the value assigned to the enum inside the enum file. e.g MILLIGRAMS label will be milligrams. 102 | 103 | ### Meta data 104 | 105 | Adding metadata allows you to return additional values alongside the label and values. 106 | 107 | Create a withMeta method on your enum to add metadata. 108 | 109 | ```php 110 | public function withMeta(): array 111 | { 112 | return match ($this) { 113 | self::MILLIGRAMS => [ 114 | 'background_color' => 'bg-green-100', 115 | 'text_color' => 'text-green-800', 116 | ], 117 | self::GRAMS => [ 118 | 'background_color' => 'bg-red-100', 119 | 'text_color' => 'text-red-800', 120 | ], 121 | self::KILOGRAMS, self::TONNE => [ 122 | 'background_color' => 'bg-gray-100', 123 | 'text_color' => 'text-gray-800', 124 | ], 125 | default => [ 126 | 'background_color' => 'bg-blue-100', 127 | 'text_color' => 'text-blue-800', 128 | ], 129 | }; 130 | } 131 | ``` 132 | 133 | If you do not specify a `withMeta` method, meta will be an empty array. 134 | 135 | ## Other methods 136 | 137 | ### options 138 | 139 | Returns an array of all enum values with their labels and metadata. 140 | 141 | #### Usage 142 | 143 | ```php 144 | VolumeUnitEnum::options(); 145 | ``` 146 | 147 | returns 148 | 149 | ```php 150 | [ 151 | [ 152 | 'name' => 'MILLIGRAMS', 153 | 'value' => 'milligrams', 154 | 'label' => 'mg', 155 | 'meta' => [ 156 | 'background_color' => 'bg-green-100', 157 | 'text_color' => 'text-green-800', 158 | ], 159 | ], 160 | [ 161 | 'name' => 'GRAMS', 162 | 'value' => 'grams', 163 | 'label' => 'g', 164 | 'meta' => [ 165 | 'background_color' => 'bg-red-100', 166 | 'text_color' => 'text-red-800', 167 | ], 168 | ... 169 | ] 170 | ] 171 | ``` 172 | 173 | ### names 174 | 175 | Returns an array of all enum values. 176 | 177 | #### Usage 178 | 179 | ```php 180 | VolumeUnitEnum::names(); 181 | ``` 182 | 183 | returns 184 | 185 | ```php 186 | [ 187 | 'MILLIGRAMS', 188 | 'GRAMS', 189 | 'KILOGRAMS', 190 | 'TONNE', 191 | ] 192 | ``` 193 | 194 | ### values 195 | 196 | Returns an array of all enum values. 197 | 198 | #### Usage 199 | 200 | ```php 201 | VolumeUnitEnum::values(); 202 | ``` 203 | 204 | returns 205 | 206 | ```php 207 | [ 208 | 'milligrams', 209 | 'grams', 210 | 'killograms', 211 | 'tonne', 212 | ] 213 | ``` 214 | 215 | ### labels 216 | 217 | Returns an array of all enum labels. 218 | 219 | #### Usage 220 | 221 | ```php 222 | VolumeUnitEnum::labels(); 223 | ``` 224 | 225 | returns 226 | 227 | ```php 228 | [ 229 | 'mg', 230 | 'g', 231 | 'kg', 232 | 't', 233 | ] 234 | ``` 235 | 236 | ### map 237 | 238 | Returns an array of all enum values mapping to their label. 239 | 240 | #### Usage 241 | 242 | ```php 243 | VolumeUnitEnum::map(); 244 | ``` 245 | 246 | returns 247 | 248 | ```php 249 | [ 250 | 'MILLIGRAMS' => 'mg', 251 | 'GRAMS' => 'g', 252 | 'KILOGRAMS' => 'kg', 253 | 'TONNE' => 't', 254 | ] 255 | ``` 256 | 257 | ### toArray 258 | 259 | Returns an array of a single enum value with its label and metadata. 260 | 261 | #### Usage 262 | 263 | ```php 264 | VolumeUnitEnum::MILLIGRAMS->toArray(); 265 | ``` 266 | 267 | returns 268 | 269 | ```php 270 | [ 271 | 'name' => 'MILLIGRAMS', 272 | 'value' => 'milligrams', 273 | 'label' => 'mg', 274 | 'meta' => [ 275 | 'color' => 'bg-green-100', 276 | 'text_color' => 'text-green-800', 277 | ], 278 | ] 279 | ``` 280 | 281 | ### toHtml 282 | 283 | An alias of ::label(). Used to satisfy Laravel's Htmlable interface. 284 | 285 | #### Usage 286 | 287 | ```php 288 | VolumeUnitEnum::MILLIGRAMS->toHtml(); 289 | ``` 290 | 291 | returns 292 | 293 | ```php 294 | mg 295 | ``` 296 | 297 | ### toJson 298 | 299 | Returns a json string represention of the toArray return value. 300 | 301 | ### is/isA/isAn 302 | 303 | Allows you to check if an enum is a given value. Returns a boolean. 304 | > **Note** 305 | > `isA`, `isAn` are just aliases for `is`. 306 | 307 | #### Usage 308 | 309 | ```php 310 | VolumeUnitEnum::MILLIGRAMS->is(VolumeUnitEnum::MILLIGRAMS); //true 311 | VolumeUnitEnum::MILLIGRAMS->is('MILLIGRAMS'); //true 312 | VolumeUnitEnum::MILLIGRAMS->is('invalid'); //exception 313 | ``` 314 | 315 | ### isNot/isNotA/isNotAn 316 | 317 | Allows you to check if an enum is not a given value. Returns a boolean. 318 | > **Note** 319 | > `isNotA` and `isNotAn` are just aliases for `isNot`. 320 | 321 | #### Usage 322 | 323 | ```php 324 | VolumeUnitEnum::MILLIGRAMS->isNot(VolumeUnitEnum::GRAMS); //true 325 | VolumeUnitEnum::MILLIGRAMS->isNot('GRAMS'); //true 326 | VolumeUnitEnum::MILLIGRAMS->isNot('invalid'); //exception 327 | ``` 328 | 329 | ### isAny 330 | 331 | Allows you to check if an enum is contained in an array. Returns a boolean. 332 | 333 | #### Usage 334 | 335 | ```php 336 | VolumeUnitEnum::MILLIGRAMS->isAny(['GRAMS', VolumeUnitEnum::TONNE]); // false 337 | VolumeUnitEnum::MILLIGRAMS->isAny([VolumeUnitEnum::GRAMS, VolumeUnitEnum::MILLIGRAMS]); // true 338 | ``` 339 | 340 | ### isNotAny 341 | 342 | Allows you to check if an enum is not contained in an array. Returns a boolean. 343 | 344 | #### Usage 345 | 346 | ```php 347 | VolumeUnitEnum::MILLIGRAMS->isNotAny(['GRAMS', VolumeUnitEnum::TONNE]); // true 348 | VolumeUnitEnum::MILLIGRAMS->isNotAny([VolumeUnitEnum::GRAMS, VolumeUnitEnum::MILLIGRAMS]); // false 349 | ``` 350 | 351 | ### rule 352 | 353 | The backed enums may be validated using Laravel's standard Enum validation rule - `new Illuminate\Validation\Rules\Enum(VolumeUnitEnum::class)`. 354 | This method a shortcut for the validation rule. 355 | 356 | #### Usage 357 | 358 | ``` 359 | public function rules(): array 360 | { 361 | return [ 362 | 'volume_unit' => [VolumeUnitEnum::rule()], 363 | ]; 364 | } 365 | ``` 366 | 367 | ## Other Classes 368 | 369 | ### AsFullEnumCollection 370 | 371 | This cast is similar to the Laravel built in `AsEnumCollection` cast but unlike the built-in will maintain the full `toArray` structure 372 | when converting to json. 373 | 374 | E.g. the Laravel built in `AsEnumCollection` cast will return the following json: 375 | 376 | ```json 377 | [ 378 | "MILLIGRAMS", 379 | "GRAMS" 380 | ] 381 | ``` 382 | 383 | This cast will return 384 | 385 | ```json 386 | [ 387 | { 388 | "name": "MILLIGRAMS", 389 | "value": "MILLIGRAMS", 390 | "label": "mg", 391 | "meta": { 392 | "background_color": "bg-green-100", 393 | "text_color": "text-green-800" 394 | } 395 | }, 396 | { 397 | "name": "GRAMS", 398 | "value": "GRAMS", 399 | "label": "g", 400 | "meta": { 401 | "background_color": "bg-red-100", 402 | "text_color": "text-red-800" 403 | } 404 | } 405 | ] 406 | ``` 407 | 408 | ## Changelog 409 | 410 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. 411 | 412 | ## Contributing 413 | 414 | We welcome all contributors to the project. 415 | 416 | ## License 417 | 418 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 419 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webfox/laravel-backed-enums", 3 | "description": "Supercharge your PHP8 backed enums with superpowers like localization support and fluent comparison methods.", 4 | "keywords": [ 5 | "webfox", 6 | "laravel", 7 | "laravel-backed-enums" 8 | ], 9 | "homepage": "https://github.com/webfox/laravel-backed-enums", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Webfox Developments Ltd", 14 | "email": "developers@webfox.co.nz", 15 | "role": "Developer" 16 | } 17 | ], 18 | "require": { 19 | "php": "^8.1", 20 | "illuminate/console": "^10.0 | ^11.0 | ^12.0", 21 | "spatie/laravel-package-tools": "^1.18", 22 | "illuminate/contracts": "^10.0 | ^11.0 | ^12.0" 23 | }, 24 | "require-dev": { 25 | "laravel/pint": "^1.20", 26 | "nunomaduro/collision": "^7.11 | ^8.5", 27 | "pestphp/pest": "^2.36 | ^3.7", 28 | "pestphp/pest-plugin-laravel": "^2.4 | ^3.0", 29 | "orchestra/testbench": "^9.9 | ^10.0", 30 | "phpunit/phpunit": "^10.5 | ^11.5", 31 | "phpstan/extension-installer": "^1.4", 32 | "larastan/larastan": "^2.9 | ^3.0", 33 | "phpstan/phpstan-phpunit": "^1.4 | ^2.0", 34 | "phpstan/phpstan-deprecation-rules": "^1.2 | ^2.0" 35 | }, 36 | "autoload": { 37 | "psr-4": { 38 | "Webfox\\LaravelBackedEnums\\": "src" 39 | } 40 | }, 41 | "autoload-dev": { 42 | "psr-4": { 43 | "Webfox\\LaravelBackedEnums\\Tests\\": "tests", 44 | "Workbench\\App\\": "workbench/app/", 45 | "Workbench\\Database\\Factories\\": "workbench/database/factories/", 46 | "Workbench\\Database\\Seeders\\": "workbench/database/seeders/" 47 | } 48 | }, 49 | "scripts": { 50 | "analyse": "vendor/bin/phpstan analyse", 51 | "test": "vendor/bin/pest", 52 | "test-coverage": "vendor/bin/pest --coverage", 53 | "format": "vendor/bin/pint", 54 | "post-autoload-dump": [ 55 | "@clear", 56 | "@prepare" 57 | ], 58 | "clear": "@php vendor/bin/testbench package:purge-skeleton --ansi", 59 | "prepare": "@php vendor/bin/testbench package:discover --ansi", 60 | "build": "@php vendor/bin/testbench workbench:build --ansi", 61 | "serve": [ 62 | "Composer\\Config::disableProcessTimeout", 63 | "@build", 64 | "@php vendor/bin/testbench serve" 65 | ], 66 | "lint": [ 67 | "@php vendor/bin/pint", 68 | "@php vendor/bin/phpstan analyse" 69 | ] 70 | }, 71 | "config": { 72 | "sort-packages": true, 73 | "allow-plugins": { 74 | "pestphp/pest-plugin": true, 75 | "phpstan/extension-installer": true 76 | } 77 | }, 78 | "extra": { 79 | "laravel": { 80 | "providers": [ 81 | "Webfox\\LaravelBackedEnums\\LaravelBackedEnumsServiceProvider" 82 | ] 83 | } 84 | }, 85 | "minimum-stability": "dev", 86 | "prefer-stable": true 87 | } 88 | -------------------------------------------------------------------------------- /config/backed-enums.php: -------------------------------------------------------------------------------- 1 | $enumClass 26 | */ 27 | public function __construct(protected string $enumClass) 28 | { 29 | } 30 | 31 | /** 32 | * @param class-string $class 33 | */ 34 | public static function of(string $class): string 35 | { 36 | return static::class.':'.$class; 37 | } 38 | 39 | /** 40 | * @return \Illuminate\Support\Collection|null 41 | */ 42 | public function get(Model $model, string $key, mixed $value, array $attributes): ?Collection 43 | { 44 | if (empty($attributes[$key])) { 45 | return new Collection(); 46 | } 47 | 48 | $data = Json::decode($attributes[$key]); 49 | 50 | if (!is_array($data)) { 51 | throw new LogicException('Invalid data for enum collection cast: ' . $attributes[$key]); 52 | } 53 | 54 | 55 | return (new Collection($data))->map(function($value) { 56 | return is_subclass_of($this->enumClass, BackedEnum::class) 57 | ? $this->enumClass::from($value) 58 | : constant($this->enumClass . '::' . $value); 59 | }); 60 | } 61 | 62 | public function set(Model $model, string $key, mixed $value, array $attributes): array 63 | { 64 | $valueArray = Collection::wrap($value) 65 | ->map(fn($enum) => $this->getStorableEnumValue($enum)) 66 | ->jsonSerialize(); 67 | 68 | return [$key => Json::encode($valueArray)]; 69 | } 70 | 71 | protected function getStorableEnumValue($enum) 72 | { 73 | if (is_string($enum) || is_int($enum)) { 74 | return $enum; 75 | } 76 | 77 | return $enum instanceof BackedEnum ? $enum->value : $enum->name; 78 | } 79 | } -------------------------------------------------------------------------------- /src/IsBackedEnum.php: -------------------------------------------------------------------------------- 1 | 15 | * @mixin \BackedEnum 16 | * @phpstan-ignore trait.unused 17 | */ 18 | trait IsBackedEnum 19 | { 20 | 21 | protected static function ensureImplementsInterface(): void 22 | { 23 | throw_unless(class_implements(static::class, BackedEnum::class), new \Exception(sprintf('Enum %s must implement BackedEnum', static::class))); 24 | } 25 | 26 | public static function options(): array 27 | { 28 | static::ensureImplementsInterface(); 29 | return array_map(fn($enum) => $enum->toArray(), static::cases()); 30 | } 31 | 32 | public static function names(): array 33 | { 34 | static::ensureImplementsInterface(); 35 | return array_map(fn($enum) => $enum->name, static::cases()); 36 | } 37 | 38 | public static function values(): array 39 | { 40 | static::ensureImplementsInterface(); 41 | return array_map(fn($enum) => $enum->value, static::cases()); 42 | } 43 | 44 | public static function map(): array 45 | { 46 | static::ensureImplementsInterface(); 47 | $array = []; 48 | 49 | foreach (static::cases() as $enum) { 50 | $array[$enum->value] = $enum->label(); 51 | } 52 | 53 | return $array; 54 | } 55 | 56 | public static function labels(): array 57 | { 58 | static::ensureImplementsInterface(); 59 | return array_map(fn($enum) => static::labelFor($enum), static::cases()); 60 | } 61 | 62 | public static function labelFor(self $value): string 63 | { 64 | static::ensureImplementsInterface(); 65 | $lang_key = sprintf( 66 | '%s.%s.%s', 67 | 'enums', 68 | static::class, 69 | $value->value 70 | ); 71 | 72 | return app('translator')->has($lang_key) ? __($lang_key) : $value->value; 73 | } 74 | 75 | public static function rule(): EnumValidationRule 76 | { 77 | static::ensureImplementsInterface(); 78 | return new EnumValidationRule(static::class); 79 | } 80 | 81 | public function label(): string 82 | { 83 | static::ensureImplementsInterface(); 84 | return static::labelFor($this); 85 | } 86 | 87 | public function withMeta(): array 88 | { 89 | static::ensureImplementsInterface(); 90 | return []; 91 | } 92 | 93 | public function toArray(): array 94 | { 95 | static::ensureImplementsInterface(); 96 | return [ 97 | 'name' => $this->name, 98 | 'value' => $this->value, 99 | 'label' => $this->label(), 100 | 'meta' => $this->withMeta(), 101 | ]; 102 | } 103 | 104 | public function toHtml(): string 105 | { 106 | static::ensureImplementsInterface(); 107 | return $this->label(); 108 | } 109 | 110 | public function toJson($options = 0): string 111 | { 112 | static::ensureImplementsInterface(); 113 | $json = json_encode($this->toArray(), $options); 114 | 115 | if (json_last_error() !== JSON_ERROR_NONE) { 116 | throw new JsonEncodingException('Error encoding enum ['.get_class($this).'] with value ['.$this->value.'] to JSON: '. json_last_error_msg()); 117 | } 118 | 119 | return $json; 120 | } 121 | 122 | public function is(string|self $value): bool 123 | { 124 | static::ensureImplementsInterface(); 125 | return $this->isAny([$value]); 126 | } 127 | 128 | public function isA(string|self $value): bool 129 | { 130 | static::ensureImplementsInterface(); 131 | return $this->is($value); 132 | } 133 | 134 | public function isAn(string|self $value): bool 135 | { 136 | static::ensureImplementsInterface(); 137 | return $this->is($value); 138 | } 139 | 140 | public function isAny(array $values): bool 141 | { 142 | static::ensureImplementsInterface(); 143 | 144 | if (empty($values)) { 145 | return false; 146 | } 147 | 148 | $values = array_map(fn($value) => $value instanceof static ? $value : static::from($value), $values); 149 | return in_array($this, $values); 150 | } 151 | 152 | public function isNot(string|self $value): bool 153 | { 154 | static::ensureImplementsInterface(); 155 | return !$this->isAny([$value]); 156 | } 157 | 158 | public function isNotA(string|self $value): bool 159 | { 160 | static::ensureImplementsInterface(); 161 | return $this->isNot($value); 162 | } 163 | 164 | public function isNotAn(string|self $value): bool 165 | { 166 | static::ensureImplementsInterface(); 167 | return $this->isNot($value); 168 | } 169 | 170 | public function isNotAny(array $values): bool 171 | { 172 | static::ensureImplementsInterface(); 173 | return !$this->isAny($values); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/LaravelBackedEnumMakeCommand.php: -------------------------------------------------------------------------------- 1 | option('string') || $this->option('int')) { 15 | return $this->resolveStubPath('/stubs/laravel-backed-enum.stub'); 16 | } 17 | return parent::getStub(); 18 | } 19 | 20 | protected function buildClass($name): array|string 21 | { 22 | if ($this->option('string') || $this->option('int')) { 23 | return str_replace( 24 | ['{{ value }}'], 25 | $this->option('string') ? '\'standard\'' : '0', 26 | parent::buildClass($name) 27 | ); 28 | } 29 | return parent::buildClass($name); 30 | } 31 | 32 | 33 | protected function resolveStubPath($stub): string 34 | { 35 | 36 | if (file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))) { 37 | return $customPath; 38 | } 39 | 40 | if (file_exists(__DIR__ . "/../" . $stub)) { 41 | return __DIR__ . "/../" . $stub; 42 | } 43 | 44 | return parent::resolveStubPath($stub); 45 | } 46 | 47 | protected function getNameInput(): string 48 | { 49 | $name = trim($this->argument('name')); 50 | if (!preg_match('/^[A-Za-z_\x7f-\xff][A-Za-z0-9_\x7f-\xff]*$/', $name)) { 51 | throw new InvalidArgumentException('Invalid enum name format'); 52 | } 53 | 54 | if (str_ends_with($name, 'Enum')) { 55 | return $name; 56 | } 57 | 58 | return $name . 'Enum'; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/LaravelBackedEnumsServiceProvider.php: -------------------------------------------------------------------------------- 1 | name('laravel-backed-enums') 19 | ->hasConfigFile(); 20 | 21 | 22 | /** @noinspection PhpFullyQualifiedNameUsageInspection */ 23 | if (class_exists(\Illuminate\Foundation\Console\EnumMakeCommand::class)) { 24 | $package->hasConsoleCommand(LaravelBackedEnumMakeCommand::class); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /stubs/laravel-backed-enum.stub: -------------------------------------------------------------------------------- 1 |