<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
solution-forest / workflow-state-machine example snippets
use WorkflowStateMachine\Traits\HasWorkflowStates;
class Task extends Model
{
use HasWorkflowStates;
// Your model content...
// The workflow relationship is automatically handled via 'workflowable' polymorphic relation
// You can assign a workflow to this model instance:
// $task->workflow()->associate($workflow);
}
use WorkflowStateMachine\Traits\CanManageWorkflowStates;
class User extends Authenticatable
{
use CanManageWorkflowStates;
// Optional: Override permission logic
public function canChangeWorkflowState($model, string $toStatus): bool
{
// Custom permission logic
return $this->hasRole('admin') || $this->id === $model->user_id;
}
}
use WorkflowStateMachine\Traits\HasWorkflowStates;
class Order extends Model
{
use HasWorkflowStates;
protected $fillable = ['customer_name', 'order_state'];
// Customize the status column name for this model
protected $status_column = 'order_state';
}
return [
// Default status column name for all models
'status_column' => 'state', // default: 'status'
// Other configurations...
];
class Order extends Model
{
use HasWorkflowStates;
protected $status_column = 'order_state'; // Custom column name
}
// You can always access status via the 'status' attribute
$order = Order::find(1);
echo $order->status; // Returns value from 'order_state' column
// Setting status also works dynamically
$order->status = 'approved'; // Sets the 'order_state' column
$order->save();
// Or use the explicit methods
echo $order->getCurrentStatus(); // Same as $order->status
$order->setStatus('completed'); // Same as $order->status = 'completed'
use WorkflowStateMachine\Traits\HasWorkflowStates;
class Order extends Model
{
use HasWorkflowStates;
protected $fillable = ['customer_name', 'order_status'];
protected $status_column = 'order_status';
// Custom status array for this model
protected $status_array = [
'pending' => 'Pending Payment',
'paid' => 'Payment Received',
'processing' => 'Processing Order',
'shipped' => 'Shipped',
'delivered' => 'Delivered',
'cancelled' => 'Cancelled',
];
}
// This model will use the global config status array
class Task extends Model
{
use HasWorkflowStates;
protected $fillable = ['title', 'status'];
// No custom $status_array, uses config
}
use WorkflowStateMachine\Models\Workflow;
use WorkflowStateMachine\Models\WorkflowProcess;
use WorkflowStateMachine\Models\WorkflowRule;
// Create a workflow
$workflow = Workflow::create([
'name' => 'task_approval_workflow',
'description' => 'Task approval workflow',
'starting_status' => 'draft',
'ending_status' => 'completed',
]);
// If 'auto_create_processes' is enabled in config, processes will be automatically
// created based on the status array (excluding 'rejected' and 'cancelled')
// Otherwise, manually create workflow processes:
$process1 = WorkflowProcess::create([
'workflow_id' => $workflow->id,
'name' => 'submit_for_review',
'from_status' => 'draft',
'to_status' => 'pending',
'order' => 1,
'auto_transition' => true,
'completed' => false,
]);
$process2 = WorkflowProcess::create([
'workflow_id' => $workflow->id,
'name' => 'approve_task',
'from_status' => 'pending',
'to_status' => 'approved',
'order' => 2,
'auto_transition' => false,
'completed' => false,
]);
// Assign workflow to a model instance via polymorphic relationship
$task = Task::create(['title' => 'New Task']);
$task->workflow()->associate($workflow);
$task->save();
// Enable auto-creation in config/workflow-state-machine.php
'auto_create_processes' => true,
// With this enabled, creating a workflow will automatically generate processes:
$workflow = Workflow::create([
'name' => 'task_approval_workflow',
'description' => 'Task approval workflow',
'starting_status' => 'draft',
'ending_status' => 'completed',
]);
// Automatically creates processes for status transitions:
// draft → pending → in_progress → review → approved → completed
// (excluding 'rejected' and 'cancelled' from the flow)
// The auto-created processes will have:
// - Sequential ordering (1, 2, 3, ...)
// - Generated names based on status transitions
// - auto_transition set to false by default
// - completed set to false by default
// config/workflow-state-machine.php
return [
// ... other config options ...
// Auto-create workflow for models when they are created
'auto_create_workflow' => true, // default: false
'auto_workflow_name' => 'Default Workflow', // default workflow name
// Auto-create processes based on status array when workflow is created
'auto_create_processes' => true, // default: false
];
use WorkflowStateMachine\Traits\HasWorkflowStates;
class Task extends Model
{
use HasWorkflowStates;
protected $fillable = ['title', 'description', 'status'];
}
// Enable auto-creation in config
config(['workflow-state-machine.auto_create_workflow' => true]);
// Create a new task - workflow will be auto-created
$task = Task::create([
'title' => 'Complete project',
'description' => 'Finish the project by Friday'
]);
// The task now has a workflow automatically assigned
echo $task->workflow->name; // "Default Workflow"
echo $task->status; // "draft" (first status from config)
use WorkflowStateMachine\Services\AutoWorkflowService;
$workflow = AutoWorkflowService::createWorkflowForModel($model);
namespace App\Rules;
use WorkflowStateMachine\Contracts\WorkflowRuleContract;
use WorkflowStateMachine\Models\WorkflowProcess;
class CustomRule implements WorkflowRuleContract
{
public function handle($model, WorkflowProcess $process, $user): bool
{
// Your business logic here
return true;
}
public function getMessage(): string
{
return 'Transition is not allowed by CustomRule.';
}
}
// app/WorkflowRules/UserPermissionRule.php
namespace App\WorkflowRules;
use WorkflowStateMachine\Contracts\WorkflowRuleContract;
use WorkflowStateMachine\Models\WorkflowProcess;
class UserPermissionRule implements WorkflowRuleContract
{
public function handle($model, WorkflowProcess $process, $user): bool
{
// Check if user has permission to transition this model
if (!$user) {
return false;
}
// Custom business logic
if ($process->to_status === 'approved') {
return $user->hasRole('manager') || $user->hasRole('admin');
}
if ($process->to_status === 'completed') {
return $model->assignee_id === $user->id || $user->hasRole('admin');
}
return true;
}
public function getMessage(): string
{
return 'User does not have permission to perform this transition.';
}
}
public function handle($model, WorkflowProcess $process, $user): bool
{
if (!$user) {
return false;
}
// Only managers can approve
if ($process->to_status === 'approved') {
return $user->hasRole('manager');
}
return true;
}
public function handle($model, WorkflowProcess $process, $user): bool
{
// Check if model is ready for transition
if ($process->to_status === 'published' && !$model->is_complete) {
return false;
}
return true;
}
public function handle($model, WorkflowProcess $process, $user): bool
{
// Only allow transitions during business hours
if ($process->to_status === 'live') {
$hour = now()->hour;
return $hour >= 9 && $hour <= 17;
}
return true;
}
#### Attaching Rules to Processes
Create and assign rules to processes:
// When a task is updated, auto-transition rules are checked
$task = Task::find(1);
$task->update(['assignee_id' => 5]);
// Event triggered: WorkflowAutoTransitionEvent
// When related models are updated
$task->comments()->create(['content' => 'Review completed']);
// If configured, this can also trigger auto-transition checks
$task = Task::find(1);
$workflow = $task->workflow;
// Get status roadmap
$roadmap = $workflow->getStatusRoadmap();
// Returns: ['draft', 'pending', 'approved', 'completed']
// Get current position in workflow
$currentStep = $task->getCurrentWorkflowStep();
// Get previous status in the workflow
$previousStatus = $task->getPreviousStatus();
// Returns: 'draft' if current status is 'pending', null if at the beginning
// Get next status in the workflow
$nextStatus = $task->getNextStatus();
// Returns: 'approved' if current status is 'pending', null if at the end
// Check if task can proceed to next step
$canProceed = $task->canProceedToNextStep($user);
$task = Task::find(1);
$user = auth()->user();
// Check if transition is possible
if ($task->canTransitionTo('completed', $user)) {
$task->transitionTo('completed', $user);
}
// Auto transition (if rules allow)
$task->checkAutoTransition($user);
// Get available transitions
$availableTransitions = $task->getAvailableTransitions($user);
// Get all status change records for a model
$auditLogs = $task->workflowAuditLogs;
// Get recent records
$recentLogs = $task->workflowAuditLogs()
->where('created_at', '>=', now()->subDays(7))
->get();
// Get workflow progress
$progress = $task->getWorkflowProgress();
// Rollback to previous state
$task->rollbackToPreviousState($user);
// Rollback to specific state
$task->rollbackToState('pending', $user);
// Get rollback history
$rollbackHistory = $task->getRollbackHistory();
use WorkflowStateMachine\Traits\HasWorkflowStates;
class CriticalTask extends Model
{
use HasWorkflowStates;
// Enable auto-transition for this model specifically
protected $enable_auto_transition = true;
}
class ManualReviewTask extends Model
{
use HasWorkflowStates;
// Disable auto-transition for this model (
// Global config setting
'enable_auto_transition' => false,
// This model will auto-transition despite global setting being disabled
class AutomatedProcess extends Model
{
use HasWorkflowStates;
protected $enable_auto_transition = true; // Overrides global config
}
// This model uses the global config setting (false in this case)
class StandardTask extends Model
{
use HasWorkflowStates;
// No $enable_auto_transition property, so uses global config
}
// Model with auto-transition enabled
class AutomatedTask extends Model
{
use HasWorkflowStates;
protected $enable_auto_transition = true;
}
// When workflows are auto-generated for this model,
// all WorkflowProcess records will have auto_transition = true
// app/WorkflowRules/MinimumTimeRule.php
namespace App\WorkflowRules;
use WorkflowStateMachine\Contracts\WorkflowRuleContract;
use WorkflowStateMachine\Models\WorkflowProcess;
class MinimumTimeRule implements WorkflowRuleContract
{
public function handle($model, WorkflowProcess $process, $user): bool
{
// Ensure model has been in current status for at least 24 hours
if (!$model->status_changed_at) {
return false;
}
return $model->status_changed_at->diffInHours(now()) >= 24;
}
public function getMessage(): string
{
return 'Model must remain in current status for at least 24 hours.';
}
}
// app/WorkflowRules/RequiredFieldsRule.php
namespace App\WorkflowRules;
use WorkflowStateMachine\Contracts\WorkflowRuleContract;
use WorkflowStateMachine\Models\WorkflowProcess;
class RequiredFieldsRule implements WorkflowRuleContract
{
public function handle($model, WorkflowProcess $process, $user): bool
{
// Check if public function getMessage(): string
{
return 'Required fields must be completed before status transition.';
}
}
// Events are automatically dispatched when:
// 1. Model is updated
// 2. Related models are updated (if configured)
// You can also manually trigger auto-transition checks
use WorkflowStateMachine\Events\WorkflowAutoTransitionEvent;
// Dispatch the event manually
event(new WorkflowAutoTransitionEvent($task, $user));
// Listen to workflow events in your EventServiceProvider
protected $listen = [
'WorkflowStateMachine\Events\StatusChanged' => [
'App\Listeners\NotifyStatusChange',
],
'WorkflowStateMachine\Events\WorkflowCompleted' => [
'App\Listeners\HandleWorkflowCompletion',
],
];
bash
php artisan migrate
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.