Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
61 / 61
100.00% covered (success)
100.00%
7 / 7
CRAP
100.00% covered (success)
100.00%
1 / 1
SqlWorkflowRepository
100.00% covered (success)
100.00%
60 / 60
100.00% covered (success)
100.00%
7 / 7
10
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 findById
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 findActiveByTrigger
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
2
 saveGraph
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 incrementRuns
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 hasRunForRecord
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
 recordRun
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
2
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\Infrastructure\Repository;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11use App\Modules\Automation\Domain\Model\Workflow;
12use App\Modules\Automation\Domain\Repository\WorkflowRepositoryInterface;
13use PDO;
14
15/**
16 * SQL Workflow Persistence Repository.
17 *
18 * Implements database operations for a_mod_workflows_records and a_mod_workflow_runs_records.
19 *
20 * @package App\Modules\Automation\Infrastructure\Repository
21 */
22final readonly class SqlWorkflowRepository implements WorkflowRepositoryInterface
23{
24    /**
25     * SqlWorkflowRepository constructor.
26     *
27     * @param PDO    $pdo         Active PDO database connection.
28     * @param string $tablePrefix Optional table prefix.
29     */
30    public function __construct(
31        private PDO    $pdo,
32        private string $tablePrefix = 'a_'
33    ) {
34    }
35
36    /**
37     * {@inheritdoc}
38     */
39    public function findById(int $id): ?Workflow
40    {
41        $table = $this->tablePrefix . 'mod_workflows_records';
42        $sql = "SELECT `id`, `name`, `target_module`, `trigger_type`, `execution_frequency`,
43                       `status`, `special_access`, `description`, `graph_data`, `compiled_flow`,
44                       `total_runs`, `last_run_at`, `owner`, `created_by`
45                FROM `{$table}`
46                WHERE `id` = :id
47                LIMIT 1";
48
49        $stmt = $this->pdo->prepare($sql);
50        $stmt->execute([':id' => $id]);
51        $row = $stmt->fetch(PDO::FETCH_ASSOC);
52
53        return $row !== false ? Workflow::fromRow($row) : null;
54    }
55
56    /**
57     * {@inheritdoc}
58     */
59    public function findActiveByTrigger(string $moduleName, string $triggerType): array
60    {
61        $table = $this->tablePrefix . 'mod_workflows_records';
62        $sql = "SELECT `id`, `name`, `target_module`, `trigger_type`, `execution_frequency`,
63                       `status`, `special_access`, `description`, `graph_data`, `compiled_flow`,
64                       `total_runs`, `last_run_at`, `owner`, `created_by`
65                FROM `{$table}`
66                WHERE `target_module` = :module
67                  AND `trigger_type` = :trigger
68                  AND `status` = 'active'
69                  AND `special_access` = 1
70                ORDER BY `id` ASC";
71
72        $stmt = $this->pdo->prepare($sql);
73        $stmt->execute([
74            ':module'  => $moduleName,
75            ':trigger' => $triggerType,
76        ]);
77
78        $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
79        $results = [];
80        foreach ($rows as $row) {
81            $results[] = Workflow::fromRow($row);
82        }
83
84        return $results;
85    }
86
87    /**
88     * {@inheritdoc}
89     */
90    public function saveGraph(int $id, string $graphData, string $compiledFlow): void
91    {
92        $table = $this->tablePrefix . 'mod_workflows_records';
93        $sql = "UPDATE `{$table}`
94                SET `graph_data` = :graph,
95                    `compiled_flow` = :compiled,
96                    `updated_at` = CURRENT_TIMESTAMP(6)
97                WHERE `id` = :id";
98
99        $stmt = $this->pdo->prepare($sql);
100        $stmt->execute([
101            ':graph'    => $graphData,
102            ':compiled' => $compiledFlow,
103            ':id'       => $id,
104        ]);
105    }
106
107    /**
108     * {@inheritdoc}
109     */
110    public function incrementRuns(int $id): void
111    {
112        $table = $this->tablePrefix . 'mod_workflows_records';
113        $sql = "UPDATE `{$table}`
114                SET `total_runs` = `total_runs` + 1,
115                    `last_run_at` = CURRENT_TIMESTAMP(6)
116                WHERE `id` = :id";
117
118        $stmt = $this->pdo->prepare($sql);
119        $stmt->execute([':id' => $id]);
120    }
121
122    /**
123     * {@inheritdoc}
124     */
125    public function hasRunForRecord(int $workflowId, int $recordId): bool
126    {
127        $table = $this->tablePrefix . 'mod_workflow_runs_records';
128        $sql = "SELECT COUNT(*)
129                FROM `{$table}`
130                WHERE `workflow_id` = :wid
131                  AND `record_id` = :rid
132                  AND `status` = 'success'
133                LIMIT 1";
134
135        $stmt = $this->pdo->prepare($sql);
136        $stmt->execute([
137            ':wid' => $workflowId,
138            ':rid' => $recordId,
139        ]);
140
141        return (int) $stmt->fetchColumn() > 0;
142    }
143
144    /**
145     * {@inheritdoc}
146     */
147    public function recordRun(
148        int     $workflowId,
149        ?int    $recordId,
150        string  $triggerType,
151        string  $status,
152        int     $executionTimeMs,
153        ?string $errorMessage = null,
154        ?array  $contextSnapshot = null
155    ): void {
156        $table = $this->tablePrefix . 'mod_workflow_runs_records';
157        $sql = "INSERT INTO `{$table}`
158                (`workflow_id`, `record_id`, `trigger_type`, `status`,
159                 `execution_time_ms`, `error_message`, `context_snapshot`, `executed_at`)
160                VALUES
161                (:wid, :rid, :trigger, :status, :ms, :err, :snapshot, CURRENT_TIMESTAMP(6))";
162
163        $stmt = $this->pdo->prepare($sql);
164        $stmt->execute([
165            ':wid'      => $workflowId,
166            ':rid'      => $recordId,
167            ':trigger'  => $triggerType,
168            ':status'   => $status,
169            ':ms'       => $executionTimeMs,
170            ':err'      => $errorMessage,
171            ':snapshot' => $contextSnapshot !== null ? json_encode($contextSnapshot) : null,
172        ]);
173    }
174}