Files
GSB-Construction/app/Traits/HasStateMachine.php

85 lines
2.3 KiB
PHP

<?php
namespace App\Traits;
use InvalidArgumentException;
/**
* Custom state machine trait for Eloquent models.
* Replacement for spatie/laravel-model-states (incompatible with Laravel 13).
*
* Usage:
* 1. Define a PHP enum for your states
* 2. Use this trait on your model
* 3. Implement `getStateMachineConfig()` returning state field + allowed transitions
*/
trait HasStateMachine
{
/**
* Get the state machine configuration.
* Override in your model.
*
* @return array{field: string, transitions: array<string, string[]>}
*/
abstract protected static function stateMachineConfig(): array;
public function getStateField(): string
{
return static::stateMachineConfig()['field'];
}
public function getCurrentState(): string
{
$field = $this->getStateField();
return $this->{$field};
}
public function canTransitionTo(string $newState): bool
{
$config = static::stateMachineConfig();
$currentState = $this->getCurrentState();
$transitions = $config['transitions'] ?? [];
if (!isset($transitions[$currentState])) {
return false;
}
return in_array($newState, $transitions[$currentState], true);
}
public function transitionTo(string $newState): static
{
if (!$this->canTransitionTo($newState)) {
$currentState = $this->getCurrentState();
$field = $this->getStateField();
throw new InvalidArgumentException(
"Cannot transition '{$field}' from '{$currentState}' to '{$newState}'."
);
}
$field = $this->getStateField();
$oldState = $this->getCurrentState();
$this->{$field} = $newState;
$this->save();
// Fire an event for the transition
$modelName = class_basename(static::class);
event("{$modelName}StateChanged", [
'model' => $this,
'field' => $field,
'from' => $oldState,
'to' => $newState,
]);
return $this;
}
public function getAllowedTransitions(): array
{
$config = static::stateMachineConfig();
$currentState = $this->getCurrentState();
return $config['transitions'][$currentState] ?? [];
}
}