Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 68
0.00% covered (danger)
0.00%
0 / 18
CRAP
0.00% covered (danger)
0.00%
0 / 1
AbstractAdapter
0.00% covered (danger)
0.00%
0 / 68
0.00% covered (danger)
0.00%
0 / 18
1122
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 calculateDateFromRelative
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 calculateDateFromEndRelative
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 getBuildUpStartDate
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 getWindDownEndDate
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 formatDateForDb
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 formatDateOnly
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 parseDate
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
30
 buildStatusResponse
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 beginTransaction
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 commit
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 rollback
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 logError
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getConfigValue
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 validateRequiredKeys
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
 recordExists
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 setEventIdOnRecord
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 clearEventIdOnRecord
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace BuyerKiosk\EventManagement\Adapters;
4
5use PDO;
6use DateTime;
7use DateInterval;
8use BuyerKiosk\EventManagement\Models\Event;
9use BuyerKiosk\EventManagement\Models\EventIntegration;
10
11/**
12 * AbstractAdapter - Base class for all integration adapters
13 *
14 * Provides common functionality for date calculations, database access,
15 * and status tracking. Concrete adapters extend this class.
16 *
17 * @package BuyerKiosk\EventManagement\Adapters
18 */
19abstract class AbstractAdapter implements IntegrationAdapterInterface
20{
21    /**
22     * @var PDO Database connection for the store
23     */
24    protected PDO $db;
25
26    /**
27     * @var string|null Store timezone
28     */
29    protected ?string $timezone;
30
31    /**
32     * Constructor
33     *
34     * @param PDO $db Database connection for the store
35     * @param string|null $timezone Store timezone (e.g., 'America/Chicago')
36     */
37    public function __construct(PDO $db, ?string $timezone = null)
38    {
39        $this->db = $db;
40        $this->timezone = $timezone ?? 'America/Chicago';
41    }
42
43    /**
44     * Calculate actual date from relative days
45     *
46     * @param Event $event The event
47     * @param int $relativeDays Days relative to event start (negative = before)
48     * @return DateTime Calculated date
49     */
50    protected function calculateDateFromRelative(Event $event, int $relativeDays): DateTime
51    {
52        $baseDate = clone $event->startDate;
53
54        if ($relativeDays === 0) {
55            return $baseDate;
56        }
57
58        if ($relativeDays > 0) {
59            $baseDate->add(new DateInterval("P{$relativeDays}D"));
60        } else {
61            $days = abs($relativeDays);
62            $baseDate->sub(new DateInterval("P{$days}D"));
63        }
64
65        return $baseDate;
66    }
67
68    /**
69     * Calculate date relative to event end
70     *
71     * @param Event $event The event
72     * @param int $relativeDays Days relative to event end (negative = before end)
73     * @return DateTime Calculated date
74     */
75    protected function calculateDateFromEndRelative(Event $event, int $relativeDays): DateTime
76    {
77        $baseDate = clone $event->endDate;
78
79        if ($relativeDays === 0) {
80            return $baseDate;
81        }
82
83        if ($relativeDays > 0) {
84            $baseDate->add(new DateInterval("P{$relativeDays}D"));
85        } else {
86            $days = abs($relativeDays);
87            $baseDate->sub(new DateInterval("P{$days}D"));
88        }
89
90        return $baseDate;
91    }
92
93    /**
94     * Get build-up start date for the event
95     *
96     * @param Event $event
97     * @return DateTime
98     */
99    protected function getBuildUpStartDate(Event $event): DateTime
100    {
101        $date = clone $event->startDate;
102        $date->sub(new DateInterval("P{$event->buildUpDays}D"));
103        return $date;
104    }
105
106    /**
107     * Get wind-down end date for the event
108     *
109     * @param Event $event
110     * @return DateTime
111     */
112    protected function getWindDownEndDate(Event $event): DateTime
113    {
114        $date = clone $event->endDate;
115        $date->add(new DateInterval("P{$event->windDownDays}D"));
116        return $date;
117    }
118
119    /**
120     * Format a DateTime for database storage
121     *
122     * @param DateTime|null $date
123     * @return string|null
124     */
125    protected function formatDateForDb(?DateTime $date): ?string
126    {
127        return $date?->format('Y-m-d H:i:s');
128    }
129
130    /**
131     * Format a DateTime as date only (no time)
132     *
133     * @param DateTime|null $date
134     * @return string|null
135     */
136    protected function formatDateOnly(?DateTime $date): ?string
137    {
138        return $date?->format('Y-m-d');
139    }
140
141    /**
142     * Parse a date string into DateTime
143     *
144     * @param string|null $dateStr
145     * @return DateTime|null
146     */
147    protected function parseDate(?string $dateStr): ?DateTime
148    {
149        if ($dateStr === null || $dateStr === '' || strpos($dateStr, '0000-00-00') === 0) {
150            return null;
151        }
152
153        try {
154            return new DateTime($dateStr);
155        } catch (\Exception $e) {
156            return null;
157        }
158    }
159
160    /**
161     * Create a standard status response
162     *
163     * @param string $status Current status
164     * @param array $details Type-specific details
165     * @param DateTime|null $lastUpdated
166     * @return array
167     */
168    protected function buildStatusResponse(
169        string $status,
170        array $details = [],
171        ?DateTime $lastUpdated = null
172    ): array {
173        return [
174            'status' => $status,
175            'details' => $details,
176            'lastUpdated' => $lastUpdated?->format('Y-m-d H:i:s') ?? date('Y-m-d H:i:s'),
177        ];
178    }
179
180    /**
181     * Begin a database transaction
182     *
183     * @return bool
184     */
185    protected function beginTransaction(): bool
186    {
187        if (!$this->db->inTransaction()) {
188            return $this->db->beginTransaction();
189        }
190        return true;
191    }
192
193    /**
194     * Commit a database transaction
195     *
196     * @return bool
197     */
198    protected function commit(): bool
199    {
200        if ($this->db->inTransaction()) {
201            return $this->db->commit();
202        }
203        return true;
204    }
205
206    /**
207     * Rollback a database transaction
208     *
209     * @return bool
210     */
211    protected function rollback(): bool
212    {
213        if ($this->db->inTransaction()) {
214            return $this->db->rollBack();
215        }
216        return true;
217    }
218
219    /**
220     * Log an error (can be overridden for custom logging)
221     *
222     * @param string $message
223     * @param array $context
224     */
225    protected function logError(string $message, array $context = []): void
226    {
227        $logMessage = "[EventManagement:{$this->getType()}{$message}";
228        if (!empty($context)) {
229            $logMessage .= ' ' . json_encode($context);
230        }
231        error_log($logMessage);
232    }
233
234    /**
235     * Get a configuration value with default
236     *
237     * @param array $config
238     * @param string $key
239     * @param mixed $default
240     * @return mixed
241     */
242    protected function getConfigValue(array $config, string $key, mixed $default = null): mixed
243    {
244        return $config[$key] ?? $default;
245    }
246
247    /**
248     * Validate required configuration keys
249     *
250     * @param array $config
251     * @param array $requiredKeys
252     * @return array Validation errors
253     */
254    protected function validateRequiredKeys(array $config, array $requiredKeys): array
255    {
256        $errors = [];
257        foreach ($requiredKeys as $key) {
258            if (!isset($config[$key]) || $config[$key] === '') {
259                $errors[] = "Missing required configuration: {$key}";
260            }
261        }
262        return $errors;
263    }
264
265    /**
266     * Check if a record exists in the target table
267     *
268     * @param string $table Table name
269     * @param int $id Record ID
270     * @param string $idColumn ID column name (default 'id')
271     * @return bool
272     */
273    protected function recordExists(string $table, int $id, string $idColumn = 'id'): bool
274    {
275        $sql = "SELECT 1 FROM `{$table}` WHERE `{$idColumn}` = :id LIMIT 1";
276        $stmt = $this->db->prepare($sql);
277        $stmt->execute([':id' => $id]);
278        return $stmt->fetch() !== false;
279    }
280
281    /**
282     * Update the eventId on a target record
283     *
284     * @param string $table Table name
285     * @param int $recordId Record ID
286     * @param int $eventId Event ID
287     * @param string $idColumn ID column name (default 'id')
288     * @return bool Success
289     */
290    protected function setEventIdOnRecord(
291        string $table,
292        int $recordId,
293        int $eventId,
294        string $idColumn = 'id'
295    ): bool {
296        $sql = "UPDATE `{$table}` SET `eventId` = :eventId WHERE `{$idColumn}` = :recordId";
297        $stmt = $this->db->prepare($sql);
298        return $stmt->execute([
299            ':eventId' => $eventId,
300            ':recordId' => $recordId,
301        ]);
302    }
303
304    /**
305     * Clear the eventId on a target record (set to NULL)
306     *
307     * @param string $table Table name
308     * @param int $recordId Record ID
309     * @param string $idColumn ID column name (default 'id')
310     * @return bool Success
311     */
312    protected function clearEventIdOnRecord(
313        string $table,
314        int $recordId,
315        string $idColumn = 'id'
316    ): bool {
317        $sql = "UPDATE `{$table}` SET `eventId` = NULL WHERE `{$idColumn}` = :recordId";
318        $stmt = $this->db->prepare($sql);
319        return $stmt->execute([':recordId' => $recordId]);
320    }
321}