Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
Maksym_Odanets authored and Maksym_Odanets committed Oct 23, 2024
1 parent 44e3488 commit b77372e
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 48 deletions.
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
}
],
"require": {
"php": "^7.3|^7.4|^8.0",
"illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
"php": "^8.2|^8.3",
"illuminate/support": "^10.0|^11.0",
"javoscript/laravel-macroable-models": "^1.0"
},
"require-dev": {
Expand Down
25 changes: 0 additions & 25 deletions phpunit.xml.dist.bak

This file was deleted.

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 [];
}
}
97 changes: 97 additions & 0 deletions tests/Feature/EnumCastingTransitionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

namespace Asantibanez\LaravelEloquentStateMachines\Tests\Feature;

use Asantibanez\LaravelEloquentStateMachines\Models\PendingTransition;
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));
}
}
12 changes: 12 additions & 0 deletions tests/TestEnums/StatusEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?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,27 @@
<?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;
}
}

0 comments on commit b77372e

Please sign in to comment.