Skip to content

Commit 1300252

Browse files
feat: improve state management with new state resolution and registration, add configurable default state
1 parent 355f953 commit 1300252

File tree

12 files changed

+145
-85
lines changed

12 files changed

+145
-85
lines changed

config/stateful-resources.php

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,31 @@
11
<?php
22

3+
use Farbcode\StatefulResources\Enums\Variant;
4+
35
return [
46
/*
57
|--------------------------------------------------------------------------
6-
| Custom States
8+
| States
79
|--------------------------------------------------------------------------
810
|
9-
| Below you may register custom resource states that you want to use inside
10-
| your stateful resources. Each state must be a valid enum class that
11-
| implements the ResourceState interface.
11+
| Below you may register the resource states that you want to use inside
12+
| your stateful resources. These can be instances of a ResourceState
13+
| or simple strings.
1214
|
1315
*/
14-
'custom_states' => [
15-
// App\Enums\CustomResourceState::class,
16+
'states' => [
17+
...Variant::cases(),
18+
//
1619
],
20+
21+
/*
22+
|--------------------------------------------------------------------------
23+
| Default State
24+
|--------------------------------------------------------------------------
25+
|
26+
| This state will be used when no state is explicitly set on the resource.
27+
| If not set, the first state in the states array will be used.
28+
|
29+
*/
30+
'default_state' => Variant::Full,
1731
];

src/Builder.php

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

33
namespace Farbcode\StatefulResources;
44

5+
use Farbcode\StatefulResources\Concerns\ResolvesState;
56
use Farbcode\StatefulResources\Contracts\ResourceState;
67
use Illuminate\Support\Facades\Context;
78

@@ -12,14 +13,24 @@
1213
*/
1314
class Builder
1415
{
16+
use ResolvesState;
17+
1518
private string $resourceClass;
1619

17-
private ResourceState $state;
20+
private string $state;
1821

19-
public function __construct(string $resourceClass, ResourceState $state)
22+
public function __construct(string $resourceClass, string|ResourceState $state)
2023
{
24+
$state = $this->resolveState($state);
25+
26+
$registeredState = app(StateRegistry::class)->tryFrom($state);
27+
28+
if ($registeredState === null) {
29+
throw new \InvalidArgumentException("State \"{$state}\" is not registered.");
30+
}
31+
2132
$this->resourceClass = $resourceClass;
22-
$this->state = $state;
33+
$this->state = $registeredState;
2334
}
2435

2536
/**

src/Concerns/ResolvesState.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace Farbcode\StatefulResources\Concerns;
4+
5+
use Farbcode\StatefulResources\Contracts\ResourceState;
6+
use Farbcode\StatefulResources\StateRegistry;
7+
8+
trait ResolvesState
9+
{
10+
/**
11+
* Resolve the value of a given state.
12+
*
13+
* @throws \InvalidArgumentException
14+
*/
15+
private function resolveState(ResourceState|string $state): string
16+
{
17+
$stateString = $state instanceof ResourceState ? (string) $state->value : $state;
18+
19+
if (app(StateRegistry::class)->tryFrom($stateString) === null) {
20+
throw new \InvalidArgumentException("State \"{$stateString}\" is not registered.");
21+
}
22+
23+
return $stateString;
24+
}
25+
}

src/Concerns/StatefullyLoadsAttributes.php

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,20 @@
2626
*/
2727
trait StatefullyLoadsAttributes
2828
{
29-
use ConditionallyLoadsAttributes;
29+
use ConditionallyLoadsAttributes, ResolvesState;
3030

3131
/**
3232
* Retrieve a value if the current state matches the given state.
3333
*
34-
* @param ResourceState $state
34+
* @param string|ResourceState $state
3535
* @param mixed $value
3636
* @param mixed $default
3737
* @return \Illuminate\Http\Resources\MissingValue|mixed
3838
*/
3939
protected function whenState($state, $value, $default = null)
4040
{
41+
$state = $this->resolveState($state);
42+
4143
if (func_num_args() === 3) {
4244
return $this->when($this->getState() === $state, $value, $default);
4345
}
@@ -48,13 +50,15 @@ protected function whenState($state, $value, $default = null)
4850
/**
4951
* Retrieve a value unless the current state matches the given state.
5052
*
51-
* @param ResourceState $state
53+
* @param string|ResourceState $state
5254
* @param mixed $value
5355
* @param mixed $default
5456
* @return \Illuminate\Http\Resources\MissingValue|mixed
5557
*/
5658
protected function unlessState($state, $value, $default = null)
5759
{
60+
$state = $this->resolveState($state);
61+
5862
if (func_num_args() === 3) {
5963
return $this->unless($this->getState() === $state, $value, $default);
6064
}
@@ -65,13 +69,15 @@ protected function unlessState($state, $value, $default = null)
6569
/**
6670
* Retrieve a value if the current state is one of the given states.
6771
*
68-
* @param array<ResourceState> $states
72+
* @param array<string|ResourceState> $states
6973
* @param mixed $value
7074
* @param mixed $default
7175
* @return \Illuminate\Http\Resources\MissingValue|mixed
7276
*/
7377
protected function whenStateIn(array $states, $value, $default = null)
7478
{
79+
$states = array_map(fn ($state) => $this->resolveState($state), $states);
80+
7581
$condition = in_array($this->getState(), $states, true);
7682

7783
if (func_num_args() === 3) {
@@ -84,13 +90,15 @@ protected function whenStateIn(array $states, $value, $default = null)
8490
/**
8591
* Retrieve a value unless the current state is one of the given states.
8692
*
87-
* @param array<ResourceState> $states
93+
* @param array<string|ResourceState> $states
8894
* @param mixed $value
8995
* @param mixed $default
9096
* @return \Illuminate\Http\Resources\MissingValue|mixed
9197
*/
9298
protected function unlessStateIn(array $states, $value, $default = null)
9399
{
100+
$states = array_map(fn ($state) => $this->resolveState($state), $states);
101+
94102
$condition = in_array($this->getState(), $states, true);
95103

96104
if (func_num_args() === 3) {
@@ -103,13 +111,15 @@ protected function unlessStateIn(array $states, $value, $default = null)
103111
/**
104112
* Merge a value if the current state matches the given state.
105113
*
106-
* @param ResourceState $state
114+
* @param string|ResourceState $state
107115
* @param mixed $value
108116
* @param mixed $default
109117
* @return \Illuminate\Http\Resources\MergeValue|mixed
110118
*/
111119
protected function mergeWhenState($state, $value, $default = null)
112120
{
121+
$state = $this->resolveState($state);
122+
113123
if (func_num_args() === 3) {
114124
return $this->mergeWhen($this->getState() === $state, $value, $default);
115125
}
@@ -120,13 +130,15 @@ protected function mergeWhenState($state, $value, $default = null)
120130
/**
121131
* Merge a value unless the current state matches the given state.
122132
*
123-
* @param ResourceState $state
133+
* @param string|ResourceState $state
124134
* @param mixed $value
125135
* @param mixed $default
126136
* @return \Illuminate\Http\Resources\MergeValue|mixed
127137
*/
128138
protected function mergeUnlessState($state, $value, $default = null)
129139
{
140+
$state = $this->resolveState($state);
141+
130142
if (func_num_args() === 3) {
131143
return $this->mergeUnless($this->getState() === $state, $value, $default);
132144
}
@@ -138,7 +150,7 @@ protected function mergeUnlessState($state, $value, $default = null)
138150
* Get the current state of the resource.
139151
* This method should be implemented by the class using this trait.
140152
*/
141-
abstract protected function getState(): ResourceState;
153+
abstract protected function getState(): string;
142154

143155
public function __call($method, $parameters)
144156
{

src/Enums/Variant.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
enum Variant: string implements ResourceState
88
{
9-
case Minimal = 'minimal';
10-
case Table = 'table';
119
case Full = 'full';
10+
case Table = 'table';
11+
case Minimal = 'minimal';
1212
}

src/StateRegistry.php

Lines changed: 36 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,69 +11,67 @@
1111
class StateRegistry
1212
{
1313
/**
14-
* @var array<string, ResourceState>
14+
* @var string[] List of registered states.
1515
*/
16-
private array $stateClasses = [];
16+
private array $states = [];
1717

1818
/**
19-
* Register a state enum class.
19+
* Register a state.
2020
*/
21-
public function register(string $stateClass): void
21+
public function register(string $state): void
2222
{
23-
if (! is_subclass_of($stateClass, ResourceState::class)) {
24-
throw new InvalidArgumentException("State class {$stateClass} must be a valid ResourceState enum.");
25-
}
26-
27-
$this->stateClasses[] = $stateClass;
23+
$this->states[] = $state;
2824
}
2925

3026
/**
31-
* Try to find a state by value across all registered state classes.
27+
* Try to find a state by value across all registered states.
3228
*/
33-
public function tryFrom(string $value): ?ResourceState
29+
public function tryFrom(string $value): ?string
3430
{
35-
foreach ($this->stateClasses as $stateClass) {
36-
$state = $stateClass::tryFrom($value);
37-
if ($state !== null) {
38-
return $state;
39-
}
31+
if (in_array($value, $this->states, true)) {
32+
return $value;
4033
}
4134

4235
return null;
4336
}
4437

4538
/**
46-
* Find a state by value across all registered state classes.
39+
* Get all available states from all registered states.
40+
*
41+
* @return string[] List of states.
4742
*/
48-
public function from(string $value): ResourceState
43+
public function all(): array
4944
{
50-
$state = $this->tryFrom($value);
51-
52-
if ($state === null) {
53-
throw new InvalidArgumentException("Unknown state: {$value}");
54-
}
55-
56-
return $state;
45+
return $this->states;
5746
}
5847

5948
/**
60-
* Get all available states from all registered classes.
49+
* Clear all registered states.
6150
*/
62-
public function all(): array
51+
public function clear(): void
6352
{
64-
$states = [];
65-
foreach ($this->stateClasses as $stateClass) {
66-
$states = array_merge($states, $stateClass::cases());
67-
}
68-
69-
return $states;
53+
$this->states = [];
7054
}
7155

72-
/**
73-
* Clear all registered state classes.
74-
*/
75-
public function clear(): void
56+
public function getDefaultState(): string
7657
{
77-
$this->stateClasses = [];
58+
$explicitDefault = config('stateful-resources.default_state');
59+
60+
if ($explicitDefault instanceof ResourceState) {
61+
$explicitDefault = $explicitDefault->value;
62+
}
63+
64+
if ($explicitDefault !== null) {
65+
$state = $this->tryFrom($explicitDefault);
66+
if ($state !== null) {
67+
return $state;
68+
}
69+
}
70+
71+
if (empty($this->states)) {
72+
throw new InvalidArgumentException('No states registered in the StateRegistry.');
73+
}
74+
75+
return $this->states[0];
7876
}
7977
}

src/StatefulJsonResource.php

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

55
use Farbcode\StatefulResources\Concerns\StatefullyLoadsAttributes;
66
use Farbcode\StatefulResources\Contracts\ResourceState;
7-
use Farbcode\StatefulResources\Enums\Variant;
87
use Illuminate\Http\Resources\Json\JsonResource;
98
use Illuminate\Support\Facades\Context;
109

@@ -17,20 +16,20 @@ abstract class StatefulJsonResource extends JsonResource
1716
{
1817
use StatefullyLoadsAttributes;
1918

20-
private ResourceState $state;
19+
private string $state;
2120

2221
/**
2322
* Create a new stateful resource builder with a specific state.
2423
*/
25-
public static function state(ResourceState $state): Builder
24+
public static function state(string|ResourceState $state): Builder
2625
{
2726
return new Builder(static::class, $state);
2827
}
2928

3029
/**
3130
* Retrieve the state of the stateful resource.
3231
*/
33-
protected function getState(): ResourceState
32+
protected function getState(): string
3433
{
3534
return $this->state;
3635
}
@@ -42,7 +41,9 @@ protected function getState(): ResourceState
4241
*/
4342
public function __construct($resource)
4443
{
45-
$this->state = Context::get('resource-state-'.static::class, Variant::Full);
44+
$defaultState = app(StateRegistry::class)->getDefaultState();
45+
46+
$this->state = Context::get('resource-state-'.static::class, $defaultState);
4647
parent::__construct($resource);
4748
}
4849

0 commit comments

Comments
 (0)