Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
11 / 11
CRAP
100.00% covered (success)
100.00%
1 / 1
AbstractLoanRepository
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
11 / 11
14
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
 findAll
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 findBy
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 findOneBy
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 findById
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 getById
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 exists
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 save
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 delete
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 deleteById
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 findByUserId
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3// ╔════════════════════════════════════════════════════════════╗
4// ║ MIT Licence (#Expat) - https://opensource.org/licenses/MIT ║
5// ║ Copyright 2026 Frederic Poeydomenge <dyno@phexium.com>     ║
6// ╚════════════════════════════════════════════════════════════╝
7
8declare(strict_types=1);
9
10namespace AppDemo\Loan\Infrastructure;
11
12use AppDemo\Loan\Domain\Loan;
13use AppDemo\Loan\Domain\LoanRepository;
14use AppDemo\Loan\Domain\LoansCollection;
15use AppDemo\Loan\Domain\Specification\UserIdSpecification;
16use AppDemo\Loan\Infrastructure\Mapper\LoanMapper;
17use InvalidArgumentException;
18use Override;
19use Phexium\Domain\Id\IdInterface;
20use Phexium\Domain\Specification\SpecificationInterface;
21use Phexium\Plugin\SqlDriver\Port\SqlDriverInterface;
22
23abstract class AbstractLoanRepository implements LoanRepository
24{
25    protected const string TABLE = 'loan';
26
27    public function __construct(
28        protected readonly SqlDriverInterface $driver,
29        protected readonly LoanMapper $mapper,
30    ) {}
31
32    #[Override]
33    public function findAll(): LoansCollection
34    {
35        $rows = $this->driver->findAll(self::TABLE);
36
37        return LoansCollection::fromMap($rows, fn (array $row): Loan => $this->mapper->fromRow($row));
38    }
39
40    #[Override]
41    public function findBy(SpecificationInterface $specification, ?array $orderBy = null, ?int $offset = null, ?int $limit = null): LoansCollection
42    {
43        $rows = $this->driver->findBy(self::TABLE, $specification, $orderBy, $offset, $limit);
44
45        return LoansCollection::fromMap($rows, fn (array $row): Loan => $this->mapper->fromRow($row));
46    }
47
48    #[Override]
49    public function findOneBy(SpecificationInterface $specification): ?Loan
50    {
51        $row = $this->driver->findOneBy(self::TABLE, $specification);
52
53        return $row !== null ? $this->mapper->fromRow($row) : null;
54    }
55
56    #[Override]
57    public function findById(IdInterface $id): ?Loan
58    {
59        $row = $this->driver->findById(self::TABLE, $id);
60
61        return $row !== null ? $this->mapper->fromRow($row) : null;
62    }
63
64    #[Override]
65    public function getById(IdInterface $id): Loan
66    {
67        $loan = $this->findById($id);
68
69        if (!$loan instanceof Loan) {
70            throw new InvalidArgumentException('Loan with ID='.$id->getValue().' not found');
71        }
72
73        return $loan;
74    }
75
76    #[Override]
77    public function exists(IdInterface $id): bool
78    {
79        return $this->driver->exists(self::TABLE, $id);
80    }
81
82    #[Override]
83    public function save(Loan $loan): void
84    {
85        $this->driver->save(self::TABLE, $this->mapper->toRow($loan));
86    }
87
88    #[Override]
89    public function delete(Loan $loan): int
90    {
91        return $this->deleteById($loan->getId());
92    }
93
94    #[Override]
95    public function deleteById(IdInterface $id): int
96    {
97        return $this->driver->deleteById(self::TABLE, $id);
98    }
99
100    #[Override]
101    public function findByUserId(IdInterface $userId): LoansCollection
102    {
103        return $this->findBy(new UserIdSpecification($userId), ['borrowed_at' => 'DESC']);
104    }
105}