Skip to content

Commit

Permalink
Merge pull request #203 from javoscript/manually-register-states
Browse files Browse the repository at this point in the history
Manually register concrete state classes
  • Loading branch information
freekmurze authored Jun 7, 2022
2 parents ebdfb65 + 0a9f0dd commit 976e5dc
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 0 deletions.
28 changes: 28 additions & 0 deletions docs/working-with-states/01-configuring-states.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,32 @@ abstract class PaymentState extends State
}
```

## Manually registering states
If you want to place your concrete state implementations in a different directory, you may do so and register them manually:

```php
use Spatie\ModelStates\State;
use Spatie\ModelStates\StateConfig;

use Your\Concrete\State\Class\Cancelled; // this may be wherever you want
use Your\Concrete\State\Class\ExampleOne;
use Your\Concrete\State\Class\ExampleTwo;

abstract class PaymentState extends State
{
abstract public function color(): string;

public static function config(): StateConfig
{
return parent::config()
->default(Pending::class)
->allowTransition(Pending::class, Paid::class)
->allowTransition(Pending::class, Failed::class)
->registerState(Cancelled::class)
->registerState([ExampleOne::class, ExampleTwo::class])
;
}
}
```

Next up, we'll take a moment to discuss how state classes are serialized to the database.
4 changes: 4 additions & 0 deletions src/State.php
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,10 @@ private static function resolveStateMapping(): array
$resolvedStates[$stateClass::getMorphClass()] = $stateClass;
}

foreach ($stateConfig->registeredStates as $stateClass) {
$resolvedStates[$stateClass::getMorphClass()] = $stateClass;
}

return $resolvedStates;
}
}
22 changes: 22 additions & 0 deletions src/StateConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ class StateConfig
/** @var string[] */
public array $allowedTransitions = [];

/** @var string[] */
public array $registeredStates = [];

public function __construct(
string $baseStateClass
) {
Expand Down Expand Up @@ -95,6 +98,25 @@ public function transitionableStates(string $fromMorphClass): array
return $transitionableStates;
}

public function registerState($stateClass): StateConfig
{
if (is_array($stateClass)) {
foreach ($stateClass as $state) {
$this->registerState($state);
}

return $this;
}

if (!is_subclass_of($stateClass, $this->baseStateClass)) {
throw InvalidConfig::doesNotExtendBaseClass($stateClass, $this->baseStateClass);
}

$this->registeredStates[] = $stateClass;

return $this;
}

/**
* @param string|\Spatie\ModelStates\State $from
* @param string|\Spatie\ModelStates\State $to
Expand Down
9 changes: 9 additions & 0 deletions tests/Dummy/ModelStates/AnotherDirectory/StateF.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace Spatie\ModelStates\Tests\Dummy\ModelStates\AnotherDirectory;

use Spatie\ModelStates\Tests\Dummy\ModelStates\ModelState;

class StateF extends ModelState
{
}
10 changes: 10 additions & 0 deletions tests/Dummy/ModelStates/AnotherDirectory/StateG.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Spatie\ModelStates\Tests\Dummy\ModelStates\AnotherDirectory;

use Spatie\ModelStates\Tests\Dummy\ModelStates\ModelState;

class StateG extends ModelState
{
public static string $name = 'G';
}
10 changes: 10 additions & 0 deletions tests/Dummy/ModelStates/AnotherDirectory/StateH.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Spatie\ModelStates\Tests\Dummy\ModelStates\AnotherDirectory;

use Spatie\ModelStates\Tests\Dummy\ModelStates\ModelState;

class StateH extends ModelState
{
public static int $name = 8;
}
6 changes: 6 additions & 0 deletions tests/Dummy/ModelStates/ModelState.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

use Spatie\ModelStates\State;
use Spatie\ModelStates\StateConfig;
use Spatie\ModelStates\Tests\Dummy\ModelStates\AnotherDirectory\StateF;
use Spatie\ModelStates\Tests\Dummy\ModelStates\AnotherDirectory\StateG;
use Spatie\ModelStates\Tests\Dummy\ModelStates\AnotherDirectory\StateH;

abstract class ModelState extends State
{
Expand All @@ -13,6 +16,9 @@ public static function config(): StateConfig
->allowTransition(StateA::class, StateB::class)
->allowTransition([StateA::class, StateB::class], StateC::class)
->allowTransition(StateA::class, StateD::class)
->allowTransition(StateA::class, StateF::class)
->registerState(StateF::class)
->registerState([StateG::class, StateH::class])
->default(StateA::class);
}
}
44 changes: 44 additions & 0 deletions tests/StateCastingTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
use Spatie\ModelStates\Tests\Dummy\ModelStates\ModelState;
use Spatie\ModelStates\Tests\Dummy\ModelStates\StateA;
use Spatie\ModelStates\Tests\Dummy\ModelStates\StateC;
use Spatie\ModelStates\Tests\Dummy\ModelStates\AnotherDirectory\StateF;
use Spatie\ModelStates\Tests\Dummy\ModelStates\AnotherDirectory\StateG;
use Spatie\ModelStates\Tests\Dummy\TestModel;

class StateCastingTest extends TestCase
Expand All @@ -24,6 +26,20 @@ public function state_without_alias_is_serialized_on_create()
]);
}

/** @test */
public function custom_registered_state_without_alias_is_serialized_on_create()
{
$model = TestModel::create([
'state' => StateF::class,
]);

$this->assertInstanceOf(StateF::class, $model->state);

$this->assertDatabaseHas($model->getTable(), [
'state' => StateF::getMorphClass(),
]);
}

/** @test */
public function state_with_alias_is_serialized_on_create_when_using_class_name()
{
Expand All @@ -38,6 +54,20 @@ public function state_with_alias_is_serialized_on_create_when_using_class_name()
]);
}

/** @test */
public function custom_registered_state_with_alias_is_serialized_on_create_when_using_class_name()
{
$model = TestModel::create([
'state' => StateG::class,
]);

$this->assertInstanceOf(StateG::class, $model->state);

$this->assertDatabaseHas($model->getTable(), [
'state' => StateG::getMorphClass(),
]);
}

/** @test */
public function state_with_alias_is_serialized_on_create_when_using_alias()
{
Expand All @@ -52,6 +82,20 @@ public function state_with_alias_is_serialized_on_create_when_using_alias()
]);
}

/** @test */
public function custom_registered_state_with_alias_is_serialized_on_create_when_using_alias()
{
$model = TestModel::create([
'state' => StateG::getMorphClass(),
]);

$this->assertInstanceOf(StateG::class, $model->state);

$this->assertDatabaseHas($model->getTable(), [
'state' => StateG::getMorphClass(),
]);
}

/** @test */
public function state_is_immediately_unserialized_on_property_set()
{
Expand Down
19 changes: 19 additions & 0 deletions tests/StateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
use Spatie\ModelStates\Tests\Dummy\ModelStates\StateC;
use Spatie\ModelStates\Tests\Dummy\ModelStates\StateD;
use Spatie\ModelStates\Tests\Dummy\ModelStates\StateE;
use Spatie\ModelStates\Tests\Dummy\ModelStates\AnotherDirectory\StateF;
use Spatie\ModelStates\Tests\Dummy\ModelStates\AnotherDirectory\StateG;
use Spatie\ModelStates\Tests\Dummy\ModelStates\AnotherDirectory\StateH;
use Spatie\ModelStates\Tests\Dummy\OtherModelStates\StateX;
use Spatie\ModelStates\Tests\Dummy\OtherModelStates\StateY;
use Spatie\ModelStates\Tests\Dummy\TestModel;
Expand All @@ -30,6 +33,11 @@ public function test_resolve_state_class()
$this->assertEquals(StateD::class, ModelState::resolveStateClass(StateD::$name));
$this->assertEquals(StateE::class, ModelState::resolveStateClass(StateE::class));
$this->assertEquals(StateE::class, ModelState::resolveStateClass(StateE::getMorphClass()));
$this->assertEquals(StateF::class, ModelState::resolveStateClass(StateF::getMorphClass()));
$this->assertEquals(StateG::class, ModelState::resolveStateClass(StateG::getMorphClass()));
$this->assertEquals(StateG::class, ModelState::resolveStateClass(StateG::getMorphClass()));
$this->assertEquals(StateH::class, ModelState::resolveStateClass(StateH::getMorphClass()));
$this->assertEquals(StateH::class, ModelState::resolveStateClass(StateH::getMorphClass()));
}

/** @test */
Expand All @@ -41,6 +49,7 @@ public function transitionable_states()
StateB::getMorphClass(),
StateC::getMorphClass(),
StateD::getMorphClass(),
StateF::getMorphClass(),
], $modelA->state->transitionableStates());

$modelB = TestModelWithDefault::create([
Expand Down Expand Up @@ -85,6 +94,7 @@ public function test_can_transition_to()

$this->assertTrue($state->canTransitionTo(StateB::class));
$this->assertTrue($state->canTransitionTo(StateC::class));
$this->assertTrue($state->canTransitionTo(StateF::class));

$state = new StateB(new TestModel());
$state->setField('state');
Expand All @@ -106,6 +116,9 @@ public function test_get_states()
StateC::getMorphClass(),
StateD::getMorphClass(),
StateE::getMorphClass(),
StateF::getMorphClass(),
StateG::getMorphClass(),
StateH::getMorphClass(),
],
],
$states->toArray()
Expand All @@ -124,6 +137,9 @@ public function test_get_states_for()
StateC::getMorphClass(),
StateD::getMorphClass(),
StateE::getMorphClass(),
StateF::getMorphClass(),
StateG::getMorphClass(),
StateH::getMorphClass(),
],
$states->toArray()
);
Expand Down Expand Up @@ -175,6 +191,9 @@ public function test_all()
StateC::getMorphClass() => StateC::class,
StateD::getMorphClass() => StateD::class,
StateE::getMorphClass() => StateE::class,
StateF::getMorphClass() => StateF::class,
StateG::getMorphClass() => StateG::class,
StateH::getMorphClass() => StateH::class,
], ModelState::all()->toArray());
}

Expand Down

0 comments on commit 976e5dc

Please sign in to comment.