Skip to content

Commit a5f5525

Browse files
feat: add support for snake and kebab cased states
1 parent 13197ab commit a5f5525

File tree

6 files changed

+78
-6
lines changed

6 files changed

+78
-6
lines changed

src/Concerns/ResolvesState.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Farbcode\StatefulResources\Contracts\ResourceState;
66
use Farbcode\StatefulResources\StateRegistry;
7+
use Illuminate\Support\Str;
78

89
trait ResolvesState
910
{
@@ -22,4 +23,27 @@ private function resolveState(ResourceState|string $state): string
2223

2324
return $stateString;
2425
}
26+
27+
/**
28+
* Resolve a state from a method name using various case conversions.
29+
*/
30+
protected static function resolveStateFromMethodName(string $state): ?string
31+
{
32+
$registry = app(StateRegistry::class);
33+
34+
$attempts = [
35+
strtolower($state),
36+
Str::snake($state),
37+
Str::kebab($state),
38+
];
39+
40+
foreach ($attempts as $attempt) {
41+
$stateInstance = $registry->tryFrom($attempt);
42+
if ($stateInstance !== null) {
43+
return $stateInstance;
44+
}
45+
}
46+
47+
return null;
48+
}
2549
}

src/Concerns/StatefullyLoadsAttributes.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace Farbcode\StatefulResources\Concerns;
44

55
use Farbcode\StatefulResources\Contracts\ResourceState;
6-
use Farbcode\StatefulResources\StateRegistry;
76
use Illuminate\Http\Resources\ConditionallyLoadsAttributes;
87

98
/**
@@ -148,12 +147,14 @@ public function __call($method, $parameters)
148147

149148
foreach ($singleStateMethods as $singleStateMethod) {
150149
if (str_starts_with($method, $singleStateMethod)) {
151-
$state = strtolower(substr($method, strlen($singleStateMethod)));
150+
151+
$state = substr($method, strlen($singleStateMethod));
152+
152153
if (empty($state)) {
153154
continue;
154155
}
155156

156-
$stateInstance = app(StateRegistry::class)->tryFrom($state);
157+
$stateInstance = $this->resolveStateFromMethodName($state);
157158

158159
if ($stateInstance === null) {
159160
continue;

src/StatefulJsonResource.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@
22

33
namespace Farbcode\StatefulResources;
44

5+
use Farbcode\StatefulResources\Concerns\ResolvesState;
56
use Farbcode\StatefulResources\Concerns\StatefullyLoadsAttributes;
67
use Farbcode\StatefulResources\Contracts\ResourceState;
78
use Illuminate\Http\Resources\Json\JsonResource;
89
use Illuminate\Support\Facades\Context;
910

1011
abstract class StatefulJsonResource extends JsonResource
1112
{
12-
use StatefullyLoadsAttributes;
13+
use ResolvesState, StatefullyLoadsAttributes;
1314

1415
private string $state;
1516

@@ -53,7 +54,7 @@ public function __construct($resource)
5354
*/
5455
public static function __callStatic($method, $parameters)
5556
{
56-
$state = app(StateRegistry::class)->tryFrom($method);
57+
$state = self::resolveStateFromMethodName($method);
5758

5859
if ($state === null) {
5960
return parent::__callStatic($method, $parameters);

tests/Feature/CustomStatesTest.php

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
CatFactory::new()->createOne();
99
});
1010

11-
it('can use a custom user-defined state', function () {
11+
it('can use a custom state', function () {
1212
/** @var TestCase $this */
1313
$cat = Cat::firstOrFail();
1414

@@ -23,6 +23,48 @@
2323
]);
2424
});
2525

26+
it('can use a custom state with a magic method', function () {
27+
$cat = Cat::firstOrFail();
28+
29+
$resource = CatResource::custom()->make($cat)->toJson();
30+
31+
expect($resource)->toBeJson();
32+
33+
expect($resource)->json()->toEqual([
34+
'id' => $cat->id,
35+
'name' => $cat->name,
36+
'custom_field' => 'custom_value',
37+
]);
38+
});
39+
40+
it('can use a snake_cased state as camelCase when using the magic method', function () {
41+
$cat = Cat::firstOrFail();
42+
43+
$resource = CatResource::snakeCustom()->make($cat)->toJson();
44+
45+
expect($resource)->toBeJson();
46+
47+
expect($resource)->json()->toEqual([
48+
'id' => $cat->id,
49+
'name' => $cat->name,
50+
'snake_custom_field' => 'snake_custom_value',
51+
]);
52+
});
53+
54+
it('can use a kebab-cased state as camelCase when using the magic method', function () {
55+
$cat = Cat::firstOrFail();
56+
57+
$resource = CatResource::kebabCustom()->make($cat)->toJson();
58+
59+
expect($resource)->toBeJson();
60+
61+
expect($resource)->json()->toEqual([
62+
'id' => $cat->id,
63+
'name' => $cat->name,
64+
'kebab_custom_field' => 'kebab_custom_value',
65+
]);
66+
});
67+
2668
it('cannot use an unregistered state', function () {
2769
/** @var TestCase $this */
2870
$cat = Cat::firstOrFail();

tests/TestCase.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ protected function getEnvironmentSetUp($app)
3131
$app['config']->set('stateful-resources.states', [
3232
...State::cases(),
3333
'custom',
34+
'snake_custom',
35+
'kebab-custom',
3436
]);
3537
}
3638

workbench/app/Http/Resources/CatResource.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public function toArray(Request $request): array
2222
'fluffyness' => $this->whenStateIn([State::Full], $this->fluffyness),
2323
'color' => $this->whenStateIn([State::Full], $this->color),
2424
'custom_field' => $this->whenState('custom', 'custom_value'),
25+
'snake_custom_field' => $this->whenStateSnakeCustom('snake_custom_value'),
26+
'kebab_custom_field' => $this->whenStateKebabCustom('kebab_custom_value'),
2527
];
2628
}
2729
}

0 commit comments

Comments
 (0)