Skip to content

Commit 01ba2ce

Browse files
committed
wip
1 parent 228db0f commit 01ba2ce

23 files changed

+2741
-698
lines changed

README.md

Lines changed: 592 additions & 1 deletion
Large diffs are not rendered by default.

config/workflow-engine.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@
1414
*/
1515
'storage' => [
1616
'driver' => env('WORKFLOW_STORAGE_DRIVER', 'database'),
17-
17+
1818
'database' => [
1919
'connection' => env('WORKFLOW_DB_CONNECTION', config('database.default')),
2020
'table' => env('WORKFLOW_DB_TABLE', 'workflow_instances'),
2121
],
22-
22+
2323
'file' => [
2424
'path' => env('WORKFLOW_FILE_PATH', storage_path('app/workflows')),
2525
],

phpstan-baseline.neon

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
parameters:
2+
ignoreErrors:
3+
-
4+
message: '#^Called ''env'' outside of the config directory which returns null when the config is cached, use ''config''\.$#'
5+
identifier: larastan.noEnvCallsOutsideOfConfig
6+
count: 11
7+
path: config/workflow-engine.php
8+
9+
-
10+
message: '#^Called ''env'' outside of the config directory which returns null when the config is cached, use ''config''\.$#'
11+
identifier: larastan.noEnvCallsOutsideOfConfig
12+
count: 11
13+
path: config/workflow-mastery.php
14+
15+
-
16+
message: '#^Match arm comparison between ''\<\='' and ''\<\='' is always true\.$#'
17+
identifier: match.alwaysTrue
18+
count: 1
19+
path: src/Core/Step.php
20+
21+
-
22+
message: '#^Match arm comparison between ''\<\='' and ''\<\='' is always true\.$#'
23+
identifier: match.alwaysTrue
24+
count: 1
25+
path: src/Core/WorkflowDefinition.php

src/Core/Step.php

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,16 @@
44

55
class Step
66
{
7-
private string $id;
8-
9-
private ?string $actionClass;
10-
11-
private array $config;
12-
13-
private ?string $timeout;
14-
15-
private int $retryAttempts;
16-
17-
private ?string $compensationAction;
18-
19-
private array $conditions;
20-
21-
private array $prerequisites;
22-
237
public function __construct(
24-
string $id,
25-
?string $actionClass = null,
26-
array $config = [],
27-
?string $timeout = null,
28-
int $retryAttempts = 0,
29-
?string $compensationAction = null,
30-
array $conditions = [],
31-
array $prerequisites = []
32-
) {
33-
$this->id = $id;
34-
$this->actionClass = $actionClass;
35-
$this->config = $config;
36-
$this->timeout = $timeout;
37-
$this->retryAttempts = $retryAttempts;
38-
$this->compensationAction = $compensationAction;
39-
$this->conditions = $conditions;
40-
$this->prerequisites = $prerequisites;
41-
}
8+
private readonly string $id,
9+
private readonly ?string $actionClass = null,
10+
private readonly array $config = [],
11+
private readonly ?string $timeout = null,
12+
private readonly int $retryAttempts = 0,
13+
private readonly ?string $compensationAction = null,
14+
private readonly array $conditions = [],
15+
private readonly array $prerequisites = []
16+
) {}
4217

4318
public function getId(): string
4419
{
@@ -104,7 +79,7 @@ public function canExecute(array $data): bool
10479
private function evaluateCondition(string $condition, array $data): bool
10580
{
10681
// Simple condition evaluation - can be enhanced later
107-
if (preg_match('/(\w+(?:\.\w+)*)\s*(===|==|!=|>|<|>=|<=)\s*(.+)/', $condition, $matches)) {
82+
if (preg_match('/(\w+(?:\.\w+)*)\s*(===|==|!=|>=|<=|>|<)\s*(.+)/', $condition, $matches)) {
10883
$key = $matches[1];
10984
$operator = $matches[2];
11085
$value = trim($matches[3], '"\'');

src/Core/WorkflowDefinition.php

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,18 @@
22

33
namespace SolutionForest\WorkflowMastery\Core;
44

5-
use Illuminate\Support\Facades\Log;
6-
75
class WorkflowDefinition
86
{
9-
private string $name;
10-
11-
private string $version;
12-
13-
private array $steps;
14-
15-
private array $transitions;
16-
17-
private array $metadata;
7+
private readonly array $steps;
188

199
public function __construct(
20-
string $name,
21-
string $version,
10+
private readonly string $name,
11+
private readonly string $version,
2212
array $steps = [],
23-
array $transitions = [],
24-
array $metadata = []
13+
private readonly array $transitions = [],
14+
private readonly array $metadata = []
2515
) {
26-
$this->name = $name;
27-
$this->version = $version;
2816
$this->steps = $this->processSteps($steps);
29-
$this->transitions = $transitions;
30-
$this->metadata = $metadata;
3117
}
3218

3319
public function getName(): string
@@ -75,7 +61,8 @@ public function getFirstStep(): ?Step
7561
}
7662

7763
// If no step found without incoming transitions, return first step
78-
return reset($this->steps) ?: null;
64+
$stepsArray = $this->steps;
65+
return reset($stepsArray) ?: null;
7966
}
8067

8168
public function getNextSteps(?string $currentStepId, array $data = []): array
@@ -127,7 +114,7 @@ private function processSteps(array $stepsData): array
127114
foreach ($stepsData as $index => $stepData) {
128115
// Use the 'id' field from step data, or fall back to array index
129116
$stepId = $stepData['id'] ?? $index;
130-
117+
131118
$actionClass = null;
132119
if (isset($stepData['action'])) {
133120
$actionClass = ActionResolver::resolve($stepData['action']);
@@ -161,7 +148,7 @@ private function evaluateCondition(string $condition, array $data): bool
161148

162149
return match ($operator) {
163150
'===' => $dataValue === $value,
164-
'!==', '!==' => $dataValue !== $value,
151+
'!==' => $dataValue !== $value,
165152
'==' => $dataValue == $value,
166153
'!=' => $dataValue != $value,
167154
'>' => $dataValue > $value,

src/Core/WorkflowEngine.php

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,30 @@
33
namespace SolutionForest\WorkflowMastery\Core;
44

55
use Illuminate\Contracts\Events\Dispatcher as EventDispatcher;
6-
use Illuminate\Support\Facades\Log;
76
use SolutionForest\WorkflowMastery\Contracts\StorageAdapter;
87
use SolutionForest\WorkflowMastery\Events\WorkflowCancelled;
98
use SolutionForest\WorkflowMastery\Events\WorkflowStarted;
109

1110
class WorkflowEngine
1211
{
13-
private DefinitionParser $parser;
12+
private readonly DefinitionParser $parser;
1413

15-
private StateManager $stateManager;
14+
private readonly StateManager $stateManager;
1615

17-
private Executor $executor;
18-
19-
private EventDispatcher $eventDispatcher;
20-
21-
private StorageAdapter $storage;
16+
private readonly Executor $executor;
2217

2318
public function __construct(
24-
StorageAdapter $storage,
25-
?EventDispatcher $eventDispatcher = null
19+
private readonly StorageAdapter $storage,
20+
private readonly ?EventDispatcher $eventDispatcher = null
2621
) {
27-
$this->storage = $storage;
2822
$this->parser = new DefinitionParser;
2923
$this->stateManager = new StateManager($storage);
3024
$this->executor = new Executor($this->stateManager);
31-
$this->eventDispatcher = $eventDispatcher ?? app(EventDispatcher::class);
25+
26+
// If no event dispatcher is provided, we'll use a fallback approach
27+
if ($this->eventDispatcher === null) {
28+
// We'll handle this case in the methods that use the event dispatcher
29+
}
3230
}
3331

3432
/**
@@ -53,7 +51,7 @@ public function start(string $workflowId, array $definition, array $context = []
5351
$this->stateManager->save($instance);
5452

5553
// Dispatch start event
56-
$this->eventDispatcher->dispatch(new WorkflowStarted(
54+
$this->dispatchEvent(new WorkflowStarted(
5755
$instance->getId(),
5856
$instance->getDefinition()->getName(),
5957
$context
@@ -107,7 +105,7 @@ public function cancel(string $instanceId, string $reason = ''): WorkflowInstanc
107105
$this->stateManager->save($instance);
108106

109107
// Dispatch cancel event
110-
$this->eventDispatcher->dispatch(new WorkflowCancelled(
108+
$this->dispatchEvent(new WorkflowCancelled(
111109
$instance->getId(),
112110
$instance->getDefinition()->getName(),
113111
$reason
@@ -121,12 +119,7 @@ public function cancel(string $instanceId, string $reason = ''): WorkflowInstanc
121119
*/
122120
public function getWorkflow(string $workflowId): WorkflowInstance
123121
{
124-
$instance = $this->stateManager->load($workflowId);
125-
if (! $instance) {
126-
throw new \InvalidArgumentException("Workflow not found: {$workflowId}");
127-
}
128-
129-
return $instance;
122+
return $this->stateManager->load($workflowId);
130123
}
131124

132125
/**
@@ -171,4 +164,14 @@ public function listWorkflows(array $filters = []): array
171164
];
172165
}, $instances);
173166
}
167+
168+
/**
169+
* Safely dispatch an event if event dispatcher is available
170+
*/
171+
private function dispatchEvent(object $event): void
172+
{
173+
if ($this->eventDispatcher !== null) {
174+
$this->eventDispatcher->dispatch($event);
175+
}
176+
}
174177
}

src/Core/WorkflowInstance.php

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@
66

77
class WorkflowInstance
88
{
9-
private string $id;
10-
11-
private WorkflowDefinition $definition;
12-
139
private WorkflowState $state;
1410

1511
private array $data;
@@ -22,20 +18,18 @@ class WorkflowInstance
2218

2319
private ?string $errorMessage = null;
2420

25-
private Carbon $createdAt;
21+
private readonly Carbon $createdAt;
2622

2723
private Carbon $updatedAt;
2824

2925
public function __construct(
30-
string $id,
31-
WorkflowDefinition $definition,
26+
private readonly string $id,
27+
private readonly WorkflowDefinition $definition,
3228
WorkflowState $state,
3329
array $data = [],
3430
?Carbon $createdAt = null,
3531
?Carbon $updatedAt = null
3632
) {
37-
$this->id = $id;
38-
$this->definition = $definition;
3933
$this->state = $state;
4034
$this->data = $data;
4135
$this->createdAt = $createdAt ?? now();
Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,16 @@
11
<?php
22

3-
namespace Solutionforest\LaravelWorkflowEngine\Facades;
3+
namespace SolutionForest\WorkflowMastery\Facades;
44

5-
use Solutionforest\LaravelWorkflowEngine\Core\WorkflowEngine;
65
use Illuminate\Support\Facades\Facade;
76

87
/**
9-
* @see \Solutionforest\LaravelWorkflowEngine\Core\WorkflowEngine
10-
*/
11-
class LaravelWorkflowEngine extends Facade
12-
{
13-
protected static function getFacadeAccessor()
14-
{
15-
return WorkflowEngine::class;
16-
}
17-
} Solutionforest\LaravelWorkflowEngine\Facades;
18-
19-
use Illuminate\Support\Facades\Facade;
20-
21-
/**
22-
* @see \Solutionforest\LaravelWorkflowEngine\LaravelWorkflowEngine
8+
* @see \SolutionForest\WorkflowMastery\LaravelWorkflowEngine
239
*/
2410
class LaravelWorkflowEngine extends Facade
2511
{
2612
protected static function getFacadeAccessor(): string
2713
{
28-
return \Solutionforest\LaravelWorkflowEngine\LaravelWorkflowEngine::class;
14+
return \SolutionForest\WorkflowMastery\LaravelWorkflowEngine::class;
2915
}
3016
}

src/LaravelWorkflowEngine.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ public function __construct(WorkflowEngine $engine)
1919
*/
2020
public function start(array|string $definition, array $initialData = []): WorkflowInstance
2121
{
22-
return $this->engine->start($definition, $initialData);
22+
$workflowId = 'workflow-' . uniqid();
23+
$this->engine->start($workflowId, $definition, $initialData);
24+
return $this->engine->getWorkflow($workflowId);
2325
}
2426

2527
/**
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace Tests\Actions\ECommerce;
4+
5+
use SolutionForest\WorkflowMastery\Contracts\WorkflowAction;
6+
use SolutionForest\WorkflowMastery\Core\ActionResult;
7+
use SolutionForest\WorkflowMastery\Core\WorkflowContext;
8+
9+
class CreateShipmentAction implements WorkflowAction
10+
{
11+
public function execute(WorkflowContext $context): ActionResult
12+
{
13+
$order = $context->getData('order');
14+
15+
// Mock shipment creation
16+
$shipmentId = 'ship_'.uniqid();
17+
$trackingNumber = 'TRK'.mt_rand(100000, 999999);
18+
19+
$context->setData('shipment.id', $shipmentId);
20+
$context->setData('shipment.tracking_number', $trackingNumber);
21+
$context->setData('shipment.created', true);
22+
23+
return new ActionResult(
24+
success: true,
25+
data: [
26+
'shipment_id' => $shipmentId,
27+
'tracking_number' => $trackingNumber,
28+
'status' => 'created',
29+
]
30+
);
31+
}
32+
33+
public function canExecute(WorkflowContext $context): bool
34+
{
35+
return $context->hasData('order') &&
36+
$context->getData('payment.success') === true;
37+
}
38+
39+
public function getName(): string
40+
{
41+
return 'Create Shipment';
42+
}
43+
44+
public function getDescription(): string
45+
{
46+
return 'Creates shipment and generates tracking number';
47+
}
48+
}

0 commit comments

Comments
 (0)