Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.77% covered (success)
96.77%
30 / 31
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
WorkflowExecutionContext
96.67% covered (success)
96.67%
29 / 30
75.00% covered (warning)
75.00%
3 / 4
5
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 withNodeOutput
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
1
 withCurrentData
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
1
 hasFieldChanged
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
2.03
1<?php
2
3declare(strict_types=1);
4
5/** @license For full copyright and license information, please see the LICENSE.md file. */
6
7namespace App\Modules\Automation\Domain\Model;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11/**
12 * Workflow Execution Context Pipeline.
13 *
14 * Carries current record data, previous snapshot, diffs, actor details,
15 * and outputs produced by preceding nodes in the DAG execution tree.
16 *
17 * @package App\Modules\Automation\Domain\Model
18 */
19final readonly class WorkflowExecutionContext
20{
21    /**
22     * WorkflowExecutionContext constructor.
23     *
24     * @param string                    $runId            Unique execution UUID.
25     * @param int                       $workflowId       Target workflow identifier.
26     * @param string                    $triggerType      Trigger category (before_save, after_create, etc.).
27     * @param string                    $moduleName       Machine name of the active module.
28     * @param int|null                  $recordId         Primary key of the active record.
29     * @param array<string, mixed>      $currentData      Current data payload.
30     * @param array<string, mixed>|null $previousSnapshot Previous database snapshot (for updates/deletions).
31     * @param int|null                  $actorUserId      ID of the executing user.
32     * @param array<string, mixed>      $nodeOutputs      Outputs keyed by node ID.
33     */
34    public function __construct(
35        public string  $runId,
36        public int     $workflowId,
37        public string  $triggerType,
38        public string  $moduleName,
39        public ?int    $recordId,
40        public array   $currentData,
41        public ?array  $previousSnapshot = null,
42        public ?int    $actorUserId = null,
43        public array   $nodeOutputs = []
44    ) {
45    }
46
47    /**
48     * Returns a clone of this context enriched with output from a completed node.
49     *
50     * @param string               $nodeId Unique node ID on the canvas.
51     * @param array<string, mixed> $output Result data produced by the node.
52     * @return self New immutable context instance.
53     */
54    public function withNodeOutput(string $nodeId, array $output): self
55    {
56        $merged = $this->nodeOutputs;
57        $merged[$nodeId] = $output;
58
59        return new self(
60            runId: $this->runId,
61            workflowId: $this->workflowId,
62            triggerType: $this->triggerType,
63            moduleName: $this->moduleName,
64            recordId: $this->recordId,
65            currentData: $this->currentData,
66            previousSnapshot: $this->previousSnapshot,
67            actorUserId: $this->actorUserId,
68            nodeOutputs: $merged
69        );
70    }
71
72    /**
73     * Returns a clone of this context with modified current record data.
74     *
75     * @param array<string, mixed> $updatedData Modified data payload.
76     * @return self New immutable context instance.
77     */
78    public function withCurrentData(array $updatedData): self
79    {
80        return new self(
81            runId: $this->runId,
82            workflowId: $this->workflowId,
83            triggerType: $this->triggerType,
84            moduleName: $this->moduleName,
85            recordId: $this->recordId,
86            currentData: $updatedData,
87            previousSnapshot: $this->previousSnapshot,
88            actorUserId: $this->actorUserId,
89            nodeOutputs: $this->nodeOutputs
90        );
91    }
92
93    /**
94     * Checks whether a specific field has changed between snapshot and current data.
95     *
96     * @param string $fieldKey Field name.
97     * @return bool True if field value was altered.
98     */
99    public function hasFieldChanged(string $fieldKey): bool
100    {
101        if ($this->previousSnapshot === null) {
102            return array_key_exists($fieldKey, $this->currentData);
103        }
104
105        $old = $this->previousSnapshot[$fieldKey] ?? null;
106        $new = $this->currentData[$fieldKey] ?? null;
107
108        return $old !== $new;
109    }
110}