Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added functionality to work with enum casting #43

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
],
"require": {
"php": "^7.3|^7.4|^8.0",
"illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0",
"illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
"javoscript/laravel-macroable-models": "^1.0"
},
"require-dev": {
Expand Down
21 changes: 13 additions & 8 deletions src/StateMachines/State.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function __construct($state, $stateMachine)

public function state()
{
return $this->state;
return $this->normalizeCasting($this->state);
}

public function stateMachine()
Expand All @@ -37,7 +37,7 @@ public function stateMachine()

public function is($state)
{
return $this->state === $state;
return $this->state() === $this->normalizeCasting($state);
}

public function isNot($state)
Expand Down Expand Up @@ -77,7 +77,7 @@ public function history()

public function canBe($state)
{
return $this->stateMachine->canBe($from = $this->state, $to = $state);
return $this->stateMachine->canBe($from = $this->state(), $to = $this->normalizeCasting($state));
}

public function pendingTransitions()
Expand All @@ -90,11 +90,16 @@ public function hasPendingTransitions()
return $this->stateMachine->hasPendingTransitions();
}

public function normalizeCasting($state)
{
return $this->stateMachine->normalizeCasting($state);
}

public function transitionTo($state, $customProperties = [], $responsible = null)
{
$this->stateMachine->transitionTo(
$from = $this->state,
$to = $state,
$from = $this->state(),
$to = $this->normalizeCasting($state),
$customProperties,
$responsible
);
Expand All @@ -111,8 +116,8 @@ public function transitionTo($state, $customProperties = [], $responsible = null
public function postponeTransitionTo($state, Carbon $when, $customProperties = [], $responsible = null) : ?PendingTransition
{
return $this->stateMachine->postponeTransitionTo(
$from = $this->state,
$to = $state,
$from = $this->state(),
$to = $this->normalizeCasting($state),
$when,
$customProperties,
$responsible
Expand All @@ -121,7 +126,7 @@ public function postponeTransitionTo($state, Carbon $when, $customProperties = [

public function latest() : ?StateHistory
{
return $this->snapshotWhen($this->state);
return $this->snapshotWhen($this->state());
}

public function getCustomProperty($key)
Expand Down
39 changes: 26 additions & 13 deletions src/StateMachines/StateMachine.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@
use Asantibanez\LaravelEloquentStateMachines\Models\StateHistory;
use Carbon\Carbon;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Validation\ValidationException;
use UnitEnum;

abstract class StateMachine
{
Expand All @@ -30,7 +29,7 @@ public function currentState()
{
$field = $this->field;

return $this->model->$field;
return $this->normalizeCasting($this->model->$field);
}

public function history()
Expand All @@ -48,7 +47,7 @@ public function timesWas($state)
return $this->history()->to($state)->count();
}

public function whenWas($state) : ?Carbon
public function whenWas($state): ?Carbon
{
$stateHistory = $this->snapshotWhen($state);

Expand All @@ -59,12 +58,12 @@ public function whenWas($state) : ?Carbon
return $stateHistory->created_at;
}

public function snapshotWhen($state) : ?StateHistory
public function snapshotWhen($state): ?StateHistory
{
return $this->history()->to($state)->latest('id')->first();
}

public function snapshotsWhen($state) : Collection
public function snapshotsWhen($state): Collection
{
return $this->history()->to($state)->get();
}
Expand All @@ -73,7 +72,9 @@ public function canBe($from, $to)
{
$availableTransitions = $this->transitions()[$from] ?? [];

return collect($availableTransitions)->contains($to);
return collect($availableTransitions)->map(function ($state) {
return $this->normalizeCasting($state);
})->contains($to);
}

public function pendingTransitions()
Expand All @@ -86,6 +87,11 @@ public function hasPendingTransitions()
return $this->pendingTransitions()->notApplied()->exists();
}

public function normalizeCasting($state)
{
return $state instanceof UnitEnum ? $state->value : $state;
}

/**
* @param $from
* @param $to
Expand All @@ -96,6 +102,9 @@ public function hasPendingTransitions()
*/
public function transitionTo($from, $to, $customProperties = [], $responsible = null)
{
$from = $this->normalizeCasting($from);
$to = $this->normalizeCasting($to);

if ($to === $this->currentState()) {
return;
}
Expand Down Expand Up @@ -148,8 +157,11 @@ public function transitionTo($from, $to, $customProperties = [], $responsible =
* @return null|PendingTransition
* @throws TransitionNotAllowedException
*/
public function postponeTransitionTo($from, $to, Carbon $when, $customProperties = [], $responsible = null) : ?PendingTransition
public function postponeTransitionTo($from, $to, Carbon $when, $customProperties = [], $responsible = null): ?PendingTransition
{
$from = $this->normalizeCasting($from);
$to = $this->normalizeCasting($to);

if ($to === $this->currentState()) {
return null;
}
Expand All @@ -175,23 +187,24 @@ public function cancelAllPendingTransitions()
$this->pendingTransitions()->delete();
}

abstract public function transitions() : array;
abstract public function transitions(): array;

abstract public function defaultState() : ?string;
abstract public function defaultState(): ?string;

abstract public function recordHistory() : bool;
abstract public function recordHistory(): bool;

public function validatorForTransition($from, $to, $model): ?Validator
{
return null;
}

public function afterTransitionHooks() : array
public function afterTransitionHooks(): array
{
return [];
}

public function beforeTransitionHooks() : array {
public function beforeTransitionHooks(): array
{
return [];
}
}
96 changes: 96 additions & 0 deletions tests/Feature/EnumCastingTransitionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

namespace Asantibanez\LaravelEloquentStateMachines\Tests\Feature;

use Asantibanez\LaravelEloquentStateMachines\Tests\TestCase;
use Asantibanez\LaravelEloquentStateMachines\Tests\TestEnums\StatusEnum;
use Asantibanez\LaravelEloquentStateMachines\Tests\TestModels\SalesOrderWithEnumCasting;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;

class EnumCastingTransitionTest extends TestCase
{
use RefreshDatabase;
use WithFaker;

/** @test */
public function can_transition_to_any_state_using_enum_casting()
{
//Arrange
$salesOrder = SalesOrderWithEnumCasting::create();

$this->assertTrue($salesOrder->status()->is(StatusEnum::PENDING));

$this->assertEquals(StatusEnum::PENDING, $salesOrder->status);

//Act
$salesOrder->status()->transitionTo(StatusEnum::APPROVED);

//Assert
$salesOrder->refresh();

$this->assertTrue($salesOrder->status()->is(StatusEnum::APPROVED));

$this->assertEquals(StatusEnum::APPROVED, $salesOrder->status);
}

/** @test */
public function can_postpone_transition_to_any_state_using_enum_casting()
{
//Arrange
$salesOrder = SalesOrderWithEnumCasting::create();

$this->assertTrue($salesOrder->status()->is(StatusEnum::PENDING));

$this->assertEquals(StatusEnum::PENDING, $salesOrder->status);

//Act
$pendingTransition = $salesOrder->status()->postponeTransitionTo(StatusEnum::APPROVED, Carbon::tomorrow()->startOfDay());

//Assert
$this->assertNotNull($pendingTransition);

$salesOrder->refresh();

$this->assertTrue($salesOrder->status()->is('pending'));

$this->assertTrue($salesOrder->status()->hasPendingTransitions());

/** @var PendingTransition $pendingTransition */
$pendingTransition = $salesOrder->status()->pendingTransitions()->first();

$this->assertEquals('status', $pendingTransition->field);
$this->assertEquals('pending', $pendingTransition->from);
$this->assertEquals('approved', $pendingTransition->to);

$this->assertEquals(Carbon::tomorrow()->startOfDay(), $pendingTransition->transition_at);

$this->assertNull($pendingTransition->applied_at);

$this->assertEquals($salesOrder->id, $pendingTransition->model->id);
}

/** @test */
public function can_access_model_state_history_using_enum_casting()
{
//Arrange
$salesOrder = SalesOrderWithEnumCasting::create();

$this->assertTrue($salesOrder->status()->is(StatusEnum::PENDING));

$this->assertEquals(StatusEnum::PENDING, $salesOrder->status);

//Act
$salesOrder->status()->transitionTo(StatusEnum::APPROVED);

//Assert
$salesOrder->refresh();

$this->assertTrue($salesOrder->status()->is(StatusEnum::APPROVED));

$this->assertEquals(StatusEnum::APPROVED, $salesOrder->status);

$this->assertTrue($salesOrder->status()->was(StatusEnum::PENDING));
}
}
13 changes: 13 additions & 0 deletions tests/TestEnums/StatusEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php


namespace Asantibanez\LaravelEloquentStateMachines\Tests\TestEnums;

enum StatusEnum: string
{
case APPROVED = 'approved';
case CANCELLED = 'cancelled';
case PENDING = 'pending';
case PROCESSED = 'processed';
case WAITING = 'waiting';
}
25 changes: 25 additions & 0 deletions tests/TestModels/SalesOrderWithEnumCasting.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Asantibanez\LaravelEloquentStateMachines\Tests\TestModels;

use Asantibanez\LaravelEloquentStateMachines\Tests\TestEnums\StatusEnum;
use Asantibanez\LaravelEloquentStateMachines\Tests\TestStateMachines\SalesOrders\StatusWithEnumCastingStateMachine;
use Asantibanez\LaravelEloquentStateMachines\Traits\HasStateMachines;
use Illuminate\Database\Eloquent\Model;

class SalesOrderWithEnumCasting extends Model
{
use HasStateMachines;

protected $table = 'sales_orders';

protected $guarded = [];

protected $casts = [
'status' => StatusEnum::class,
];

public $stateMachines = [
'status' => StatusWithEnumCastingStateMachine::class,
];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php


namespace Asantibanez\LaravelEloquentStateMachines\Tests\TestStateMachines\SalesOrders;


use Asantibanez\LaravelEloquentStateMachines\StateMachines\StateMachine;
use Asantibanez\LaravelEloquentStateMachines\Tests\TestEnums\StatusEnum;

class StatusWithEnumCastingStateMachine extends StateMachine
{
public function recordHistory(): bool
{
return true;
}

public function transitions(): array
{
return [
StatusEnum::PENDING->value => [StatusEnum::APPROVED, StatusEnum::WAITING],
StatusEnum::APPROVED->value => [StatusEnum::PROCESSED],
StatusEnum::WAITING->value => [StatusEnum::CANCELLED],
];
}

public function defaultState(): ?string
{
return StatusEnum::PENDING->value;
}
}