PHP code example of juststeveking / state-machine

1. Go to this page and download the library: Download juststeveking/state-machine library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

juststeveking / state-machine example snippets




declare(strict_types=1);

use App\Domain\Posts\PostStateMachine;
use App\Domain\Posts\Transitions\PublishPost;
use JustSteveKing\StateMachine\Exceptions\InvalidTransitionException;
use JustSteveKing\StateMachine\StateMachine;

$stateMachine = new StateMachine(
	machine: new PostStateMachine(currentStatus: 'draft'),
);

try {
	$event = $stateMachine->transition(
		transition: new PublishPost(),
		context: ['can_publish' => true],
	);

	// Persist new status, dispatch event, etc.
} catch (InvalidTransitionException $exception) {
	// Return/log why the transition was denied
}



declare(strict_types=1);

namespace App\Domain\Posts\States;

use JustSteveKing\StateMachine\Contracts\StateContract;

final class DraftState implements StateContract
{
	public function value(): string
	{
		return 'draft';
	}

	public function label(): string
	{
		return 'Draft';
	}
}

final class PublishedState implements StateContract
{
	public function value(): string
	{
		return 'published';
	}

	public function label(): string
	{
		return 'Published';
	}
}



declare(strict_types=1);

namespace App\Domain\Posts\Events;

use JustSteveKing\StateMachine\Events\DomainEvent;

final class PostPublished extends DomainEvent
{
	public function name(): string
	{
		return 'post.published';
	}
}



declare(strict_types=1);

namespace App\Domain\Posts\Transitions;

use App\Domain\Posts\Events\PostPublished;
use App\Domain\Posts\States\DraftState;
use App\Domain\Posts\States\PublishedState;
use JustSteveKing\StateMachine\Contracts\StateContract;
use JustSteveKing\StateMachine\Contracts\TransitionContract;

final class PublishPost implements TransitionContract
{
	/**
	 * @return array<int, StateContract>
	 */
	public function from(): array
	{
		return [new DraftState()];
	}

	public function to(): StateContract
	{
		return new PublishedState();
	}

	/**
	 * @return class-string<PostPublished>
	 */
	public function eventClass(): string
	{
		return PostPublished::class;
	}

	public function guard(mixed $context): ?string
	{
		if (! is_array($context) || ($context['can_publish'] ?? false) !== true) {
			return 'User is not allowed to publish this post.';
		}

		return null;
	}
}



declare(strict_types=1);

namespace App\Domain\Posts;

use App\Domain\Posts\States\DraftState;
use App\Domain\Posts\Transitions\PublishPost;
use JustSteveKing\StateMachine\Contracts\StateContract;
use JustSteveKing\StateMachine\Contracts\StateMachineContract;
use JustSteveKing\StateMachine\Contracts\TransitionContract;

final readonly class PostStateMachine implements StateMachineContract
{
	public function __construct(
		private string $currentStatus,
	) {}

	/**
	 * @return array<int, TransitionContract>
	 */
	public function transitions(): array
	{
		return [
			new PublishPost(),
		];
	}

	public function currentState(): StateContract
	{
		return match ($this->currentStatus) {
			'draft' => new DraftState(),
			default => new DraftState(),
		};
	}
}



declare(strict_types=1);

use App\Domain\Posts\PostStateMachine;
use App\Domain\Posts\Transitions\PublishPost;
use JustSteveKing\StateMachine\Exceptions\InvalidTransitionException;
use JustSteveKing\StateMachine\StateMachine;

$machine = new StateMachine(
	machine: new PostStateMachine(currentStatus: 'draft'),
);

try {
	$event = $machine->transition(
		transition: new PublishPost(),
		context: ['can_publish' => true],
	);

	// $event->from, $event->to, $event->context, $event->occurredAt
} catch (InvalidTransitionException $exception) {
	// Handle denial / invalid transition
	// Example: log, return a validation message, etc.
}



declare(strict_types=1);

namespace App\Domain\Posts\Transitions;

use App\Domain\Posts\Events\PostArchived;
use App\Domain\Posts\States\ArchivedState;
use App\Domain\Posts\States\PublishedState;
use JustSteveKing\StateMachine\Contracts\StateContract;
use JustSteveKing\StateMachine\Contracts\TransitionContract;

final class ArchivePost implements TransitionContract
{
	/**
	 * @return array<int, StateContract>
	 */
	public function from(): array
	{
		return [new PublishedState()];
	}

	public function to(): StateContract
	{
		return new ArchivedState();
	}

	/**
	 * @return class-string<PostArchived>
	 */
	public function eventClass(): string
	{
		return PostArchived::class;
	}

	public function guard(mixed $context): ?string
	{
		if (! is_array($context) || ($context['can_archive'] ?? false) !== true) {
			return 'User is not allowed to archive this post.';
		}

		return null;
	}
}



declare(strict_types=1);

namespace App\Domain\Posts;

use App\Domain\Posts\States\ArchivedState;
use App\Domain\Posts\States\DraftState;
use App\Domain\Posts\States\PublishedState;
use App\Domain\Posts\Transitions\ArchivePost;
use App\Domain\Posts\Transitions\PublishPost;
use JustSteveKing\StateMachine\Contracts\StateContract;
use JustSteveKing\StateMachine\Contracts\StateMachineContract;
use JustSteveKing\StateMachine\Contracts\TransitionContract;

final readonly class PostStateMachine implements StateMachineContract
{
	public function __construct(
		private string $currentStatus,
	) {}

	/**
	 * @return array<int, TransitionContract>
	 */
	public function transitions(): array
	{
		return [
			new PublishPost(),
			new ArchivePost(),
		];
	}

	public function currentState(): StateContract
	{
		return match ($this->currentStatus) {
			'draft' => new DraftState(),
			'published' => new PublishedState(),
			'archived' => new ArchivedState(),
			default => new DraftState(),
		};
	}
}



declare(strict_types=1);

use App\Domain\Posts\PostStateMachine;
use App\Domain\Posts\Transitions\ArchivePost;
use JustSteveKing\StateMachine\StateMachine;

$machine = new StateMachine(
	machine: new PostStateMachine(currentStatus: 'published'),
);

$event = $machine->transition(
	transition: new ArchivePost(),
	context: ['can_archive' => true],
);

// $event->from->value() === 'published'
// $event->to->value() === 'archived'