Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
SqlUserRepository
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 4
110
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 findByUsernameOrEmail
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 findById
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 mapRowToEntity
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2
3declare(strict_types=1);
4
5namespace App\Modules\User\Infrastructure\Repository;
6
7use App\Modules\User\Domain\Model\User;
8use App\Modules\User\Domain\Repository\UserRepositoryInterface;
9use DateTimeImmutable;
10use PDO;
11
12/**
13 * SQL User Repository Implementation.
14 *
15 * Interacts with prefixed database table a_mod_users_records.
16 *
17 * @package App\Modules\User\Infrastructure\Repository
18 */
19final readonly class SqlUserRepository implements UserRepositoryInterface
20{
21    /**
22     * SqlUserRepository constructor.
23     *
24     * @param PDO $pdo PDO database connection instance.
25     * @param string $tablePrefix Database table prefix (e.g. 'a_').
26     */
27    public function __construct(
28        private PDO $pdo,
29        private string $tablePrefix = 'a_'
30    ) {
31    }
32
33    /**
34     * {@inheritdoc}
35     */
36    public function findByUsernameOrEmail(string $usernameOrEmail): ?User
37    {
38        $tableName = $this->tablePrefix . 'mod_users_records';
39        $sql = "SELECT id, username, email, password_hash, is_active, created_at, updated_at, created_by, owner
40                FROM {$tableName}
41                WHERE (username = :val OR email = :val) AND is_active = 1
42                LIMIT 1";
43
44        $stmt = $this->pdo->prepare($sql);
45        $stmt->execute([':val' => $usernameOrEmail]);
46
47        /** @var array<string, mixed>|false $row */
48        $row = $stmt->fetch(PDO::FETCH_ASSOC);
49        if ($row === false) {
50            return null;
51        }
52
53        return $this->mapRowToEntity($row);
54    }
55
56    /**
57     * {@inheritdoc}
58     */
59    public function findById(int $id): ?User
60    {
61        $tableName = $this->tablePrefix . 'mod_users_records';
62        $sql = "SELECT id, username, email, password_hash, is_active, created_at, updated_at, created_by, owner
63                FROM {$tableName}
64                WHERE id = :id AND is_active = 1
65                LIMIT 1";
66
67        $stmt = $this->pdo->prepare($sql);
68        $stmt->execute([':id' => $id]);
69
70        /** @var array<string, mixed>|false $row */
71        $row = $stmt->fetch(PDO::FETCH_ASSOC);
72        if ($row === false) {
73            return null;
74        }
75
76        return $this->mapRowToEntity($row);
77    }
78
79    /**
80     * Maps raw database record array to User domain entity.
81     *
82     * @param array<string, mixed> $row Raw database row.
83     * @return User Mapped User domain entity.
84     */
85    private function mapRowToEntity(array $row): User
86    {
87        $createdAt = isset($row['created_at']) && is_string($row['created_at'])
88            ? new DateTimeImmutable($row['created_at'])
89            : null;
90        $updatedAt = isset($row['updated_at']) && is_string($row['updated_at'])
91            ? new DateTimeImmutable($row['updated_at'])
92            : null;
93
94        return new User(
95            id: (int)$row['id'],
96            username: (string)$row['username'],
97            email: (string)$row['email'],
98            passwordHash: (string)$row['password_hash'],
99            isActive: (bool)$row['is_active'],
100            createdAt: $createdAt,
101            updatedAt: $updatedAt,
102            createdBy: (int)($row['created_by'] ?? 1),
103            owner: (int)($row['owner'] ?? 1)
104        );
105    }
106}