Skip to content

Commit

Permalink
Merge pull request #17 from bjrnblm/master
Browse files Browse the repository at this point in the history
Get transitionable states from state on Model
  • Loading branch information
brendt committed Oct 28, 2019
2 parents 1de2e3c + 8824bbc commit 928b369
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
title: Retrieving transitionable states
weight: 4
---

An array of transitionable states can use the retrieved with the `transitionableStates()` method on your model.

```php
class Payment extends Model
{
// …

protected function registerStates(): void
{
$this->addState('state', PaymentState::class)
->allowTransition(Pending::class, Paid::class)
->allowTransition(Paid::class, Refunded::class)
}
}
```

```php
$transitionableStates = $payment->transitionableStates(Pending::class);
```

This will return an array with all transitionable states for `Pending::class`

```php
[
0 => "paid"
]
```

## Transitionable states from state

Its also possible to use `transitionableStates()` method directly on a state:

```php
$payment->state->transitionableStates();
```

## Multiple state fields on model

If there are multiple fields, a `\Spatie\ModelStates\Exceptions\InvalidConfig` exception will be thrown. You can pass the state field name explicitly as a parameter:

```php
// From a model
$payment->transitionableStates(Pending::class, 'fieldName')

// From a state
$payment->state->transitionableStates('fieldName');
```
17 changes: 17 additions & 0 deletions src/HasStates.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,23 @@ public function transitionTo($state, string $field = null)
$this->{$field}->transitionTo($state);
}

public function transitionableStates(string $fromClass, ?string $field = null): array
{
$stateConfig = self::getStateConfig();

if ($field === null && count($stateConfig) > 1) {
throw InvalidConfig::fieldNotFound($fromClass, $this);
}

$field = $field ?? reset($stateConfig)->field;

if (! array_key_exists($field, $stateConfig)) {
throw InvalidConfig::unknownState($field, $this);
}

return $stateConfig[$field]->transitionableStates($fromClass);
}

/**
* @param string $fromClass
* @param string $toClass
Expand Down
10 changes: 10 additions & 0 deletions src/State.php
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,16 @@ public function transitionTo($state, ...$args): Model
return $this->transition($transition, ...$args);
}

/**
* Get the transitionable states from this state.
*
* @return array
*/
public function transitionableStates($field = null): array
{
return $this->model->transitionableStates(get_class($this), $field);
}

/**
* This method is used to find all available implementations of a given abstract state class.
* Finding all implementations can be done in two ways:.
Expand Down
17 changes: 17 additions & 0 deletions src/StateConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,23 @@ public function allowTransitions(array $transitions): StateConfig
return $this;
}

public function transitionableStates(string $fromClass): array
{
$transitionableStates = [];

foreach ($this->allowedTransitions as $allowedTransition => $value) {
[$from, $to] = explode('-', $allowedTransition);

if ($from !== $fromClass) {
continue;
}

$transitionableStates[] = $to::getMorphClass();
}

return $transitionableStates;
}

/**
* @param string $from
* @param string $to
Expand Down
59 changes: 59 additions & 0 deletions tests/TransitionableStatesTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace Spatie\ModelStates\Tests;

use Spatie\ModelStates\Tests\Dummy\Payment;
use Spatie\ModelStates\Exceptions\InvalidConfig;
use Spatie\ModelStates\Tests\Dummy\States\Failed;
use Spatie\ModelStates\Tests\Dummy\States\Created;
use Spatie\ModelStates\Tests\Dummy\States\Pending;
use Spatie\ModelStates\Tests\Dummy\ModelWithMultipleStates;

class TransitionableStatesTest extends TestCase
{
/** @test */
public function transitionable_states_with_fieldname()
{
$payment = new Payment();

$transitionableStates = $payment->transitionableStates(Created::class, 'state');

$this->assertEquals(
$transitionableStates,
[Pending::getMorphClass(), Failed::getMorphClass()]
);
}

/** @test */
public function transitionable_states_without_fieldname()
{
$payment = new Payment();

$transitionableStates = $payment->transitionableStates(Created::class);

$this->assertEquals(
$transitionableStates,
[Pending::getMorphClass(), Failed::getMorphClass()]
);
}

/** @test */
public function transitionable_states_with_invalid_fieldname_fails()
{
$this->expectException(InvalidConfig::class);

$payment = new Payment();

$transitionableStates = $payment->transitionableStates(Created::class, 'wrong');
}

/** @test */
public function transitionable_states_with_multiple_states_without_fieldname_fails()
{
$this->expectException(InvalidConfig::class);

$payment = new ModelWithMultipleStates();

$transitionableStates = $payment->transitionableStates(DummyState::class);
}
}

0 comments on commit 928b369

Please sign in to comment.