Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
InMemoryLoanRepository
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
6 / 6
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
 findAllWithDetails
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
4
 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%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 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        private readonly InMemoryDriver $inMemoryDriver,
29        LoanMapper $mapper,
30        private readonly BookRepository $bookRepository,
31        private readonly UserRepository $userRepository,
32        private readonly ClockInterface $clock,
33    ) {
34        parent::__construct($inMemoryDriver, $mapper);
35    }
36
37    #[Override]
38    public function findAllWithDetails(?int $offset = null, ?int $limit = null): array
39    {
40        $loanItems = $this->findAll()
41            ->mapToArray(function (Loan $loan): LoanWithDetails {
42                $user = $this->userRepository->findById($loan->getUserId());
43                $userEmail = $user instanceof User ? $user->getEmail()->getValue() : 'Unknown';
44
45                return $this->createLoanWithDetails($loan, $userEmail);
46            })
47        ;
48
49        if ($offset !== null || $limit !== null) {
50            return array_slice($loanItems, $offset ?? 0, $limit);
51        }
52
53        return $loanItems;
54    }
55
56    #[Override]
57    public function countAllWithDetails(): int
58    {
59        return count($this->findAll());
60    }
61
62    #[Override]
63    public function findByUserIdWithBookDetails(IdInterface $userId): array
64    {
65        return $this->findByUserId($userId)
66            ->mapToArray(fn (Loan $loan): LoanWithDetails => $this->createLoanWithDetails($loan, ''))
67        ;
68    }
69
70    public function reset(): void
71    {
72        $this->inMemoryDriver->reset(self::TABLE);
73    }
74
75    private function createLoanWithDetails(Loan $loan, string $userEmail): LoanWithDetails
76    {
77        $book = $this->bookRepository->findById($loan->getBookId());
78
79        return new LoanWithDetails(
80            loanId: (string) $loan->getId()->getValue(),
81            userId: (string) $loan->getUserId()->getValue(),
82            userEmail: $userEmail,
83            bookId: (string) $loan->getBookId()->getValue(),
84            bookTitle: $book instanceof Book ? $book->getTitle()->getValue() : 'Unknown',
85            borrowedAt: $loan->getBorrowedAt()->format(DateTimeFormat::SQL_DATETIME),
86            dueAt: $loan->getDueAt()->format(DateTimeFormat::SQL_DATETIME),
87            returnedAt: $loan->getReturnedAt()?->format(DateTimeFormat::SQL_DATETIME),
88            status: $loan->getStatus()->value,
89            isOverdue: $loan->isOverdue($this->clock->now()),
90        );
91    }
92}