Compare commits
43 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
583984b157 | ||
|
|
8bd0bb17e4 | ||
|
|
385552f454 | ||
|
|
b671dd3e1c | ||
|
|
beb64c6470 | ||
|
|
0277f4d0bc | ||
|
|
3687d37c2f | ||
|
|
ff3f6336db | ||
|
|
7b2f3530e9 | ||
|
|
606c380e97 | ||
|
|
8b25893b99 | ||
|
|
71f45fa5ed | ||
|
|
cb2ec1bd82 | ||
|
|
57a94ab316 | ||
|
|
cc695e6a07 | ||
|
|
bbd0872389 | ||
|
|
34965dee37 | ||
|
|
1c342113b6 | ||
|
|
1e2e9bdecf | ||
| 4dfe0eb17c | |||
| 5b9806fd79 | |||
| 163eeabcd1 | |||
| 03bac5ba89 | |||
| fdfb6af639 | |||
| a7617753b1 | |||
| 19108c833e | |||
| 9c91bb3659 | |||
| c54d3602f3 | |||
| 0bb9a6f9b8 | |||
| 7bbbe28e27 | |||
| e687db37e7 | |||
| 1a5be20be2 | |||
| 5e3f825dca | |||
| b3f54a65c5 | |||
| 4cc27e6769 | |||
| 3e654f5d3c | |||
| bb09844610 | |||
| 5793cf591f | |||
| 2fb55ed30d | |||
| d8402c3397 | |||
| 402609ad07 | |||
| c80000e2fd | |||
| 8348fc06c5 |
47 changed files with 1862 additions and 7994 deletions
9
.github/workflows/php.yml
vendored
9
.github/workflows/php.yml
vendored
|
|
@ -13,6 +13,13 @@ jobs:
|
|||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: shivammathur/setup-php@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.2'
|
||||
tools: phpcs
|
||||
|
||||
- name: Validate composer.json and composer.lock
|
||||
run: composer validate --strict
|
||||
|
|
@ -27,7 +34,7 @@ jobs:
|
|||
${{ runner.os }}-php-
|
||||
|
||||
- name: Install dependencies
|
||||
run: composer install --prefer-dist --no-progress
|
||||
run: composer install --no-progress
|
||||
|
||||
# Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit"
|
||||
# Docs: https://getcomposer.org/doc/articles/scripts.md
|
||||
|
|
|
|||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,4 +1,7 @@
|
|||
workspace.code-workspace
|
||||
vendor
|
||||
.phpunit.result.cache
|
||||
.phpunit.cache
|
||||
.vscode/launch.json
|
||||
composer.lock
|
||||
build
|
||||
|
|
|
|||
|
|
@ -1,2 +0,0 @@
|
|||
php:
|
||||
preset: laravel
|
||||
124
changelog.md
124
changelog.md
|
|
@ -2,11 +2,98 @@
|
|||
|
||||
All notable changes to `Enum` will be documented in this file.
|
||||
|
||||
## Version 0.0.1
|
||||
## **Version 3.0.0**
|
||||
- Changed use of php8.2 and more only For laravel 10 usage
|
||||
|
||||
### Initial release
|
||||
## **Version 2.1.3**
|
||||
|
||||
## Version 0.0.2
|
||||
### Fixed
|
||||
|
||||
- Allow usage on php 7.4 and more as php 8 for cross compatibility
|
||||
- Bumped orchestra to 6 to allow unit test for all php versions
|
||||
|
||||
## **Version 2.1.2**
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed check for label duplicates or missing declaration
|
||||
|
||||
## **Version 2.1.1**
|
||||
|
||||
### Fixed
|
||||
|
||||
- Reintroduce getRouteKey to handle router with model + tests
|
||||
|
||||
## **Version 2.1.0**
|
||||
|
||||
### Added
|
||||
|
||||
- Full unit tests with 100% coverage
|
||||
- A laravel command to generate an Enum ``php artisan make:enum`
|
||||
- examples and a full, readme doc
|
||||
|
||||
### Changed
|
||||
|
||||
- Code refactoring for most of the code
|
||||
- Removed unused code parts
|
||||
- Set requirements for unit test to laravel 7
|
||||
- translations are in php files
|
||||
|
||||
### Fixed
|
||||
|
||||
- An enum used as in a route will now provoque a 404 code instead of generating null enum
|
||||
- CheckEnumRule nows works properly and has unit tests
|
||||
|
||||
## **Version 2.0.0**
|
||||
|
||||
### Changed
|
||||
|
||||
- Now if you are using enums in laravel, you have to extend from `BaseEnumRoutable` instead of `BaseEnum`. This will allow for enum to be used in routes as a parameter getting them as a controller parameter with implicit binding.
|
||||
- All laravel dependencies have been removed, this allows enum to be used in another project type. The project is now dependency free.
|
||||
|
||||
## **Version 0.1.4**
|
||||
|
||||
### Added
|
||||
|
||||
- Validation rule EnumIsValidRule, the constructor needs a BaseEnum class
|
||||
eg: new EnumIsValidRule(ArcadiaMailType::class)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Unit tests with 0.1.2 changes
|
||||
|
||||
## **Version 0.1.3**
|
||||
|
||||
### Added
|
||||
|
||||
- Validation rule EnumIsValidRule, the constructor needs a BaseEnum class
|
||||
eg: new EnumIsValidRule(ArcadiaMailType::class)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Unit tests with 0.1.2 changes
|
||||
|
||||
## **Version 0.1.2**
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed toJson to output array with label and value
|
||||
|
||||
## **Version 0.1.1**
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed performances issue on toValues and toLabels
|
||||
|
||||
## **Version 0.1.0**
|
||||
|
||||
### - Changed
|
||||
|
||||
- toArray Method now returns an array of BaseEnums
|
||||
this is because php numeric string array indexes to int, this would break
|
||||
origin enum values
|
||||
|
||||
## **Version 0.0.2**
|
||||
|
||||
### - Add
|
||||
|
||||
|
|
@ -21,33 +108,6 @@ All notable changes to `Enum` will be documented in this file.
|
|||
'status' => OrderStatus::class // This is a class that extends \Kwaadpepper\Enum\BaseEnum
|
||||
];
|
||||
|
||||
## Version 0.1.0
|
||||
## **Version 0.0.1**
|
||||
|
||||
### - Changed
|
||||
|
||||
- toArray Method now returns an array of BaseEnums
|
||||
this is because php numeric string array indexes to int, this would break
|
||||
origin enum values
|
||||
|
||||
## Version 0.1.1
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed performances issue on toValues and toLabels
|
||||
|
||||
## Version 0.1.2
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed toJson to output array with label and value
|
||||
|
||||
## Version 0.1.3
|
||||
|
||||
### Added
|
||||
|
||||
- Validation rule EnumIsValidRule, the constructor needs a BaseEnum class
|
||||
eg: new EnumIsValidRule(ArcadiaMailType::class)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Unit tests with 0.1.2 changes
|
||||
### Initial release
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"name": "kwaadpepper/enum",
|
||||
"type": "library",
|
||||
"description": "Enums to use for any project",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
|
|
@ -14,11 +15,16 @@
|
|||
"Laravel",
|
||||
"Enum"
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~9.0",
|
||||
"squizlabs/php_codesniffer": "^3.5",
|
||||
"orchestra/testbench": "^6.13",
|
||||
"slevomat/coding-standard": "^7.0"
|
||||
"phpunit/phpunit": "^9",
|
||||
"squizlabs/php_codesniffer": "^3",
|
||||
"orchestra/testbench": "^8",
|
||||
"slevomat/coding-standard": "^7",
|
||||
"phpunit/php-code-coverage": "^9",
|
||||
"nunomaduro/collision": "^6.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
@ -38,6 +44,14 @@
|
|||
}
|
||||
},
|
||||
"scripts": {
|
||||
"post-autoload-dump": [
|
||||
"@php ./vendor/bin/testbench package:discover --ansi"
|
||||
],
|
||||
"test": "vendor/bin/phpunit"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
7583
composer.lock
generated
7583
composer.lock
generated
File diff suppressed because it is too large
Load diff
16
examples/BasicEnum.php
Normal file
16
examples/BasicEnum.php
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnumRoutable;
|
||||
|
||||
/**
|
||||
*
|
||||
* @method static self child()
|
||||
* @method static self adult()
|
||||
* @method static self senior()
|
||||
*/
|
||||
final class BasicEnum extends BaseEnumRoutable
|
||||
{
|
||||
//
|
||||
}
|
||||
66
examples/Days.php
Normal file
66
examples/Days.php
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnumRoutable;
|
||||
|
||||
/**
|
||||
* This defines a day type with all
|
||||
* seven days of the week, include 'none'
|
||||
* to allow setting an 'undefined' value.
|
||||
*
|
||||
* @method static self none() This is used to set 'null' day
|
||||
* @method static self mon() The first day of the week
|
||||
* @method static self tue() The second day of the week
|
||||
* @method static self wed() The third day of the week
|
||||
* @method static self thu() The fourth day of the week
|
||||
* @method static self fri() The fifth day of the week
|
||||
* @method static self sat() The sixth day of the week
|
||||
* @method static self sun() The last day of the week
|
||||
*/
|
||||
final class Days extends BaseEnumRoutable
|
||||
{
|
||||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
'none' => 1,
|
||||
'mon' => 1 << 1,
|
||||
'tue' => 1 << 2,
|
||||
'wed' => 1 << 3,
|
||||
'thu' => 1 << 4,
|
||||
'fri' => 1 << 5,
|
||||
'sat' => 1 << 6,
|
||||
'sun' => 1 << 7,
|
||||
];
|
||||
}
|
||||
|
||||
protected static function labels(): array
|
||||
{
|
||||
return [
|
||||
'none' => 'None',
|
||||
'mon' => 'Monday',
|
||||
'tue' => 'Tuesday',
|
||||
'wed' => 'Wednesday',
|
||||
'thu' => 'Thursday',
|
||||
'fri' => 'Friday',
|
||||
'sat' => 'Saturday',
|
||||
'sun' => 'Sunday'
|
||||
];
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return (string)$this->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if days contain this day
|
||||
*
|
||||
* @param int $days
|
||||
* @return boolean
|
||||
*/
|
||||
public function has(int $days)
|
||||
{
|
||||
return (bool)($this->value & $days);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
MIT License (!! includes CC BY-NC 3.0 parts please read Readme.md)
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Jérémy Munsch
|
||||
|
||||
|
|
|
|||
161
phpcs.xml
161
phpcs.xml
|
|
@ -3,67 +3,82 @@
|
|||
<arg name="colors" />
|
||||
<arg name="extensions" value="php" />
|
||||
<file>src/</file>
|
||||
<rule ref="PSR1" />
|
||||
<rule ref="PSR1" />
|
||||
<rule ref="PSR12" />
|
||||
<rule ref="Generic.CodeAnalysis.EmptyStatement" />
|
||||
<rule ref="Generic.CodeAnalysis.ForLoopShouldBeWhileLoop" />
|
||||
<rule ref="Generic.CodeAnalysis.ForLoopWithTestFunctionCall" />
|
||||
<rule ref="Generic.CodeAnalysis.JumbledIncrementer" />
|
||||
<rule ref="Generic.CodeAnalysis.UnconditionalIfStatement" />
|
||||
<rule ref="Generic.CodeAnalysis.UnnecessaryFinalModifier" />
|
||||
<rule ref="Generic.CodeAnalysis.UnusedFunctionParameter" />
|
||||
<rule ref="Generic.CodeAnalysis.UselessOverridingMethod" />
|
||||
<rule ref="Generic.Commenting.Todo" />
|
||||
<rule ref="Generic.Commenting.Fixme" />
|
||||
<rule ref="Generic.ControlStructures.InlineControlStructure" />
|
||||
<rule ref="Generic.Files.ByteOrderMark" />
|
||||
<rule ref="Generic.Files.LineEndings" />
|
||||
<rule ref="Generic.Files.LineLength" />
|
||||
<rule ref="Generic.Formatting.DisallowMultipleStatements" />
|
||||
<rule ref="Generic.Formatting.MultipleStatementAlignment" />
|
||||
<rule ref="Generic.Formatting.NoSpaceAfterCast" />
|
||||
<rule ref="Generic.Functions.CallTimePassByReference" />
|
||||
<rule ref="Generic.Functions.FunctionCallArgumentSpacing" />
|
||||
<rule ref="Generic.Metrics.CyclomaticComplexity" />
|
||||
<rule ref="Generic.Metrics.NestingLevel" />
|
||||
<rule ref="Generic.NamingConventions.ConstructorName" />
|
||||
<rule ref="Generic.NamingConventions.UpperCaseConstantName" />
|
||||
<rule ref="Generic.NamingConventions.CamelCapsFunctionName" />
|
||||
<rule ref="Generic.PHP.DeprecatedFunctions" />
|
||||
<rule ref="Generic.PHP.DisallowShortOpenTag" />
|
||||
<rule ref="Generic.PHP.ForbiddenFunctions" />
|
||||
<rule ref="Generic.PHP.LowerCaseConstant" />
|
||||
<rule ref="Generic.PHP.NoSilencedErrors" />
|
||||
<rule ref="Generic.Strings.UnnecessaryStringConcat" />
|
||||
<rule ref="Generic.WhiteSpace.DisallowTabIndent" />
|
||||
<rule ref="Generic.WhiteSpace.ScopeIndent" />
|
||||
<rule ref="MySource.PHP.EvalObjectFactory" />
|
||||
<rule ref="MySource.PHP.GetRequestData" />
|
||||
<rule ref="MySource.PHP.AjaxNullComparison" />
|
||||
<rule ref="PEAR.Classes.ClassDeclaration" />
|
||||
<rule ref="PEAR.Commenting.InlineComment" />
|
||||
<rule ref="PSR1.Classes.ClassDeclaration" />
|
||||
<rule ref="PSR1.Files.SideEffects" />
|
||||
<rule ref="Squiz.PHP.CommentedOutCode" />
|
||||
<rule ref="Squiz.PHP.DisallowSizeFunctionsInLoops" />
|
||||
|
||||
<rule ref="Generic.Classes.DuplicateClassName"/>
|
||||
<rule ref="Generic.CodeAnalysis.EmptyStatement"/>
|
||||
<rule ref="Generic.CodeAnalysis.ForLoopShouldBeWhileLoop"/>
|
||||
<rule ref="Generic.CodeAnalysis.ForLoopWithTestFunctionCall"/>
|
||||
<rule ref="Generic.CodeAnalysis.JumbledIncrementer"/>
|
||||
<rule ref="Generic.CodeAnalysis.UnconditionalIfStatement"/>
|
||||
<rule ref="Generic.CodeAnalysis.UnnecessaryFinalModifier"/>
|
||||
<rule ref="Generic.CodeAnalysis.UnusedFunctionParameter"/>
|
||||
<rule ref="Generic.CodeAnalysis.UselessOverridingMethod"/>
|
||||
<rule ref="Generic.Commenting.DocComment.SpacingBeforeTags"/>
|
||||
<rule ref="Generic.Commenting.Todo"/>
|
||||
<rule ref="Generic.Commenting.Fixme"/>
|
||||
<rule ref="Generic.ControlStructures.InlineControlStructure"/>
|
||||
<rule ref="Generic.Files.ByteOrderMark"/>
|
||||
<rule ref="Generic.Files.LineEndings"/>
|
||||
<rule ref="Generic.Files.LineLength"/>
|
||||
<rule ref="Generic.Formatting.DisallowMultipleStatements"/>
|
||||
<rule ref="Generic.Formatting.MultipleStatementAlignment"/>
|
||||
<rule ref="Generic.Formatting.NoSpaceAfterCast"/>
|
||||
<rule ref="Generic.Functions.CallTimePassByReference"/>
|
||||
<rule ref="Generic.Functions.FunctionCallArgumentSpacing"/>
|
||||
<rule ref="Generic.Metrics.CyclomaticComplexity"/>
|
||||
<rule ref="Generic.Metrics.NestingLevel"/>
|
||||
<rule ref="Generic.NamingConventions.ConstructorName"/>
|
||||
<rule ref="Generic.NamingConventions.UpperCaseConstantName"/>
|
||||
<rule ref="Generic.NamingConventions.CamelCapsFunctionName"/>
|
||||
<rule ref="Generic.PHP.DeprecatedFunctions"/>
|
||||
<rule ref="Generic.PHP.DisallowShortOpenTag"/>
|
||||
<rule ref="Generic.PHP.ForbiddenFunctions"/>
|
||||
<rule ref="Generic.PHP.NoSilencedErrors"/>
|
||||
<rule ref="Generic.Strings.UnnecessaryStringConcat"/>
|
||||
<rule ref="Generic.WhiteSpace.DisallowTabIndent"/>
|
||||
<rule ref="Generic.WhiteSpace.ScopeIndent"/>
|
||||
<rule ref="MySource.PHP.EvalObjectFactory"/>
|
||||
<rule ref="MySource.PHP.GetRequestData"/>
|
||||
<rule ref="MySource.PHP.AjaxNullComparison"/>
|
||||
<rule ref="PEAR.Classes.ClassDeclaration"/>
|
||||
<rule ref="PEAR.Commenting.InlineComment"/>
|
||||
<rule ref="PSR1.Classes.ClassDeclaration"/>
|
||||
<rule ref="PSR1.Files.SideEffects"/>
|
||||
<rule ref="PSR2.Classes.ClassDeclaration"/>
|
||||
<rule ref="PSR2.Classes.PropertyDeclaration"/>
|
||||
<rule ref="PSR2.ControlStructures.ElseIfDeclaration"/>
|
||||
<rule ref="PSR2.ControlStructures.SwitchDeclaration"/>
|
||||
<rule ref="PSR2.Files.EndFileNewline"/>
|
||||
<rule ref="PSR2.Methods.MethodDeclaration"/>
|
||||
<rule ref="PSR2.Namespaces.NamespaceDeclaration"/>
|
||||
<rule ref="PSR2.Namespaces.UseDeclaration"/>
|
||||
<rule ref="Squiz.PHP.CommentedOutCode"/>
|
||||
<rule ref="Squiz.PHP.DisallowMultipleAssignments">
|
||||
<exclude name="Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure" />
|
||||
</rule>
|
||||
<rule ref="Squiz.PHP.DisallowSizeFunctionsInLoops"/>
|
||||
|
||||
<rule ref="Squiz.PHP.DiscouragedFunctions">
|
||||
<properties>
|
||||
<property name="error" value="true" />
|
||||
<property name="error" value="true"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="Squiz.PHP.Eval" />
|
||||
<rule ref="Squiz.PHP.GlobalKeyword" />
|
||||
<rule ref="Squiz.PHP.Heredoc" />
|
||||
<rule ref="Squiz.PHP.InnerFunctions" />
|
||||
<rule ref="Squiz.PHP.LowercasePHPFunctions" />
|
||||
<rule ref="Squiz.PHP.NonExecutableCode" />
|
||||
<rule ref="Squiz.Scope.MemberVarScope" />
|
||||
<rule ref="Squiz.Scope.MethodScope" />
|
||||
<rule ref="Squiz.Scope.StaticThisUsage" />
|
||||
<rule ref="Squiz.WhiteSpace.MemberVarSpacing" />
|
||||
<rule ref="Squiz.WhiteSpace.ObjectOperatorSpacing">
|
||||
<rule ref="Squiz.PHP.Eval"/>
|
||||
<rule ref="Squiz.PHP.GlobalKeyword"/>
|
||||
<rule ref="Squiz.PHP.Heredoc"/>
|
||||
<rule ref="Squiz.PHP.InnerFunctions"/>
|
||||
<rule ref="Squiz.PHP.LowercasePHPFunctions"/>
|
||||
<rule ref="Squiz.PHP.NonExecutableCode"/>
|
||||
<rule ref="Squiz.Scope.MemberVarScope"/>
|
||||
<rule ref="Squiz.Scope.MethodScope"/>
|
||||
<rule ref="Squiz.Scope.StaticThisUsage"/>
|
||||
<rule ref="Squiz.WhiteSpace.LanguageConstructSpacing"/>
|
||||
<rule ref="Squiz.WhiteSpace.LogicalOperatorSpacing"/>
|
||||
<rule ref="Squiz.WhiteSpace.MemberVarSpacing">
|
||||
<properties>
|
||||
<property name="ignoreNewlines" value="true"/>
|
||||
<property name="spacingBeforeFirst " value="false"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="Squiz.WhiteSpace.OperatorSpacing">
|
||||
|
|
@ -71,7 +86,39 @@
|
|||
<property name="ignoreNewlines" value="true"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="Squiz.WhiteSpace.ScopeKeywordSpacing" />
|
||||
<rule ref="Squiz.WhiteSpace.SemicolonSpacing" />
|
||||
<rule ref="Squiz.WhiteSpace.ObjectOperatorSpacing">
|
||||
<properties>
|
||||
<property name="ignoreNewlines" value="true"/>
|
||||
</properties>
|
||||
</rule>
|
||||
<rule ref="Squiz.WhiteSpace.ScopeKeywordSpacing"/>
|
||||
<rule ref="Squiz.WhiteSpace.SemicolonSpacing"/>
|
||||
<rule ref="Squiz.Commenting.BlockComment">
|
||||
<exclude name="Squiz.Commenting.BlockComment.WrongStart" />
|
||||
</rule>
|
||||
<rule ref="Squiz.Commenting.ClassComment">
|
||||
<exclude name="Squiz.Commenting.ClassComment.Missing" />
|
||||
<exclude name="Squiz.Commenting.ClassComment.TagNotAllowed" />
|
||||
</rule>
|
||||
<rule ref="Squiz.Commenting.DocCommentAlignment"/>
|
||||
<rule ref="Squiz.Commenting.EmptyCatchComment"/>
|
||||
<rule ref="Squiz.Commenting.FunctionComment">
|
||||
<exclude name="Squiz.Commenting.FunctionComment.MissingParamComment" />
|
||||
</rule>
|
||||
<rule ref="Squiz.Commenting.FunctionCommentThrowTag"/>
|
||||
<rule ref="Squiz.Commenting.InlineComment">
|
||||
<exclude name="Squiz.Commenting.InlineComment.DocBlock" />
|
||||
</rule>
|
||||
<rule ref="Squiz.Commenting.LongConditionClosingComment">
|
||||
<exclude name="Squiz.Commenting.LongConditionClosingComment.SpacingBefore" />
|
||||
</rule>
|
||||
<rule ref="Squiz.Commenting.PostStatementComment"/>
|
||||
<rule ref="Squiz.Commenting.VariableComment"/>
|
||||
<rule ref="SlevomatCodingStandard.Namespaces.UnusedUses.UnusedUse" />
|
||||
|
||||
<!-- For Laravel migrations -->
|
||||
<rule ref="PSR1.Classes.ClassDeclaration.MissingNamespace">
|
||||
<exclude-pattern>*/database/migrations/*</exclude-pattern>
|
||||
</rule>
|
||||
|
||||
</ruleset>
|
||||
|
|
|
|||
30
phpunit.xml
30
phpunit.xml
|
|
@ -1,13 +1,37 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" bootstrap="vendor/autoload.php" backupGlobals="false" backupStaticAttributes="false" colors="true" verbose="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
|
||||
<coverage>
|
||||
<phpunit
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
bootstrap="vendor/autoload.php"
|
||||
backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
verbose="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
|
||||
<coverage cacheDirectory=".phpunit.cache/code-coverage" processUncoveredFiles="true">
|
||||
<include>
|
||||
<directory>src/</directory>
|
||||
<directory suffix=".php">src/</directory>
|
||||
</include>
|
||||
<exclude>
|
||||
<file>src/EnumServiceProvider.php</file>
|
||||
</exclude>
|
||||
<report>
|
||||
<html outputDirectory="build/coverage"/>
|
||||
<text outputFile="build/coverage.txt"/>
|
||||
<clover outputFile="build/coverage.xml" />
|
||||
</report>
|
||||
</coverage>
|
||||
<testsuites>
|
||||
<testsuite name="Package">
|
||||
<directory suffix=".php">./tests/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<php>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
<env name="DB_CONNECTION" value="testing"/>
|
||||
</php>
|
||||
</phpunit>
|
||||
|
|
|
|||
96
readme.md
96
readme.md
|
|
@ -1,11 +1,13 @@
|
|||
# Enum
|
||||
|
||||

|
||||
[![Latest Version on Packagist][ico-version]][link-packagist]
|
||||
[![Total Downloads][ico-downloads]][link-downloads]
|
||||
[![Build Status][ico-travis]][link-travis]
|
||||
[![StyleCI][ico-styleci]][link-styleci]
|
||||

|
||||
|
||||
This is where your description should go. Take a look at [contributing.md](contributing.md) to see a to do list.
|
||||
This package goal is to provide a complete solution to lack a long time php weakness : it do not have any `enum` support.
|
||||
This will change with [**php 8 enums proposition**](https://php.watch/versions/8.1/enums). In the mean time we still a support for this allowing us to generate generic and extendable applications (instead of have singletons sql tables..).
|
||||
|
||||
You will be able to use enums in any project using a simple definition, take a look at example [Basic](examples/BasicEnum.php) or a [more sophisticated Example](examples/Days.php)
|
||||
|
||||
## Installation
|
||||
|
||||
|
|
@ -15,26 +17,110 @@ Via Composer
|
|||
$ composer require kwaadpepper/enum
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
**All enum have two properties : `label` and `value`, only value has to be unique. Each enum can have multiple options all written on class comment as static methods, these have to be unique !**
|
||||
|
||||
- On Any project, take a look on [examples](examples) to create an enum class.
|
||||
|
||||
1. **Invoke an enum value**
|
||||
``` php
|
||||
Days::mon()
|
||||
```
|
||||
|
||||
2. **Compare enums**
|
||||
``` php
|
||||
Days::mon()->equals(Days::tue()) // false
|
||||
Days::mon()->equals(Days::mon()) // true
|
||||
Days::mon()->value === Days::tue()->value // false
|
||||
Days::mon()->value === Days::mon()->value // true
|
||||
```
|
||||
|
||||
3. **Print an enum**
|
||||
``` php
|
||||
echo Days::mon(); // 2
|
||||
echo Days::tue(); // 4
|
||||
echo Days::mon()->label; // Monday
|
||||
```
|
||||
As you can see enum implements the __toString method which you can override to display the label instead of the value.
|
||||
This default behavior is set like this for a better behavior in laravel.
|
||||
|
||||
4. **Serialise an enum**
|
||||
``` php
|
||||
echo json_encode(Days::mon()); // {"label":"Monday","value":2}
|
||||
```
|
||||
Enums implement the JsonSerializable interface
|
||||
|
||||
- On a Laravel project you can use enums in multiple ways
|
||||
|
||||
1. **As a property**
|
||||
``` php
|
||||
// Add theses to your model
|
||||
use CastsEnums;
|
||||
protected $enumCasts = [
|
||||
'day' => Days::class
|
||||
];
|
||||
```
|
||||
This will allow you model to store an enum in database using its value, and then cast the property to an enum when accessing it
|
||||
|
||||
2. **As a route parameter**
|
||||
|
||||
Define a route like the following
|
||||
``` php
|
||||
Route::get('/days/{day}', function (Days $day) {
|
||||
return response()->json([$day]);
|
||||
})->middleware('bindings');
|
||||
// OR
|
||||
Route::get('/days/{day}', [DayController::class, 'getDay']);
|
||||
```
|
||||
Then on your controller for the `DayController` example
|
||||
``` php
|
||||
public function getDay(Request $request, Day $day) {
|
||||
return response()->json([$day]);
|
||||
}
|
||||
```
|
||||
|
||||
3. **As a request parameter using validation**
|
||||
|
||||
Take a look at [this request example](tests/Requests/PassDayRequest.php).
|
||||
|
||||
``` php
|
||||
new EnumIsValidRule(Days::class)
|
||||
```
|
||||
|
||||
It use EnumIsValidRule to validate the parameter as being a valid enum value.
|
||||
|
||||
**Note that you may still have to cast your parameter to int if your enum value is an int, as enum check is strict**
|
||||
|
||||
4. **As a model primary Key**
|
||||
|
||||
Take a look at [unit test](tests/BaseEnumRoutableTest.php) using [the Report enum class from tests](tests/Models/Report.php)
|
||||
|
||||
---
|
||||
## Change log
|
||||
|
||||
Please see the [changelog](changelog.md) for more information on what has changed recently.
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
``` bash
|
||||
$ composer test
|
||||
```
|
||||
|
||||
---
|
||||
## Contributing
|
||||
|
||||
Please see [contributing.md](contributing.md) for details and a todolist.
|
||||
|
||||
---
|
||||
## Security
|
||||
|
||||
If you discover any security related issues, please email github@jeremydev.ovh instead of using the issue tracker.
|
||||
|
||||
---
|
||||
## Credits
|
||||
|
||||
- [Jérémy Munsch][link-author]
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"enum::enum.isNotValid": "The field :attribute is not valid, it has to be an integer or a string.",
|
||||
"enum::enum.notInList": "The field :attribute has to be one of the followings: :values"
|
||||
}
|
||||
5
resources/lang/en/enum.php
Normal file
5
resources/lang/en/enum.php
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'notInList' => 'The field :attribute has to be one of the followings: :values'
|
||||
];
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"enum::enum.isNotValid": "Le champ :attribute n'est pas valide, il doit être un entier ou une chaine.",
|
||||
"enum::enum.notInList": "Le champ :attribute doit être une des valeurs suivantes: :values"
|
||||
}
|
||||
5
resources/lang/fr/enum.php
Normal file
5
resources/lang/fr/enum.php
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'notInList' => 'Le champ :attribute doit être une des valeurs suivantes: :values'
|
||||
];
|
||||
16
resources/templates/TemplateEnum.php
Normal file
16
resources/templates/TemplateEnum.php
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnumRoutable;
|
||||
|
||||
/**
|
||||
* This is an enumeration class,
|
||||
* the following methods are its possible values
|
||||
*
|
||||
* ##VALUES
|
||||
*/
|
||||
class TemplateEnum extends BaseEnumRoutable
|
||||
{
|
||||
// ##OVERRIDE
|
||||
}
|
||||
177
src/BaseEnum.php
177
src/BaseEnum.php
|
|
@ -2,31 +2,29 @@
|
|||
|
||||
namespace Kwaadpepper\Enum;
|
||||
|
||||
use BadMethodCallException;
|
||||
use Closure;
|
||||
use JsonSerializable;
|
||||
use Kwaadpepper\Enum\Exceptions\DuplicateDefinitionException;
|
||||
use Kwaadpepper\Enum\Exceptions\DuplicateLabelsException;
|
||||
use Kwaadpepper\Enum\Exceptions\DuplicateValuesException;
|
||||
use Kwaadpepper\Enum\Exceptions\EmptyDefinitionException;
|
||||
use Kwaadpepper\Enum\Exceptions\EnumNotRoutableException;
|
||||
use Kwaadpepper\Enum\Exceptions\UnknownEnumProperty;
|
||||
use ReflectionClass;
|
||||
use TypeError;
|
||||
|
||||
/**
|
||||
* This Base Enum is the class which should
|
||||
* be extended to create an enumeration
|
||||
*
|
||||
* @property-read int|string $value The enum value, can be string or int
|
||||
* @property-read string $label The enum label, is a string
|
||||
*/
|
||||
abstract class BaseEnum implements JsonSerializable
|
||||
abstract class BaseEnum implements \JsonSerializable
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string|int
|
||||
* @readonly
|
||||
* @var string|integer
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
* @readonly
|
||||
*/
|
||||
protected $label;
|
||||
|
||||
|
|
@ -36,10 +34,32 @@ abstract class BaseEnum implements JsonSerializable
|
|||
|
||||
// -- ENUM MAGIC METHODS --
|
||||
|
||||
/**
|
||||
* @param integer|string $value
|
||||
* @param string $label
|
||||
* @throws \TypeError If anything else than string or int
|
||||
* is used for value.
|
||||
* @throws \Kwaadpepper\Enum\Exceptions\EnumNotRoutableException If provided value are null.
|
||||
* @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
|
||||
*/
|
||||
public function __construct($value = null, string $label = null)
|
||||
{
|
||||
// phpcs:ignore
|
||||
/**
|
||||
* This cannot be allowed, happens with laravel resolver
|
||||
* when using BaseEnum instead of BaseEnumRoutable
|
||||
*/
|
||||
if ($label === null and !is_subclass_of(static::class, BaseEnumRoutable::class)) {
|
||||
throw new EnumNotRoutableException();
|
||||
}
|
||||
$this->label = $label;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $attribute
|
||||
* @return int|string
|
||||
* @throws UnknownEnumProperty
|
||||
* @return integer|string
|
||||
* @throws \Kwaadpepper\Enum\Exceptions\UnknownEnumProperty As it could not be found.
|
||||
*/
|
||||
public function __get(string $attribute)
|
||||
{
|
||||
|
|
@ -54,35 +74,22 @@ abstract class BaseEnum implements JsonSerializable
|
|||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
* @param array $arguments
|
||||
* @return static
|
||||
* @throws TypeError If anything else thant string or int is used.
|
||||
* @throws BadMethodCallException If a matching definition cannot be found.
|
||||
* @throws \TypeError If anything else than string or int is used.
|
||||
* @throws \BadMethodCallException If a matching definition cannot be found.
|
||||
* @throws \Kwaadpepper\Enum\Exceptions\EnumNotRoutableException If provided value are null.
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed
|
||||
public static function __callStatic(string $name, array $arguments)
|
||||
{
|
||||
// phpcs:enable
|
||||
return static::make($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
* @return bool|mixed
|
||||
* @throws TypeError If anything else thant string or int is used.
|
||||
* @throws BadMethodCallException If a matching definition cannot be found.
|
||||
*/
|
||||
public function __call(string $name, array $arguments)
|
||||
{
|
||||
if (strpos($name, 'is') === 0) {
|
||||
$other = static::make(substr($name, 2));
|
||||
return $this->equals($other);
|
||||
}
|
||||
return self::__callStatic($name, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the value as a string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
|
|
@ -140,7 +147,8 @@ abstract class BaseEnum implements JsonSerializable
|
|||
// -- DEFINITON METHODS --
|
||||
|
||||
/**
|
||||
* @return string[]|int[]|Closure
|
||||
* Get all values
|
||||
*
|
||||
* @return array<string, string|int> | Closure(string):(int|string)
|
||||
*/
|
||||
protected static function values()
|
||||
|
|
@ -149,7 +157,8 @@ abstract class BaseEnum implements JsonSerializable
|
|||
}
|
||||
|
||||
/**
|
||||
* @return string[]|Closure
|
||||
* Get all labels
|
||||
*
|
||||
* @return array<string, string> | Closure(string):string
|
||||
*/
|
||||
protected static function labels()
|
||||
|
|
@ -158,11 +167,11 @@ abstract class BaseEnum implements JsonSerializable
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the enum definition
|
||||
* Get the enum definition name
|
||||
*
|
||||
* @return mixed|null
|
||||
* @return string|null
|
||||
*/
|
||||
public function getDefinition()
|
||||
public function getDefinition(): ?string
|
||||
{
|
||||
$className = static::class;
|
||||
foreach (self::$definitionCache[$className] as $definition => $enum) {
|
||||
|
|
@ -176,44 +185,48 @@ abstract class BaseEnum implements JsonSerializable
|
|||
/**
|
||||
* Parse a value to create its corresponding enumeration value.
|
||||
*
|
||||
* @param string|int $value
|
||||
* @param string|integer $value
|
||||
* @return static
|
||||
* @throws TypeError If anything else thant string or int is used.
|
||||
* @throws BadMethodCallException If a matching definition cannot be found.
|
||||
* @throws TypeError If anything else than string or int is used.
|
||||
* @throws \BadMethodCallException If a matching definition cannot be found.
|
||||
* @throws \Kwaadpepper\Enum\Exceptions\EnumNotRoutableException If provided value are null.
|
||||
* @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
|
||||
*/
|
||||
public static function make($value): BaseEnum
|
||||
{
|
||||
if (!(is_string($value) || is_int($value))) {
|
||||
$enumClass = static::class;
|
||||
throw new TypeError("Only string and integer are allowed values for enum $enumClass.");
|
||||
}
|
||||
// phpcs:enable
|
||||
self::assertValidValue($value);
|
||||
|
||||
$definition = static::findDefinition($value);
|
||||
|
||||
if ($definition === null) {
|
||||
$enumClass = static::class;
|
||||
throw new BadMethodCallException(
|
||||
throw new \BadMethodCallException(
|
||||
"There's no value $value defined for enum $enumClass, consider adding it in the docblock definition."
|
||||
);
|
||||
}
|
||||
|
||||
$obj = new static();
|
||||
$obj->value = $definition->value;
|
||||
$obj->label = $definition->label;
|
||||
$obj = new static(
|
||||
$definition->value,
|
||||
$definition->label
|
||||
);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function equals(BaseEnum ...$others): bool
|
||||
/**
|
||||
* Test if an enum values is equal to another
|
||||
*
|
||||
* @param \Kwaadpepper\Enum\BaseEnum $other
|
||||
* @return boolean
|
||||
*/
|
||||
public function equals(BaseEnum $other): bool
|
||||
{
|
||||
foreach ($others as $other) {
|
||||
// @phpcs:disable PSR2.ControlStructures.ControlStructureSpacing.SpacingAfterOpenBrace
|
||||
if (
|
||||
get_class($this) === get_class($other)
|
||||
&& $this->value === $other->value
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
get_class($this) === get_class($other)
|
||||
&& $this->value === $other->value
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -233,7 +246,7 @@ abstract class BaseEnum implements JsonSerializable
|
|||
// -- PRIVATE METHODS --
|
||||
|
||||
/**
|
||||
* @param string|int $input
|
||||
* @param string|integer $input
|
||||
* @return \Kwaadpepper\Enum\BaseEnumDefinition|null
|
||||
*/
|
||||
private static function findDefinition($input): ?BaseEnumDefinition
|
||||
|
|
@ -247,15 +260,22 @@ abstract class BaseEnum implements JsonSerializable
|
|||
}
|
||||
|
||||
/**
|
||||
* Resolve enum defintion
|
||||
*
|
||||
* @return \Kwaadpepper\Enum\BaseEnumDefinition[]
|
||||
* @throws \Kwaadpepper\Enum\Exceptions\EmptyDefinitionException If there is any.
|
||||
* @throws \Kwaadpepper\Enum\Exceptions\DuplicateValuesException If there is any in labels map.
|
||||
* @throws \Kwaadpepper\Enum\Exceptions\DuplicateValuesException If there is any in values map.
|
||||
* @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
|
||||
*/
|
||||
private static function resolveDefinition(): array
|
||||
{
|
||||
// phpcs:enable
|
||||
if (isset(self::$definitionCache[static::class])) {
|
||||
return self::$definitionCache[static::class];
|
||||
}
|
||||
|
||||
$reflectionClass = new ReflectionClass(static::class);
|
||||
$reflectionClass = new \ReflectionClass(static::class);
|
||||
$docComment = $reflectionClass->getDocComment();
|
||||
|
||||
preg_match_all('/@method\s+static\s+self\s+([\w_]+)\(\)/', $docComment, $matches);
|
||||
|
|
@ -264,11 +284,11 @@ abstract class BaseEnum implements JsonSerializable
|
|||
$valueMap = static::values();
|
||||
$labelMap = static::labels();
|
||||
|
||||
if ($valueMap instanceof Closure) {
|
||||
$valueMap = array_map($valueMap, array_combine($matches[1], $matches[1]));
|
||||
if (!count($matches[1])) {
|
||||
throw new EmptyDefinitionException('You are missing defining enum definition in comments.');
|
||||
}
|
||||
if ($labelMap instanceof Closure) {
|
||||
$labelMap = array_map($labelMap, array_combine($matches[1], $matches[1]));
|
||||
if (self::arrayHasDuplicates($matches[1])) {
|
||||
throw new DuplicateDefinitionException('You have duplicate values in comments enum definition.');
|
||||
}
|
||||
|
||||
foreach ($matches[1] as $methodName) {
|
||||
|
|
@ -282,14 +302,16 @@ abstract class BaseEnum implements JsonSerializable
|
|||
}
|
||||
|
||||
if (self::arrayHasDuplicates($valueMap)) {
|
||||
throw new DuplicateValuesException();
|
||||
throw new DuplicateValuesException('You have duplicates values in enum values method.');
|
||||
}
|
||||
|
||||
if (self::arrayHasDuplicates($labelMap)) {
|
||||
throw new DuplicateLabelsException();
|
||||
throw new DuplicateLabelsException('You have duplicates values in enum labels method.');
|
||||
}
|
||||
|
||||
return self::$definitionCache[static::class] = $definition;
|
||||
self::$definitionCache[static::class] = $definition;
|
||||
|
||||
return self::$definitionCache[static::class];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -300,6 +322,27 @@ abstract class BaseEnum implements JsonSerializable
|
|||
*/
|
||||
private static function arrayHasDuplicates(array $array): bool
|
||||
{
|
||||
return count($array) > count(array_unique($array));
|
||||
$uniqueVals = [];
|
||||
foreach ($array as $value) {
|
||||
if (!in_array($value, $uniqueVals, true)) {
|
||||
$uniqueVals[] = $value;
|
||||
}
|
||||
}
|
||||
return count($array) > count($uniqueVals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert value is valid
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
* @throws \TypeError If anything else than string or int is used.
|
||||
*/
|
||||
private static function assertValidValue(mixed $value): void
|
||||
{
|
||||
if (!(is_string($value) || is_int($value))) {
|
||||
$enumClass = static::class;
|
||||
throw new \TypeError("Only string and integer are allowed values for enum $enumClass.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ namespace Kwaadpepper\Enum;
|
|||
*/
|
||||
class BaseEnumDefinition
|
||||
{
|
||||
|
||||
/** @var string|int */
|
||||
/** @var string|integer */
|
||||
public $value;
|
||||
|
||||
/** @var string */
|
||||
|
|
@ -19,9 +18,9 @@ class BaseEnumDefinition
|
|||
private $methodName;
|
||||
|
||||
/**
|
||||
* @param string $methodName
|
||||
* @param string|int $value
|
||||
* @param string $label
|
||||
* @param string $methodName
|
||||
* @param string|integer $value
|
||||
* @param string $label
|
||||
*/
|
||||
public function __construct(string $methodName, $value, string $label)
|
||||
{
|
||||
|
|
@ -31,8 +30,8 @@ class BaseEnumDefinition
|
|||
}
|
||||
|
||||
/**
|
||||
* @param string|int $input
|
||||
* @return bool
|
||||
* @param string|integer $input
|
||||
* @return boolean
|
||||
*/
|
||||
public function equals($input): bool
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ namespace Kwaadpepper\Enum;
|
|||
|
||||
use BadMethodCallException;
|
||||
use Illuminate\Contracts\Routing\UrlRoutable;
|
||||
use Kwaadpepper\Enum\Exceptions\EnumNotRoutableException;
|
||||
use Kwaadpepper\Enum\Exceptions\NotImplementedException;
|
||||
use TypeError;
|
||||
|
||||
/**
|
||||
* This Base Enum will be routable with laravel.
|
||||
|
|
@ -27,32 +29,43 @@ abstract class BaseEnumRoutable extends BaseEnum implements UrlRoutable
|
|||
/**
|
||||
* Get the route key for the model.
|
||||
*
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.InvalidNoReturn
|
||||
*
|
||||
* @return string
|
||||
* @throws \Kwaadpepper\Enum\Exceptions\NotImplementedException GetRouteKeyName not implemented,
|
||||
* should not be used.
|
||||
*/
|
||||
public function getRouteKeyName()
|
||||
{
|
||||
throw new NotImplementedException('getRouteKeyName not implemented, should not be used');
|
||||
// phpcs:enable
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new NotImplementedException('GetRouteKeyName not implemented, should not be used.');
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the model for a bound value.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param string|null $field
|
||||
* @return \Illuminate\Database\Eloquent\Model|null
|
||||
* @param mixed $value
|
||||
* @param string|null $field
|
||||
* @return \Kwaadpepper\Enum\BaseEnum|null
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
|
||||
public function resolveRouteBinding($value, $field = null)
|
||||
public function resolveRouteBinding(mixed $value, $field = null)
|
||||
{
|
||||
// try to cast numeric value first
|
||||
// phpcs:enable
|
||||
// Try to cast numeric value first.
|
||||
try {
|
||||
return static::make(is_numeric($value) ? intval($value) : $value);
|
||||
} catch (BadMethodCallException $e) {
|
||||
// try string value after that
|
||||
// Try cast to int.
|
||||
$iValue = (is_numeric($value) and floatval(intval($value)) === floatval($value)) ?
|
||||
(int)$value : $value;
|
||||
return static::make($iValue);
|
||||
} catch (BadMethodCallException | EnumNotRoutableException | TypeError $e) {
|
||||
// Try string value after that.
|
||||
try {
|
||||
return static::make($value);
|
||||
} catch (BadMethodCallException $e) {
|
||||
// could not find a suitable value
|
||||
} catch (BadMethodCallException | EnumNotRoutableException | TypeError $e) {
|
||||
// Could not find a suitable value.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -61,25 +74,22 @@ abstract class BaseEnumRoutable extends BaseEnum implements UrlRoutable
|
|||
/**
|
||||
* Retrieve the child model for a bound value.
|
||||
*
|
||||
* @param string $childType
|
||||
* @param mixed $value
|
||||
* @param string|null $field
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.InvalidNoReturn
|
||||
*
|
||||
* @param string $childType
|
||||
* @param mixed $value
|
||||
* @param string|null $field
|
||||
* @return \Illuminate\Database\Eloquent\Model|null
|
||||
* @throws NotImplementedException
|
||||
* @throws \Kwaadpepper\Enum\Exceptions\NotImplementedException ResolveChildRouteBinding not implemented,
|
||||
* should not be used.
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.ScalarTypeHintMissing
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
|
||||
*/
|
||||
public function resolveChildRouteBinding($childType, $value, $field)
|
||||
{
|
||||
throw new NotImplementedException('resolveChildRouteBinding not implemented, should not be used');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Enum name from class name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getEnumName()
|
||||
{
|
||||
$cls = explode('\\', static::class);
|
||||
return array_pop($cls);
|
||||
// phpcs:enable
|
||||
// @codeCoverageIgnoreStart
|
||||
throw new NotImplementedException('ResolveChildRouteBinding not implemented, should not be used.');
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
|
|
|
|||
201
src/Console/CreateEnum.php
Normal file
201
src/Console/CreateEnum.php
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
<?php
|
||||
|
||||
namespace Kwaadpepper\Enum\Console;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
class CreateEnum extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'make:enum';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Create a new enum class';
|
||||
|
||||
/**
|
||||
* Handle the command
|
||||
*
|
||||
* @return integer|void
|
||||
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// phpcs:enable
|
||||
$validate = false;
|
||||
$info = [
|
||||
'name' => false,
|
||||
'definition' => [],
|
||||
'labels' => [],
|
||||
'values' => []
|
||||
];
|
||||
|
||||
$this->info('Enumeration Generator.');
|
||||
|
||||
do {
|
||||
do {
|
||||
$name = ucfirst($this->ask('Which name should have the enum class ? (use PascalCase, ex: UserRole)'));
|
||||
if (\strlen($name) <= 1 or \preg_match('/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/', $name) !== 1) {
|
||||
$this->question('You shall provide a valid class name.');
|
||||
if (!$this->confirm('Do you want to continue ?')) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (File::exists(\app_path("Enums/$name.php"))) {
|
||||
$this->question("$name.php already exists.");
|
||||
if (!$this->confirm('Do you want to continue ?')) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
$info['name'] = $name;
|
||||
}
|
||||
} while ($info['name'] === false);
|
||||
|
||||
do {
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
$values = \trim($this->ask('Write down definitions separated with pipes (|), ex: admin|editor|user|guest'));
|
||||
|
||||
$info['definition'] = (\strlen($values) > 1 and \strpos($values, '|') !== 0) ?
|
||||
explode('|', $values) : [];
|
||||
|
||||
if (empty($info['definition'])) {
|
||||
$this->question('You have to provide a definition for your enum.');
|
||||
if (!$this->confirm('Do you want to continue ?')) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} while (empty($info['definition']));
|
||||
|
||||
if ($this->confirm('Do you want to add custom values to your enum ?')) {
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
$values = $this->ask('Write down your values separated with pipes (|), ex: 0|1|2|3, or \'value1\'|\'value2\'|\'value3\'');
|
||||
|
||||
$info['values'] = explode('|', $values);
|
||||
}
|
||||
if ($this->confirm('Do you want to add custom labels to your enum ?')) {
|
||||
// phpcs:ignore Generic.Files.LineLength.TooLong
|
||||
$values = $this->ask('Write down your labels separated with pipes (|), ex: When off|When on|When blinking');
|
||||
|
||||
$info['labels'] = explode('|', str_replace('\'', '\\\'', $values));
|
||||
}
|
||||
|
||||
$this->info('You have selected the followings :');
|
||||
$print = [];
|
||||
foreach ($info['definition'] as $k => $definition) {
|
||||
$print[] = [
|
||||
$definition => [
|
||||
'label' => $info['labels'][$k] ?? $definition,
|
||||
'value' => $info['values'][$k] ?? $definition,
|
||||
]
|
||||
];
|
||||
}
|
||||
$this->info(\json_encode($print, \JSON_PRETTY_PRINT | \JSON_NUMERIC_CHECK));
|
||||
|
||||
if ($this->confirm('Do you want to use these parameters ?', true)) {
|
||||
$validate = true;
|
||||
} elseif (!$this->confirm('Do you want to continue ?')) {
|
||||
return -1;
|
||||
}
|
||||
} while (!$validate);
|
||||
|
||||
if (!File::exists(\app_path('Enums'))) {
|
||||
$this->info('Creating Enums directory');
|
||||
File::makeDirectory(\app_path('Enums'));
|
||||
}
|
||||
|
||||
$this->info('Generating enum');
|
||||
|
||||
$templateEnum = File::get(__DIR__ . '/../../resources/templates/TemplateEnum.php');
|
||||
|
||||
$this->replaceClassName($templateEnum, $info);
|
||||
$this->replaceDefinition($templateEnum, $info);
|
||||
$this->insertLabelsAndValues($templateEnum, $info);
|
||||
|
||||
File::put(\app_path("Enums/$name.php"), $templateEnum);
|
||||
$this->info(\sprintf('File generated %s', \app_path("Enums/$name.php")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the class name
|
||||
*
|
||||
* @param string $templateEnum
|
||||
* @param array $info
|
||||
* @return void
|
||||
*/
|
||||
private function replaceClassName(string &$templateEnum, array $info): void
|
||||
{
|
||||
$targetDefinition = "class TemplateEnum";
|
||||
$replaceDefinition = "class {$info['name']}";
|
||||
|
||||
$templateEnum = str_replace($targetDefinition, $replaceDefinition, $templateEnum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace definition
|
||||
*
|
||||
* @param string $templateEnum
|
||||
* @param array $info
|
||||
* @return void
|
||||
*/
|
||||
private function replaceDefinition(string &$templateEnum, array $info): void
|
||||
{
|
||||
$targetDefinition = " * ##VALUES\n";
|
||||
$replaceDefinition = '';
|
||||
|
||||
foreach ($info['definition'] as $definition) {
|
||||
$replaceDefinition .= " * @method static self $definition()\n";
|
||||
}
|
||||
$replaceDefinition .= " *\n";
|
||||
|
||||
$templateEnum = str_replace($targetDefinition, $replaceDefinition, $templateEnum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert lables and values
|
||||
*
|
||||
* @param string $templateEnum
|
||||
* @param array $info
|
||||
* @return void
|
||||
*/
|
||||
private function insertLabelsAndValues(string &$templateEnum, array $info): void
|
||||
{
|
||||
$targetDefinition = " // ##OVERRIDE\n";
|
||||
$replaceDefinition = '';
|
||||
$templateValues = 'public static function values()';
|
||||
$templateLabels = 'public static function labels()';
|
||||
|
||||
if (count($info['values'])) {
|
||||
$replaceDefinition = " $templateValues\n {\n return [\n";
|
||||
foreach ($info['definition'] as $k => $definition) {
|
||||
$replaceDefinition .= sprintf(
|
||||
" '%s' => %s,\n",
|
||||
$definition,
|
||||
$info['values'][$k] ?? $definition
|
||||
);
|
||||
}
|
||||
$replaceDefinition = rtrim($replaceDefinition, ',');
|
||||
$replaceDefinition .= " ];\n }\n";
|
||||
}
|
||||
if (count($info['labels'])) {
|
||||
$replaceDefinition .= "\n $templateLabels\n {\n return [\n";
|
||||
foreach ($info['definition'] as $k => $definition) {
|
||||
$replaceDefinition .= sprintf(
|
||||
" '%s' => '%s',\n",
|
||||
$definition,
|
||||
$info['labels'][$k] ?? $definition
|
||||
);
|
||||
}
|
||||
$replaceDefinition = rtrim($replaceDefinition, ',');
|
||||
$replaceDefinition .= " ];\n }\n";
|
||||
}
|
||||
$templateEnum = str_replace($targetDefinition, $replaceDefinition, $templateEnum);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
namespace Kwaadpepper\Enum;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Kwaadpepper\Enum\Console\CreateEnum;
|
||||
|
||||
class EnumServiceProvider extends ServiceProvider
|
||||
{
|
||||
|
|
@ -13,10 +14,17 @@ class EnumServiceProvider extends ServiceProvider
|
|||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->loadJsonTranslationsFrom(__DIR__ . '/../resources/lang', 'enum');
|
||||
$this->loadTranslationsFrom(__DIR__ . '/../resources/lang', 'enum');
|
||||
|
||||
$this->publishes([
|
||||
__DIR__ . '/../resources/lang' => resource_path('lang/vendor/enum'),
|
||||
]);
|
||||
|
||||
// Register the command if we are using the application via the CLI.
|
||||
if ($this->app->runningInConsole()) {
|
||||
$this->commands([
|
||||
CreateEnum::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
15
src/Exceptions/DuplicateDefinitionException.php
Normal file
15
src/Exceptions/DuplicateDefinitionException.php
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace Kwaadpepper\Enum\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Is thrown when your enum definition
|
||||
* contain multiple times the same label value
|
||||
*
|
||||
* This should happen if you have a duplication declaration
|
||||
*/
|
||||
class DuplicateDefinitionException extends Exception
|
||||
{
|
||||
}
|
||||
|
|
@ -4,7 +4,12 @@ namespace Kwaadpepper\Enum\Exceptions;
|
|||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Is thrown when your enum definition
|
||||
* contain multiple times the same label value
|
||||
*
|
||||
* This should only happen if you override the labels method
|
||||
*/
|
||||
class DuplicateLabelsException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,12 @@ namespace Kwaadpepper\Enum\Exceptions;
|
|||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Is thrown when your enum definition
|
||||
* contain multiple times the same value
|
||||
*
|
||||
* This should only happen if you override the values method
|
||||
*/
|
||||
class DuplicateValuesException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
||||
|
|
|
|||
15
src/Exceptions/EmptyDefinitionException.php
Normal file
15
src/Exceptions/EmptyDefinitionException.php
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace Kwaadpepper\Enum\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Is thrown when your enum definition
|
||||
* contain multiple times the same label value
|
||||
*
|
||||
* This should happen if you have a duplication declaration
|
||||
*/
|
||||
class EmptyDefinitionException extends Exception
|
||||
{
|
||||
}
|
||||
15
src/Exceptions/EnumNotRoutableException.php
Normal file
15
src/Exceptions/EnumNotRoutableException.php
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace Kwaadpepper\Enum\Exceptions;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Is thrown on enum constructor
|
||||
* as it happens with laravel resolver
|
||||
* it is named like this event if it is only on
|
||||
* BaseEnum which works on any application
|
||||
*/
|
||||
class EnumNotRoutableException extends Exception
|
||||
{
|
||||
}
|
||||
|
|
@ -11,5 +11,4 @@ use Exception;
|
|||
*/
|
||||
class NotImplementedException extends Exception
|
||||
{
|
||||
//
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,4 @@ use Exception;
|
|||
|
||||
class UnknownEnumClass extends Exception
|
||||
{
|
||||
//
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,19 @@ use Exception;
|
|||
*/
|
||||
class UnknownEnumProperty extends Exception
|
||||
{
|
||||
|
||||
/**
|
||||
* The error message
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $message;
|
||||
|
||||
/**
|
||||
* Construct the exception. Note: The message is NOT binary safe.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(string $attribute)
|
||||
{
|
||||
$this->message = "Unknown attribute $attribute, enumeration only have propeties `label` and `value`.";
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
namespace Kwaadpepper\Enum\Rules;
|
||||
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
use Kwaadpepper\Enum\BaseEnum;
|
||||
use Kwaadpepper\Enum\Exceptions\UnknownEnumClass;
|
||||
|
||||
abstract class BaseEnumRule implements Rule
|
||||
{
|
||||
|
||||
/** @var string $enum */
|
||||
protected $enumClass;
|
||||
|
||||
|
|
@ -17,10 +17,11 @@ abstract class BaseEnumRule implements Rule
|
|||
*
|
||||
* @param string $enumClass
|
||||
* @return void
|
||||
* @throws \Kwaadpepper\Enum\Exceptions\UnknownEnumClass If the class passed is not an enum.
|
||||
*/
|
||||
public function __construct(string $enumClass)
|
||||
{
|
||||
if (!class_exists($enumClass)) {
|
||||
if (!class_exists($enumClass) or !is_subclass_of($enumClass, BaseEnum::class)) {
|
||||
throw new UnknownEnumClass();
|
||||
}
|
||||
$this->enumClass = $enumClass;
|
||||
|
|
@ -29,11 +30,14 @@ abstract class BaseEnumRule implements Rule
|
|||
/**
|
||||
* Determine if the validation rule passes.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
* @return boolean
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.ScalarTypeHintMissing
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
|
||||
*/
|
||||
abstract public function passes($attribute, $value): bool;
|
||||
// phpcs:enable
|
||||
|
||||
/**
|
||||
* Get the validation error message.
|
||||
|
|
|
|||
|
|
@ -5,30 +5,30 @@ namespace Kwaadpepper\Enum\Rules;
|
|||
use BadMethodCallException;
|
||||
use TypeError;
|
||||
|
||||
/**
|
||||
* Custom Rule to validate an enum
|
||||
*/
|
||||
class EnumIsValidRule extends BaseEnumRule
|
||||
{
|
||||
|
||||
private $messageType = 0;
|
||||
|
||||
/**
|
||||
* Determine if the validation rule passes.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
* @return boolean
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.ScalarTypeHintMissing
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
|
||||
*/
|
||||
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
|
||||
public function passes($attribute, $value): bool
|
||||
{
|
||||
// phpcs:enable
|
||||
try {
|
||||
// try cast to int if is int
|
||||
$value = is_numeric($value) && floatval(intval($value)) === floatval($value) ? (int)$value : $value;
|
||||
// Try cast to int if is int.
|
||||
$value = (is_numeric($value) and floatval(intval($value)) === floatval($value)) ?
|
||||
(int)$value : $value;
|
||||
forward_static_call([$this->enumClass, 'make'], $value);
|
||||
} catch (TypeError $e) {
|
||||
$this->messageType = 1;
|
||||
return false;
|
||||
} catch (BadMethodCallException $e) {
|
||||
$this->messageType = 2;
|
||||
} catch (TypeError | BadMethodCallException $e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -40,13 +40,8 @@ class EnumIsValidRule extends BaseEnumRule
|
|||
*/
|
||||
public function message(): string
|
||||
{
|
||||
switch ($this->messageType) {
|
||||
case 1:
|
||||
return trans('enum::enum.isNotValid');
|
||||
case 2:
|
||||
return trans('enum::enum.notInList', [
|
||||
'values' => implode(',', forward_static_call([$this->enumClass, 'toValues']))
|
||||
]);
|
||||
}
|
||||
return trans('enum::enum.notInList', [
|
||||
'values' => implode(',', forward_static_call([$this->enumClass, 'toValues']))
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,21 +2,27 @@
|
|||
|
||||
namespace Kwaadpepper\Enum\Traits;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Kwaadpepper\Enum\BaseEnum;
|
||||
|
||||
/**
|
||||
* @property array $attributes Laravel model attributes
|
||||
* @property array $enumCasts Map attribute names to enum classes.
|
||||
* @method bool hasCast(string $key, array|string|null $types = null)
|
||||
* @method mixed castAttribute(string $key, mixed $value)
|
||||
*/
|
||||
trait CastsEnums
|
||||
{
|
||||
/**
|
||||
* Get a plain attribute (not a relationship).
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.ScalarTypeHintMissing
|
||||
*/
|
||||
public function getAttributeValue($key)
|
||||
{
|
||||
// phpcs:enable
|
||||
$value = parent::getAttributeValue($key);
|
||||
|
||||
if ($this->hasEnumCast($key)) {
|
||||
|
|
@ -29,68 +35,83 @@ trait CastsEnums
|
|||
/**
|
||||
* Set a given attribute on the model.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return $this
|
||||
* @throws \TypeError If cast fails.
|
||||
* @throws \BadMethodCallException If cast fails.
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.ScalarTypeHintMissing
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
|
||||
*/
|
||||
public function setAttribute($key, $value)
|
||||
{
|
||||
if ($value !== null && $this->hasEnumCast($key)) {
|
||||
/** @var \App\Enums\BaseEnum $enum */
|
||||
$enum = $this->enumCasts[$key];
|
||||
|
||||
if ($value instanceof $enum) {
|
||||
$this->attributes[$key] = $value->value;
|
||||
} else {
|
||||
if ($this->hasCast($key)) {
|
||||
$value = $this->castAttribute($key, $value);
|
||||
}
|
||||
// try cast to int if is int
|
||||
$value = is_numeric($value) && floatval(intval($value)) === floatval($value) ?
|
||||
(int)$value : $value;
|
||||
|
||||
$this->attributes[$key] = $enum::make($value)->value;
|
||||
// phpcs:enable
|
||||
try {
|
||||
if ($enum = $this->castToEnum($key, $value) and $enum instanceof BaseEnum) {
|
||||
$this->attributes[$key] = $enum->value;
|
||||
return $this;
|
||||
}
|
||||
} catch (\TypeError | \BadMethodCallException $e) {
|
||||
Log::debug(\sprintf('%s : trait CastEnums error, %s', __CLASS__, $e->getMessage()));
|
||||
if (in_array(config('app.env'), ['debug', 'testing'])) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
return parent::setAttribute($key, $value);
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether an attribute should be cast to a enum.
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
* @param string $key
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasEnumCast($key): bool
|
||||
public function hasEnumCast(string $key): bool
|
||||
{
|
||||
// This can happen if this trait is added to the model
|
||||
// but no enum casts have been added yet
|
||||
/**
|
||||
* This can happen if this trait is added to the model
|
||||
* but no enum casts have been added yet.
|
||||
*/
|
||||
if ($this->enumCasts === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return array_key_exists($key, $this->enumCasts);
|
||||
return array_key_exists($key, $this->enumCasts) and
|
||||
is_subclass_of($this->enumCasts[$key], BaseEnum::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts the given key to an enum instance
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
* @return \App\Enums\BaseEnum|null
|
||||
* @return \Kwaadpepper\Enum\BaseEnum|mixed|null
|
||||
* @throws TypeError — If anything else than string or int is used.
|
||||
* @throws BadMethodCallException — If a matching definition cannot be found.
|
||||
*/
|
||||
protected function castToEnum($key, $value): ?BaseEnum
|
||||
protected function castToEnum(string $key, mixed $value)
|
||||
{
|
||||
/** @var \App\Enums\BaseEnum $enum */
|
||||
if (
|
||||
$value === null or
|
||||
(\is_object($value) and \is_subclass_of($value, BaseEnum::class)) or
|
||||
!isset($this->enumCasts[$key])
|
||||
) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ($this->hasCast($key)) {
|
||||
$value = $this->castAttribute($key, $value);
|
||||
}
|
||||
|
||||
// Try cast to int.
|
||||
$value = (is_numeric($value) and floatval(intval($value)) === floatval($value)) ?
|
||||
(int)$value : $value;
|
||||
|
||||
/** @var \Kwaadpepper\Enum\BaseEnum $enum */
|
||||
$enum = $this->enumCasts[$key];
|
||||
|
||||
if ($value === null || $value instanceof BaseEnum) {
|
||||
return $value;
|
||||
} else {
|
||||
return $enum::make($value);
|
||||
}
|
||||
return $enum::make($value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
319
tests/BaseEnumRoutableTest.php
Normal file
319
tests/BaseEnumRoutableTest.php
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
<?php
|
||||
|
||||
namespace Kwaadpepper\Enum\Tests;
|
||||
|
||||
use App\Http\Requests\PassDayRequest;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Kwaadpepper\Enum\Exceptions\EnumNotRoutableException;
|
||||
use Kwaadpepper\Enum\Exceptions\UnknownEnumClass;
|
||||
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
|
||||
use Kwaadpepper\Enum\Tests\Enums\ContactFormCivility;
|
||||
use Kwaadpepper\Enum\Tests\Enums\Days;
|
||||
use Kwaadpepper\Enum\Tests\Enums\ForceStringsFromInteger;
|
||||
use Kwaadpepper\Enum\Tests\Enums\NotRoutableEnum;
|
||||
use Kwaadpepper\Enum\Tests\Models\Alarm;
|
||||
use Kwaadpepper\Enum\Tests\Models\Journal;
|
||||
use Kwaadpepper\Enum\Tests\Models\Report;
|
||||
|
||||
class BaseEnumRoutableTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Defines aliases
|
||||
*
|
||||
* @param \Illuminate\Foundation\Application $app
|
||||
* @return void
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
|
||||
*/
|
||||
protected function getPackageAliases($app)
|
||||
{
|
||||
// phpcs:enable
|
||||
$app->alias(Alarm::class, 'Alarm');
|
||||
$app->alias(Journal::class, 'Journal');
|
||||
$app->alias(Report::class, 'Report');
|
||||
$app->alias(Days::class, 'Days');
|
||||
$app->alias(ContactFormCivility::class, 'ContactFormCivility');
|
||||
$app->alias(ForceStringsFromInteger::class, 'ForceStringsFromInteger');
|
||||
$app->alias(NotRoutableEnum::class, 'NotRoutableEnum');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get package providers.
|
||||
*
|
||||
* @param \Illuminate\Foundation\Application $app
|
||||
*
|
||||
* @return array
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
|
||||
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
|
||||
*/
|
||||
protected function getPackageProviders($app)
|
||||
{
|
||||
// phpcs:enable
|
||||
return [
|
||||
\Kwaadpepper\Enum\EnumServiceProvider::class,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines migrations
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function defineDatabaseMigrations()
|
||||
{
|
||||
Schema::create('reports', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->smallInteger('day');
|
||||
});
|
||||
Schema::create('alarms', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->smallInteger('day');
|
||||
});
|
||||
Schema::create('journals', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->smallInteger('day');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines seeders
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function defineDatabaseSeeders()
|
||||
{
|
||||
Report::create(['day' => Days::none()]);
|
||||
Report::create(['day' => Days::mon()]);
|
||||
Report::create(['day' => Days::tue()]);
|
||||
Report::create(['day' => Days::wed()]);
|
||||
Report::create(['day' => Days::thu()]);
|
||||
Report::create(['day' => Days::fri()]);
|
||||
Report::create(['day' => Days::sat()]);
|
||||
Report::create(['day' => Days::sun()]);
|
||||
Alarm::create(['day' => Days::none()]);
|
||||
Alarm::create(['day' => Days::mon()]);
|
||||
Alarm::create(['day' => Days::tue()]);
|
||||
Alarm::create(['day' => Days::wed()]);
|
||||
Alarm::create(['day' => Days::thu()]);
|
||||
Alarm::create(['day' => Days::fri()]);
|
||||
Alarm::create(['day' => Days::sat()]);
|
||||
Alarm::create(['day' => Days::sun()]);
|
||||
Journal::create(['day' => Days::none()]);
|
||||
Journal::create(['day' => Days::mon()]);
|
||||
Journal::create(['day' => Days::tue()]);
|
||||
Journal::create(['day' => Days::wed()]);
|
||||
Journal::create(['day' => Days::thu()]);
|
||||
Journal::create(['day' => Days::fri()]);
|
||||
Journal::create(['day' => Days::sat()]);
|
||||
Journal::create(['day' => Days::sun()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define routes setup.
|
||||
*
|
||||
* @param \Illuminate\Routing\Router $router
|
||||
*
|
||||
* @return void
|
||||
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
|
||||
*/
|
||||
protected function defineRoutes($router)
|
||||
{
|
||||
// phpcs:enable
|
||||
$router->get('/journals/{journal}', function (Journal $journal) {
|
||||
return response()->json([$journal->day]);
|
||||
})->middleware(\Illuminate\Routing\Middleware\SubstituteBindings::class)->name('journal.view');
|
||||
$router->get('/reports/{report}', function (Report $report) {
|
||||
return response()->json([$report->day]);
|
||||
})->middleware(\Illuminate\Routing\Middleware\SubstituteBindings::class);
|
||||
$router->get('/alarms/{alarm}', function (Alarm $alarm) {
|
||||
return response()->json([$alarm->day]);
|
||||
})->middleware(\Illuminate\Routing\Middleware\SubstituteBindings::class);
|
||||
$router->get('/days/{day}', function (Days $day) {
|
||||
return response()->json([$day]);
|
||||
})->middleware(\Illuminate\Routing\Middleware\SubstituteBindings::class);
|
||||
$router->get('/day-validation', function (PassDayRequest $request) {
|
||||
return response()->json([Days::make((int)$request->day)]);
|
||||
})->middleware(\Illuminate\Routing\Middleware\SubstituteBindings::class);
|
||||
$router->get('/civilities/{civility}', function (ContactFormCivility $civility) {
|
||||
return response()->json([$civility]);
|
||||
})->middleware(\Illuminate\Routing\Middleware\SubstituteBindings::class);
|
||||
$router->get('/force/{force}', function (ForceStringsFromInteger $force) {
|
||||
return response()->json([$force]);
|
||||
})->middleware(\Illuminate\Routing\Middleware\SubstituteBindings::class);
|
||||
$router->get('/notroutable/{notroutable}', function (NotRoutableEnum $notroutable) {
|
||||
return response()->json([$notroutable]);
|
||||
})->middleware(\Illuminate\Routing\Middleware\SubstituteBindings::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test route values
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testRoutesDays()
|
||||
{
|
||||
$response = $this->json('GET', sprintf('/days/%s', Days::none()->value));
|
||||
$response->assertOk();
|
||||
$response->assertExactJson([Days::none()]);
|
||||
$response = $this->json('GET', sprintf('/days/%s', Days::mon()->value));
|
||||
$response->assertOk();
|
||||
$response->assertExactJson([Days::mon()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test route with example civilities
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testRoutesCivilities()
|
||||
{
|
||||
$response = $this->json('GET', sprintf('/civilities/%s', ContactFormCivility::mme()->value));
|
||||
$response->assertOk();
|
||||
$response->assertExactJson([ContactFormCivility::mme()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test route with enum as string
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testRoutesWithEnumIntString()
|
||||
{
|
||||
$response = $this->json('GET', sprintf('/force/%s', ForceStringsFromInteger::valueB()->value));
|
||||
$response->assertOk();
|
||||
$response->assertExactJson([ForceStringsFromInteger::valueB()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test route with enum not found
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testRoutesWithEnumNotFound()
|
||||
{
|
||||
$response = $this->json('GET', '/force/6');
|
||||
$response->assertNotFound();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test routes with enums as pk
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testRoutesWithEnumAsPrimaryKey()
|
||||
{
|
||||
$response = $this->json('GET', sprintf('/reports/%s', Days::none()->value));
|
||||
$response->assertOk();
|
||||
$response = $this->json('GET', sprintf('/reports/%s', Days::mon()->value));
|
||||
$response->assertOk();
|
||||
$response = $this->json('GET', sprintf('/reports/%s', Days::tue()->value));
|
||||
$response->assertOk();
|
||||
$response = $this->json('GET', sprintf('/reports/%s', Days::fri()->value));
|
||||
$response->assertOk();
|
||||
$response = $this->json('GET', sprintf('/reports/%s', 9999));
|
||||
$response->assertNotFound();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test routes with enum as pk without cast enums
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testRoutesWithEnumAsPrimaryKeyWithoutCastEnums()
|
||||
{
|
||||
$response = $this->json('GET', sprintf('/alarms/%s', Days::none()->value));
|
||||
$response->assertOk();
|
||||
$response = $this->json('GET', sprintf('/alarms/%s', Days::mon()->value));
|
||||
$response->assertOk();
|
||||
$response = $this->json('GET', sprintf('/alarms/%s', Days::tue()->value));
|
||||
$response->assertOk();
|
||||
$response = $this->json('GET', sprintf('/alarms/%s', Days::fri()->value));
|
||||
$response->assertOk();
|
||||
$response = $this->json('GET', sprintf('/alarms/%s', 9999));
|
||||
$response->assertNotFound();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test routes wit henum as pk without cast enums
|
||||
*
|
||||
* ! This test includes getRouteKey method for coverage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testRoutesWithEnumAsPrimaryKeyWithoutCastEnumsAndCast()
|
||||
{
|
||||
$response = $this->json('GET', \route('journal.view', Days::none()));
|
||||
$response->assertOk();
|
||||
$response = $this->json('GET', \route('journal.view', Days::mon()));
|
||||
$response->assertOk();
|
||||
$response = $this->json('GET', \route('journal.view', Days::tue()));
|
||||
$response->assertOk();
|
||||
$response = $this->json('GET', \route('journal.view', Days::fri()));
|
||||
$response->assertOk();
|
||||
$response = $this->json('GET', \route('journal.view', 9999));
|
||||
$response->assertNotFound();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test enum is valid rule
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testRules()
|
||||
{
|
||||
$rule = new EnumIsValidRule(Days::class);
|
||||
$this->assertTrue($rule->passes('day', Days::mon()->value));
|
||||
$this->assertFalse($rule->passes('day', 9999));
|
||||
$this->assertFalse($rule->passes('day', Days::mon()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test route with enum
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testRulesRequest()
|
||||
{
|
||||
$response = $this->json('GET', sprintf('/day-validation?day=%s', Days::fri()->value));
|
||||
$response->assertOk();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test route fails on non enum
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testRulesRequestFail()
|
||||
{
|
||||
$response = $this->json('GET', sprintf('/day-validation?day=%s', '"!:'));
|
||||
$response->assertJsonValidationErrors('day');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Enum validate enum class on non enum class
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testRulesRequestInvalid()
|
||||
{
|
||||
$this->expectException(UnknownEnumClass::class);
|
||||
new EnumIsValidRule(\SplFileObject::class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test enum is not routable
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testNotRoutableEnum()
|
||||
{
|
||||
$response = $this->json('GET', sprintf('/notroutable/%s', '1'));
|
||||
$response->assertStatus(500);
|
||||
$this->assertEquals(
|
||||
new EnumNotRoutableException(),
|
||||
$response->exception
|
||||
);
|
||||
}
|
||||
}
|
||||
219
tests/BaseEnumTest.php
Normal file
219
tests/BaseEnumTest.php
Normal file
|
|
@ -0,0 +1,219 @@
|
|||
<?php
|
||||
|
||||
namespace Kwaadpepper\Enum\Tests;
|
||||
|
||||
use Kwaadpepper\Enum\Exceptions\DuplicateLabelsException;
|
||||
use Kwaadpepper\Enum\Exceptions\DuplicateValuesException;
|
||||
use Kwaadpepper\Enum\Exceptions\UnknownEnumProperty;
|
||||
use Kwaadpepper\Enum\Tests\Enums\Days;
|
||||
use Kwaadpepper\Enum\Tests\Enums\DuplicatedLabels;
|
||||
use Kwaadpepper\Enum\Tests\Enums\DuplicatedValues;
|
||||
use Kwaadpepper\Enum\Tests\Enums\ForceStringsFromInteger;
|
||||
use Kwaadpepper\Enum\Tests\Models\Report;
|
||||
|
||||
class BaseEnumTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* Test enum
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testEnum()
|
||||
{
|
||||
$mon = Days::mon();
|
||||
$tue = Days::tue();
|
||||
Days::toArray();
|
||||
Days::toValues();
|
||||
Days::toLabels();
|
||||
json_encode($tue);
|
||||
|
||||
// Basic tests.
|
||||
$this->assertEquals('Monday', $mon->label, 'Days::mon()->value should be equals to `Monday`');
|
||||
$this->assertEquals(2, $mon->value, 'Days::mon()->value should be equals to `2`');
|
||||
$this->assertTrue(\property_exists(Days::class, 'value'), 'Days should have a value property');
|
||||
$this->assertTrue(\property_exists(Days::class, 'label'), 'Days should have a label property');
|
||||
|
||||
// Undef prop test.
|
||||
$exception = null;
|
||||
try {
|
||||
$mon->propthatdontexists;
|
||||
} catch (\Exception $e) {
|
||||
$exception = $e;
|
||||
} finally {
|
||||
$this->assertEquals(
|
||||
UnknownEnumProperty::class,
|
||||
\is_object($exception) ? \get_class($exception) : $exception,
|
||||
'Accessing non existent prop on base enum should throw an UnknownEnumProperty exception'
|
||||
);
|
||||
}
|
||||
|
||||
$this->assertEquals('Days', $mon->getEnumName(), 'getEnumName should return the enum class name');
|
||||
$this->assertEquals('mon', $mon->getDefinition(), 'Days::mon() definition should be `mon`');
|
||||
$this->assertFalse($mon->equals($tue), 'Days::mon() should not be equals to Days::tue()');
|
||||
$this->assertTrue($mon->equals($mon), 'Days::mon() should be equals to Days::mon()');
|
||||
$this->assertIsArray(Days::toArray(), 'toArray method should return an array');
|
||||
$this->assertIsArray(Days::toValues(), 'toValues method should return an array');
|
||||
$this->assertIsArray(Days::toLabels(), 'toLabels method should return an array');
|
||||
$this->assertEquals([
|
||||
0 => 1,
|
||||
1 => 2,
|
||||
2 => 4,
|
||||
3 => 8,
|
||||
4 => 16,
|
||||
5 => 32,
|
||||
6 => 64,
|
||||
7 => 128
|
||||
], Days::toValues(), 'Days::toValues method should return an integer array');
|
||||
$this->assertEquals([
|
||||
0 => 'None',
|
||||
1 => 'Monday',
|
||||
2 => 'Tuesday',
|
||||
3 => 'Wednesday',
|
||||
4 => 'Thursday',
|
||||
5 => 'Friday',
|
||||
6 => 'Saturday',
|
||||
7 => 'Sunday'
|
||||
], Days::toLabels(), 'Days::toLabels method should return a string array');
|
||||
$this->assertEquals([
|
||||
0 => Days::none(),
|
||||
1 => Days::mon(),
|
||||
2 => Days::tue(),
|
||||
3 => Days::wed(),
|
||||
4 => Days::thu(),
|
||||
5 => Days::fri(),
|
||||
6 => Days::sat(),
|
||||
7 => Days::sun()
|
||||
], Days::toArray(), 'Days::toLabels method should return an enum array');
|
||||
$this->assertEquals(
|
||||
(string)ForceStringsFromInteger::valueA()->value,
|
||||
(string)ForceStringsFromInteger::valueA(),
|
||||
'BaseEnum::__toString should return the value as a string'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cannot assign an enum value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCannotAssignAnEnumValue()
|
||||
{
|
||||
$this->expectException(\Error::class);
|
||||
$enum = Days::mon();
|
||||
$enum->value = 'anything';
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cannot assign an enum label
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testCannotAssignAnEnumLabel()
|
||||
{
|
||||
$this->expectException(\Error::class);
|
||||
$enum = Days::mon();
|
||||
$enum->label = 'anything';
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is here for code coverage
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testHackingEnumValueCouldConfuseCache()
|
||||
{
|
||||
$enum = Days::mon();
|
||||
$refClass = new \ReflectionClass($enum);
|
||||
$property = $refClass->getProperty('value');
|
||||
$property->setAccessible(true);
|
||||
$property->setValue($enum, 'anything');
|
||||
$this->assertEquals('anything', $enum->value);
|
||||
$this->assertNull(
|
||||
$enum->getDefinition(),
|
||||
'BaseEnum::getDefinition should be null when hacking var consing cache'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test setting enum value onto model prop
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testValidEnumSet()
|
||||
{
|
||||
$this->expectNotToPerformAssertions();
|
||||
$report = new Report();
|
||||
$report->day = Days::mon();
|
||||
$report->day = Days::mon()->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test setting invalid enum value onto model prop
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testInvalidEnumSet()
|
||||
{
|
||||
$this->expectException(\BadMethodCallException::class);
|
||||
$report = new Report();
|
||||
$report->day = Days::mon()->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test setting prop enum with float (invalid)
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testInvalidEnumSetWithFloat()
|
||||
{
|
||||
$this->expectException(\TypeError::class);
|
||||
$report = new Report();
|
||||
$report->day = 0.1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test setting invalid enum prop with object
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testInvalidEnumSetNotAuthorizedType()
|
||||
{
|
||||
$this->expectException(\TypeError::class);
|
||||
$report = new Report();
|
||||
$report->day = new \stdClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test setting enum prop with null as valid
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testInvalidEnumCanSetNull()
|
||||
{
|
||||
$this->expectNotToPerformAssertions();
|
||||
$report = new Report();
|
||||
$report->day = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test enum has duplicated values
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDuplicatedValues()
|
||||
{
|
||||
$this->expectException(DuplicateValuesException::class);
|
||||
DuplicatedValues::one();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test enm has duplicated labels
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testDuplicatedLabels()
|
||||
{
|
||||
$this->expectException(DuplicateLabelsException::class);
|
||||
DuplicatedLabels::one();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Kwaadpepper\Enum\Tests;
|
||||
|
||||
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
|
||||
use Kwaadpepper\Enum\Tests\Enums\ContactFormCivility;
|
||||
use Kwaadpepper\Enum\Tests\Enums\Days;
|
||||
use Kwaadpepper\Enum\Tests\Enums\ForceStringsFromInteger;
|
||||
use Orchestra\Testbench\TestCase;
|
||||
|
||||
class EnumTest extends TestCase
|
||||
{
|
||||
protected function getPackageAliases($app)
|
||||
{
|
||||
$app->alias(Days::class, 'Days');
|
||||
$app->alias(ContactFormCivility::class, 'ContactFormCivility');
|
||||
$app->alias(ForceStringsFromInteger::class, 'ForceStringsFromInteger');
|
||||
}
|
||||
|
||||
/**
|
||||
* Define routes setup.
|
||||
*
|
||||
* @param \Illuminate\Routing\Router $router
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function defineRoutes($router)
|
||||
{
|
||||
$router->get('/days/{day}', function (Days $day) {
|
||||
return response()->json([$day]);
|
||||
})->middleware('bindings');
|
||||
$router->get('/civilities/{civility}', function (ContactFormCivility $civility) {
|
||||
return response()->json([$civility]);
|
||||
})->middleware('bindings');
|
||||
$router->get('/force/{force}', function (ForceStringsFromInteger $force) {
|
||||
return response()->json([$force]);
|
||||
})->middleware('bindings');
|
||||
}
|
||||
|
||||
|
||||
public function testEnum()
|
||||
{
|
||||
$mon = Days::mon();
|
||||
$tue = Days::tue();
|
||||
Days::toArray();
|
||||
Days::toValues();
|
||||
Days::toLabels();
|
||||
json_encode($tue);
|
||||
$mon->getDefinition();
|
||||
$this->assertClassHasAttribute('value', Days::class);
|
||||
$this->assertClassHasAttribute('label', Days::class);
|
||||
$this->assertFalse($mon->equals($tue));
|
||||
$this->assertTrue($mon->equals($mon));
|
||||
$this->assertIsArray(Days::toArray());
|
||||
$this->assertIsArray(Days::toValues());
|
||||
$this->assertIsArray(Days::toLabels());
|
||||
$this->assertEquals([
|
||||
0 => 0,
|
||||
1 => 1,
|
||||
2 => 2,
|
||||
3 => 4,
|
||||
4 => 8,
|
||||
5 => 16,
|
||||
6 => 32,
|
||||
7 => 64
|
||||
], Days::toValues());
|
||||
$this->assertEquals([
|
||||
0 => 'None',
|
||||
1 => 'Monday',
|
||||
2 => 'Tuesday',
|
||||
3 => 'Wednesday',
|
||||
4 => 'Thursday',
|
||||
5 => 'Friday',
|
||||
6 => 'Saturday',
|
||||
7 => 'Sunday'
|
||||
], Days::toLabels());
|
||||
$this->assertEquals([
|
||||
0 => Days::none(),
|
||||
1 => Days::mon(),
|
||||
2 => Days::tue(),
|
||||
3 => Days::wed(),
|
||||
4 => Days::thu(),
|
||||
5 => Days::fri(),
|
||||
6 => Days::sat(),
|
||||
7 => Days::sun()
|
||||
], Days::toArray());
|
||||
$this->assertEquals('Monday', (string)$mon);
|
||||
}
|
||||
|
||||
public function testRoutesDays()
|
||||
{
|
||||
$response = $this->call('GET', '/days/1');
|
||||
$response->assertOk();
|
||||
$response->assertExactJson([Days::mon()]);
|
||||
$response = $this->call('GET', '/days/2');
|
||||
$response->assertOk();
|
||||
$response->assertExactJson([Days::tue()]);
|
||||
}
|
||||
|
||||
public function testRoutesCivilities()
|
||||
{
|
||||
$response = $this->call('GET', '/civilities/mme');
|
||||
$response->assertOk();
|
||||
$response->assertExactJson([ContactFormCivility::mme()]);
|
||||
}
|
||||
|
||||
public function testRoutesWithEnumIntString()
|
||||
{
|
||||
$response = $this->call('GET', '/force/1');
|
||||
$response->assertOk();
|
||||
$response->assertExactJson([ForceStringsFromInteger::valueB()]);
|
||||
}
|
||||
|
||||
public function testRules()
|
||||
{
|
||||
$rule = new EnumIsValidRule(Days::class);
|
||||
$this->assertTrue($rule->passes('day', Days::mon()->value));
|
||||
$this->assertFalse($rule->passes('day', 9999));
|
||||
$this->assertFalse($rule->passes('day', Days::mon()));
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,11 @@ use Kwaadpepper\Enum\BaseEnumRoutable;
|
|||
*/
|
||||
final class ContactFormCivility extends BaseEnumRoutable
|
||||
{
|
||||
/**
|
||||
* Output the value as a string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return (string)$this->label;
|
||||
|
|
|
|||
|
|
@ -16,20 +16,30 @@ use Kwaadpepper\Enum\BaseEnumRoutable;
|
|||
*/
|
||||
final class Days extends BaseEnumRoutable
|
||||
{
|
||||
/**
|
||||
* All values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
'none' => 0,
|
||||
'mon' => 1,
|
||||
'tue' => 1 << 1,
|
||||
'wed' => 1 << 2,
|
||||
'thu' => 1 << 3,
|
||||
'fri' => 1 << 4,
|
||||
'sat' => 1 << 5,
|
||||
'sun' => 1 << 6,
|
||||
'none' => 1,
|
||||
'mon' => 1 << 1,
|
||||
'tue' => 1 << 2,
|
||||
'wed' => 1 << 3,
|
||||
'thu' => 1 << 4,
|
||||
'fri' => 1 << 5,
|
||||
'sat' => 1 << 6,
|
||||
'sun' => 1 << 7,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* All labels
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function labels(): array
|
||||
{
|
||||
return [
|
||||
|
|
@ -44,6 +54,11 @@ final class Days extends BaseEnumRoutable
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the value as a string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return (string)$this->label;
|
||||
|
|
@ -52,7 +67,7 @@ final class Days extends BaseEnumRoutable
|
|||
/**
|
||||
* Checks if days contain this day
|
||||
*
|
||||
* @param int $days
|
||||
* @param integer $days
|
||||
* @return boolean
|
||||
*/
|
||||
public function has(int $days)
|
||||
|
|
|
|||
27
tests/Enums/DuplicatedLabels.php
Normal file
27
tests/Enums/DuplicatedLabels.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Kwaadpepper\Enum\Tests\Enums;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnumRoutable;
|
||||
|
||||
/**
|
||||
* @method static self one()
|
||||
* @method static self two()
|
||||
* @method static self three()
|
||||
*/
|
||||
final class DuplicatedLabels extends BaseEnumRoutable
|
||||
{
|
||||
/**
|
||||
* All labels
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function labels(): array
|
||||
{
|
||||
return [
|
||||
'one' => 'one',
|
||||
'two' => 'two',
|
||||
'three' => 'two'
|
||||
];
|
||||
}
|
||||
}
|
||||
27
tests/Enums/DuplicatedValues.php
Normal file
27
tests/Enums/DuplicatedValues.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Kwaadpepper\Enum\Tests\Enums;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnumRoutable;
|
||||
|
||||
/**
|
||||
* @method static self one()
|
||||
* @method static self two()
|
||||
* @method static self three()
|
||||
*/
|
||||
final class DuplicatedValues extends BaseEnumRoutable
|
||||
{
|
||||
/**
|
||||
* All values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
'one' => '0',
|
||||
'two' => '1',
|
||||
'three' => '1'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,11 @@ use Kwaadpepper\Enum\BaseEnumRoutable;
|
|||
*/
|
||||
final class ForceStringsFromInteger extends BaseEnumRoutable
|
||||
{
|
||||
/**
|
||||
* All values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
|
|
|
|||
27
tests/Enums/NotRoutableEnum.php
Normal file
27
tests/Enums/NotRoutableEnum.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Kwaadpepper\Enum\Tests\Enums;
|
||||
|
||||
use Kwaadpepper\Enum\BaseEnum;
|
||||
|
||||
/**
|
||||
* @method static self one()
|
||||
* @method static self two()
|
||||
* @method static self three()
|
||||
*/
|
||||
final class NotRoutableEnum extends BaseEnum
|
||||
{
|
||||
/**
|
||||
* All values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function values(): array
|
||||
{
|
||||
return [
|
||||
'one' => '0',
|
||||
'two' => '1',
|
||||
'three' => '2'
|
||||
];
|
||||
}
|
||||
}
|
||||
48
tests/Models/Alarm.php
Normal file
48
tests/Models/Alarm.php
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace Kwaadpepper\Enum\Tests\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Kwaadpepper\Enum\Traits\CastsEnums;
|
||||
|
||||
class Alarm extends Model
|
||||
{
|
||||
use CastsEnums;
|
||||
|
||||
/**
|
||||
* Indicates if the model should be timestamped.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* The primary key for the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $primaryKey = 'day';
|
||||
|
||||
/**
|
||||
* The "type" of the primary key ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $keyType = 'int';
|
||||
|
||||
/**
|
||||
* Indicates if the IDs are auto-incrementing.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $incrementing = false;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
'day'
|
||||
];
|
||||
}
|
||||
67
tests/Models/Journal.php
Normal file
67
tests/Models/Journal.php
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace Kwaadpepper\Enum\Tests\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Kwaadpepper\Enum\Tests\Enums\Days;
|
||||
use Kwaadpepper\Enum\Traits\CastsEnums;
|
||||
|
||||
class Journal extends Model
|
||||
{
|
||||
use CastsEnums;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* The primary key for the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $primaryKey = 'day';
|
||||
|
||||
/**
|
||||
* The "type" of the primary key ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $keyType = 'int';
|
||||
|
||||
/**
|
||||
* Indicates if the IDs are auto-incrementing.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $incrementing = false;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
'day'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $casts = [
|
||||
'day' => 'int'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be to enum.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $enumCasts = [
|
||||
'day' => Days::class
|
||||
];
|
||||
}
|
||||
58
tests/Models/Report.php
Normal file
58
tests/Models/Report.php
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace Kwaadpepper\Enum\Tests\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Kwaadpepper\Enum\Tests\Enums\Days;
|
||||
use Kwaadpepper\Enum\Traits\CastsEnums;
|
||||
|
||||
class Report extends Model
|
||||
{
|
||||
use CastsEnums;
|
||||
|
||||
/**
|
||||
* Indicates if the model should be timestamped.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* The primary key for the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $primaryKey = 'day';
|
||||
|
||||
/**
|
||||
* The "type" of the primary key ID.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $keyType = 'int';
|
||||
|
||||
/**
|
||||
* Indicates if the IDs are auto-incrementing.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $incrementing = false;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $fillable = [
|
||||
'day'
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be to enum.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $enumCasts = [
|
||||
'day' => Days::class
|
||||
];
|
||||
}
|
||||
32
tests/Requests/PassDayRequest.php
Normal file
32
tests/Requests/PassDayRequest.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
|
||||
use Kwaadpepper\Enum\Tests\Enums\Days;
|
||||
|
||||
class PassDayRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'day' => ['required', new EnumIsValidRule(Days::class)],
|
||||
];
|
||||
}
|
||||
}
|
||||
40
tests/TestCase.php
Normal file
40
tests/TestCase.php
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace Kwaadpepper\Enum\Tests;
|
||||
|
||||
abstract class TestCase extends \Orchestra\Testbench\TestCase
|
||||
{
|
||||
/**
|
||||
* Automatically loads environment file if available.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
protected $loadEnvironmentVariables = false;
|
||||
|
||||
/**
|
||||
* Setup custom phpunit catch for deprecated error cathing.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
set_error_handler(
|
||||
static function ($errno, $errstr) {
|
||||
throw new \Exception($errstr, $errno);
|
||||
},
|
||||
E_ALL
|
||||
);
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore phpunit handler
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function tearDown(): void
|
||||
{
|
||||
restore_error_handler();
|
||||
parent::tearDown();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue