Skip to content

Commit 89da92b

Browse files
Karim-AshrafKarim-Ashraf
authored andcommitted
optimize Enum and handle EnumHelpers
1 parent 077d409 commit 89da92b

7 files changed

Lines changed: 144 additions & 32 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- `EnumHelpers` trait providing `values()`, `options()`, `label()`, `is()` and `isNot()` to backed enums; any helper can be overridden by redeclaring it on the enum.
13+
14+
### Changed
15+
16+
- Generated enums now use the `EnumHelpers` trait instead of duplicating the helper methods in every enum class.
17+
1018
## [1.0.0] - 2026-07-21
1119

1220
### Added

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ A unique field automatically gets `Rule::unique(...)` in the store request and `
141141

142142
### Enum fields
143143

144-
Declare a field as `enum` (e.g. `status:enum`) and, with the `enum` pattern enabled, the generator produces a string-backed enum class (`App\Enums\ProductStatus`) with `values()`, `options()` and `label()` helpers — and wires it through the whole module:
144+
Declare a field as `enum` (e.g. `status:enum`) and, with the `enum` pattern enabled, the generator produces a string-backed enum class (`App\Enums\ProductStatus`) that uses the package's `EnumHelpers` trait — giving it `values()`, `options()`, `label()`, `is()` and `isNot()` without any boilerplate. Override any helper by redeclaring it on the enum (e.g. a translated `label()`). The enum is wired through the whole module:
145145

146146
- the model casts the attribute to the enum (`'status' => ProductStatus::class`)
147147
- the form requests validate it with `Rule::enum(ProductStatus::class)`

src/Enums/Concerns/EnumHelpers.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KarimAshraf\LaraArchitect\Enums\Concerns;
6+
7+
use Illuminate\Support\Str;
8+
9+
/**
10+
* Shared helpers for backed enums. Any method can be overridden by simply
11+
* redeclaring it on the enum — a common case is a custom label():
12+
*
13+
* enum ProductStatus: string
14+
* {
15+
* use EnumHelpers;
16+
*
17+
* case Active = 'active';
18+
*
19+
* public function label(): string
20+
* {
21+
* return __("statuses.{$this->value}");
22+
* }
23+
* }
24+
*/
25+
trait EnumHelpers
26+
{
27+
/**
28+
* All case values, e.g. for validation or database columns.
29+
*
30+
* @return list<string|int>
31+
*/
32+
public static function values(): array
33+
{
34+
return array_column(self::cases(), 'value');
35+
}
36+
37+
/**
38+
* Value => label map, e.g. for select inputs.
39+
*
40+
* @return array<string|int, string>
41+
*/
42+
public static function options(): array
43+
{
44+
return array_combine(
45+
self::values(),
46+
array_map(fn (self $case): string => $case->label(), self::cases()),
47+
);
48+
}
49+
50+
/**
51+
* Human-readable label ("in_review" becomes "In Review").
52+
*/
53+
public function label(): string
54+
{
55+
return Str::headline((string) $this->value);
56+
}
57+
58+
public function is(self $other): bool
59+
{
60+
return $this === $other;
61+
}
62+
63+
public function isNot(self $other): bool
64+
{
65+
return $this !== $other;
66+
}
67+
}

stubs/enum.stub

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,17 @@
22

33
namespace {{ namespace }};
44

5-
use Illuminate\Support\Str;
5+
use KarimAshraf\LaraArchitect\Enums\Concerns\EnumHelpers;
66

77
enum {{ class }}: string
88
{
9-
case Draft = 'draft';
10-
case Active = 'active';
11-
case Archived = 'archived';
12-
139
/**
14-
* @return list<string>
10+
* Provides values(), options(), label(), is() and isNot().
11+
* Override any of them by redeclaring the method here.
1512
*/
16-
public static function values(): array
17-
{
18-
return array_column(self::cases(), 'value');
19-
}
13+
use EnumHelpers;
2014

21-
/**
22-
* @return array<string, string>
23-
*/
24-
public static function options(): array
25-
{
26-
return array_combine(
27-
self::values(),
28-
array_map(fn (self $case) => $case->label(), self::cases()),
29-
);
30-
}
31-
32-
public function label(): string
33-
{
34-
return Str::headline($this->value);
35-
}
36-
37-
public function is(self $other): bool
38-
{
39-
return $this === $other;
40-
}
15+
case Draft = 'draft';
16+
case Active = 'active';
17+
case Archived = 'archived';
4118
}

tests/Feature/MakeModuleCommandTest.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ public function test_enum_fields_generate_enums_wired_into_the_module(): void
109109

110110
$enum = File::get(app_path('Enums/ProductStatus.php'));
111111
$this->assertStringContainsString('enum ProductStatus: string', $enum);
112-
$this->assertStringContainsString('public static function values(): array', $enum);
112+
$this->assertStringContainsString('use KarimAshraf\LaraArchitect\Enums\Concerns\EnumHelpers;', $enum);
113+
$this->assertStringContainsString('use EnumHelpers;', $enum);
113114

114115
$model = File::get(app_path('Models/Product.php'));
115116
$this->assertStringContainsString('use App\Enums\ProductStatus;', $model);

tests/Fixtures/PostStatus.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44

55
namespace KarimAshraf\LaraArchitect\Tests\Fixtures;
66

7+
use KarimAshraf\LaraArchitect\Enums\Concerns\EnumHelpers;
8+
79
enum PostStatus: string
810
{
11+
use EnumHelpers;
12+
913
case Draft = 'draft';
1014
case Published = 'published';
1115
}

tests/Unit/EnumHelpersTest.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KarimAshraf\LaraArchitect\Tests\Unit;
6+
7+
use KarimAshraf\LaraArchitect\Enums\Concerns\EnumHelpers;
8+
use KarimAshraf\LaraArchitect\Tests\Fixtures\PostStatus;
9+
use KarimAshraf\LaraArchitect\Tests\TestCase;
10+
11+
enum ReviewState: string
12+
{
13+
use EnumHelpers;
14+
15+
case InReview = 'in_review';
16+
case Approved = 'approved';
17+
18+
public function label(): string
19+
{
20+
return strtoupper($this->value);
21+
}
22+
}
23+
24+
class EnumHelpersTest extends TestCase
25+
{
26+
public function test_values_returns_all_case_values(): void
27+
{
28+
$this->assertSame(['draft', 'published'], PostStatus::values());
29+
}
30+
31+
public function test_options_maps_values_to_headline_labels(): void
32+
{
33+
$this->assertSame(
34+
['draft' => 'Draft', 'published' => 'Published'],
35+
PostStatus::options(),
36+
);
37+
}
38+
39+
public function test_is_and_is_not_compare_cases(): void
40+
{
41+
$this->assertTrue(PostStatus::Draft->is(PostStatus::Draft));
42+
$this->assertFalse(PostStatus::Draft->is(PostStatus::Published));
43+
$this->assertTrue(PostStatus::Draft->isNot(PostStatus::Published));
44+
$this->assertFalse(PostStatus::Draft->isNot(PostStatus::Draft));
45+
}
46+
47+
public function test_label_can_be_overridden_on_the_enum(): void
48+
{
49+
$this->assertSame('IN_REVIEW', ReviewState::InReview->label());
50+
$this->assertSame(
51+
['in_review' => 'IN_REVIEW', 'approved' => 'APPROVED'],
52+
ReviewState::options(),
53+
);
54+
}
55+
}

0 commit comments

Comments
 (0)