Skip to content

Commit

Permalink
Merge pull request #237 from fmeccanici/feature/custom-state-changed-…
Browse files Browse the repository at this point in the history
…event

Feature/Allow to set custom StateChanged event
  • Loading branch information
freekmurze authored Sep 25, 2023
2 parents cab2015 + a0b5bf6 commit 63e27e2
Show file tree
Hide file tree
Showing 14 changed files with 201 additions and 2 deletions.
21 changes: 21 additions & 0 deletions docs/working-with-states/01-configuring-states.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,27 @@ abstract class PaymentState extends State
}
```

### Registering custom StateChanged event
By default, when a state is changed, the `StateChanged` event is fired. If you want to use a custom event, you can register it in the `config` method:

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

use Your\Concrete\State\Event\CustomStateChanged;

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

public static function config(): StateConfig
{
return parent::config()
->stateChangedEvent(CustomStateChanged::class)
;
}
}
```
## Configuring states using attributes

If you're using PHP 8 or higher, you can also configure your state using attributes:
Expand Down
12 changes: 10 additions & 2 deletions src/State.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use ReflectionClass;
use Spatie\ModelStates\Attributes\AttributeLoader;
use Spatie\ModelStates\Events\StateChanged;
use Spatie\ModelStates\Exceptions\ClassDoesNotExtendBaseClass;
use Spatie\ModelStates\Exceptions\CouldNotPerformTransition;
use Spatie\ModelStates\Exceptions\InvalidConfig;

Expand Down Expand Up @@ -169,8 +170,9 @@ public function transitionTo($newState, ...$transitionArgs)
}

/**
* @param Transition $transition
* @param Transition $transition
* @return \Illuminate\Database\Eloquent\Model
* @throws ClassDoesNotExtendBaseClass
*/
public function transition(Transition $transition)
{
Expand All @@ -183,7 +185,13 @@ public function transition(Transition $transition)
$model = app()->call([$transition, 'handle']);
$model->{$this->field}->setField($this->field);

event(new StateChanged(
$stateChangedEvent = $this->stateConfig->stateChangedEvent;

if ($stateChangedEvent !== StateChanged::class && get_parent_class($stateChangedEvent) !== StateChanged::class) {
throw ClassDoesNotExtendBaseClass::make($stateChangedEvent, StateChanged::class);
}

event(new $stateChangedEvent(
$this,
$model->{$this->field},
$transition,
Expand Down
10 changes: 10 additions & 0 deletions src/StateConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Spatie\ModelStates;

use Spatie\ModelStates\Events\StateChanged;
use Spatie\ModelStates\Exceptions\InvalidConfig;

class StateConfig
Expand All @@ -18,6 +19,8 @@ class StateConfig
/** @var string[] */
public array $registeredStates = [];

public string $stateChangedEvent = StateChanged::class;

public function __construct(
string $baseStateClass
) {
Expand Down Expand Up @@ -117,6 +120,13 @@ public function registerState($stateClass): StateConfig
return $this;
}

public function stateChangedEvent(string $event): StateConfig
{
$this->stateChangedEvent = $event;

return $this;
}

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

namespace Spatie\ModelStates\Tests\Dummy\CustomEventModelState;

use Spatie\ModelStates\State;
use Spatie\ModelStates\StateConfig;

abstract class CustomEventModelState extends State
{
public static function config(): StateConfig
{
return parent::config()
->default(CustomEventModelStateA::class)
->allowTransition(CustomEventModelStateA::class, CustomEventModelStateB::class)
->stateChangedEvent(CustomStateChangedEvent::class);
}
}
8 changes: 8 additions & 0 deletions tests/Dummy/CustomEventModelState/CustomEventModelStateA.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Spatie\ModelStates\Tests\Dummy\CustomEventModelState;

class CustomEventModelStateA extends CustomEventModelState
{

}
8 changes: 8 additions & 0 deletions tests/Dummy/CustomEventModelState/CustomEventModelStateB.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Spatie\ModelStates\Tests\Dummy\CustomEventModelState;

class CustomEventModelStateB extends CustomEventModelState
{

}
17 changes: 17 additions & 0 deletions tests/Dummy/CustomEventModelState/CustomInvalidEventModelState.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Spatie\ModelStates\Tests\Dummy\CustomEventModelState;

use Spatie\ModelStates\State;
use Spatie\ModelStates\StateConfig;

abstract class CustomInvalidEventModelState extends State
{
public static function config(): StateConfig
{
return parent::config()
->default(CustomInvalidEventModelStateA::class)
->allowTransition(CustomInvalidEventModelStateA::class, CustomInvalidEventModelStateB::class)
->stateChangedEvent(CustomInvalidStateChangedEvent::class);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Spatie\ModelStates\Tests\Dummy\CustomEventModelState;

class CustomInvalidEventModelStateA extends CustomInvalidEventModelState
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Spatie\ModelStates\Tests\Dummy\CustomEventModelState;

class CustomInvalidEventModelStateB extends CustomInvalidEventModelState
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace Spatie\ModelStates\Tests\Dummy\CustomEventModelState;

use Spatie\ModelStates\Events\StateChanged;
use Spatie\ModelStates\State;
use Spatie\ModelStates\Transition;

class CustomInvalidStateChangedEvent
{

}
12 changes: 12 additions & 0 deletions tests/Dummy/CustomEventModelState/CustomStateChangedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace Spatie\ModelStates\Tests\Dummy\CustomEventModelState;

use Spatie\ModelStates\Events\StateChanged;
use Spatie\ModelStates\State;
use Spatie\ModelStates\Transition;

class CustomStateChangedEvent extends StateChanged
{

}
15 changes: 15 additions & 0 deletions tests/Dummy/TestModelCustomEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Spatie\ModelStates\Tests\Dummy;

use Illuminate\Database\Eloquent\Model;
use Spatie\ModelStates\HasStates;
use Spatie\ModelStates\Tests\Dummy\CustomEventModelState\CustomEventModelState;
use Spatie\ModelStates\Tests\Dummy\ModelStates\ModelState;

class TestModelCustomEvent extends TestModel
{
protected $casts = [
'state' => CustomEventModelState::class,
];
}
16 changes: 16 additions & 0 deletions tests/Dummy/TestModelCustomInvalidEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Spatie\ModelStates\Tests\Dummy;

use Illuminate\Database\Eloquent\Model;
use Spatie\ModelStates\HasStates;
use Spatie\ModelStates\Tests\Dummy\CustomEventModelState\CustomEventModelState;
use Spatie\ModelStates\Tests\Dummy\CustomEventModelState\CustomInvalidEventModelState;
use Spatie\ModelStates\Tests\Dummy\ModelStates\ModelState;

class TestModelCustomInvalidEvent extends TestModel
{
protected $casts = [
'state' => CustomInvalidEventModelState::class,
];
}
39 changes: 39 additions & 0 deletions tests/StateTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
<?php

use Illuminate\Support\Facades\Event;
use Spatie\ModelStates\Events\StateChanged;
use Spatie\ModelStates\Exceptions\ClassDoesNotExtendBaseClass;
use Spatie\ModelStates\Tests\Dummy\CustomEventModelState\CustomEventModelStateB;
use Spatie\ModelStates\Tests\Dummy\CustomEventModelState\CustomInvalidEventModelStateB;
use Spatie\ModelStates\Tests\Dummy\CustomEventModelState\CustomInvalidStateChangedEvent;
use Spatie\ModelStates\Tests\Dummy\CustomEventModelState\CustomStateChangedEvent;
use Spatie\ModelStates\Tests\Dummy\ModelStates\ModelState;
use Spatie\ModelStates\Tests\Dummy\ModelStates\StateA;
use Spatie\ModelStates\Tests\Dummy\ModelStates\StateB;
Expand All @@ -13,6 +19,8 @@
use Spatie\ModelStates\Tests\Dummy\OtherModelStates\StateX;
use Spatie\ModelStates\Tests\Dummy\OtherModelStates\StateY;
use Spatie\ModelStates\Tests\Dummy\TestModel;
use Spatie\ModelStates\Tests\Dummy\TestModelCustomEvent;
use Spatie\ModelStates\Tests\Dummy\TestModelCustomInvalidEvent;
use Spatie\ModelStates\Tests\Dummy\TestModelUpdatingEvent;
use Spatie\ModelStates\Tests\Dummy\TestModelWithCustomTransition;
use Spatie\ModelStates\Tests\Dummy\TestModelWithDefault;
Expand Down Expand Up @@ -208,3 +216,34 @@

Event::assertNotDispatched(TestModelUpdatingEvent::class);
});

it('can emit a custom state changed event', function () {
Event::fake();

$model = TestModelCustomEvent::create();

$model->state->transitionTo(CustomEventModelStateB::class);

Event::assertDispatched(CustomStateChangedEvent::class);
});

it('emits the standard state changed event', function () {
Event::fake();

$model = TestModel::create();

$model->state->transitionTo(StateB::class);

Event::assertDispatched(StateChanged::class);
});

it('should throw exception when custom state changed event does not extend StateChanged', function () {
Event::fake();

$model = TestModelCustomInvalidEvent::create();

$this->expectException(ClassDoesNotExtendBaseClass::class);
$this->expectExceptionMessage('Class ' . CustomInvalidStateChangedEvent::class . ' does not extend the `' . StateChanged::class . '` base class.');

$model->state->transitionTo(CustomInvalidEventModelStateB::class);
});

0 comments on commit 63e27e2

Please sign in to comment.