Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
30 / 30
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
InMemoryLoanRepository
100.00% covered (success)
100.00%
30 / 30
100.00% covered (success)
100.00%
6 / 6
12
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
 findAllWithDetails
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
5
 countAllWithDetails
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 findByUserIdWithBookDetails
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 reset
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createLoanWithDetails
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
2
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\Library\Domain\Book;
13use AppDemo\Library\Domain\BookRepository;
14use AppDemo\Loan\Domain\Loan;
15use AppDemo\Loan\Domain\ReadModel\LoanWithDetails;
16use AppDemo\Loan\Infrastructure\Mapper\LoanMapper;
17use AppDemo\User\Domain\User;
18use AppDemo\User\Domain\UserRepository;
19use Override;
20use Phexium\Domain\Constant\DateTimeFormat;
21use Phexium\Domain\Id\IdInterface;
22use Phexium\Plugin\Clock\Port\ClockInterface;
23use Phexium\Plugin\SqlDriver\Adapter\InMemoryDriver;
24
25final class InMemoryLoanRepository extends AbstractLoanRepository
26{
27    public function __construct(
28        InMemoryDriver $driver,
29        LoanMapper $mapper,
30        private readonly BookRepository $bookRepository,
31        private readonly UserRepository $userRepository,
32        private readonly ClockInterface $clock,
33    ) {
34        parent::__construct($driver, $mapper);
35    }
36
37    #[Override]
38    public function findAllWithDetails(?int $offset = null, ?int $limit = null): array
39    {
40        $loans = $this->findAll();
41        $loanItems = [];
42
43        foreach ($loans as $loan) {
44            $user = $this->userRepository->findById($loan->getUserId());
45            $userEmail = $user instanceof User ? $user->getEmail()->getValue() : 'Unknown';
46
47            $loanItems[] = $this->createLoanWithDetails($loan, $userEmail);
48        }
49
50        if ($offset !== null || $limit !== null) {
51            return array_slice($loanItems, $offset ?? 0, $limit);
52        }
53
54        return $loanItems;
55    }
56
57    #[Override]
58    public function countAllWithDetails(): int
59    {
60        return count($this->findAll());
61    }
62
63    #[Override]
64    public function findByUserIdWithBookDetails(IdInterface $userId): array
65    {
66        $loans = $this->findByUserId($userId);
67        $loanItems = [];
68
69        foreach ($loans as $loan) {
70            $loanItems[] = $this->createLoanWithDetails($loan, '');
71        }
72
73        return $loanItems;
74    }
75
76    public function reset(): void
77    {
78        $this->driver->reset(self::TABLE);
79    }
80
81    private function createLoanWithDetails(Loan $loan, string $userEmail): LoanWithDetails
82    {
83        $book = $this->bookRepository->findById($loan->getBookId());
84
85        return new LoanWithDetails(
86            loanId: (string) $loan->getId()->getValue(),
87            userId: (string) $loan->getUserId()->getValue(),
88            userEmail: $userEmail,
89            bookId: (string) $loan->getBookId()->getValue(),
90            bookTitle: $book instanceof Book ? $book->getTitle()->getValue() : 'Unknown',
91            borrowedAt: $loan->getBorrowedAt()->format(DateTimeFormat::SQL_DATETIME),
92            dueAt: $loan->getDueAt()->format(DateTimeFormat::SQL_DATETIME),
93            returnedAt: $loan->getReturnedAt()?->format(DateTimeFormat::SQL_DATETIME),
94            status: $loan->getStatus()->value,
95            isOverdue: $loan->isOverdue($this->clock->now()),
96        );
97    }
98}