Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
ReturnBookController
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
2 / 2
4
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
 returnBook
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
1 / 1
3
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\Application\Http;
11
12use AppDemo\Loan\Application\Command\ReturnBookCommand;
13use AppDemo\Loan\Domain\Exception\LoanAlreadyReturnedException;
14use AppDemo\Loan\Domain\Exception\LoanNotOwnedByUserException;
15use AppDemo\Shared\Application\AbstractHttpController;
16use AppDemo\Shared\Domain\Interface\SessionServiceInterface;
17use Phexium\Application\ControllerInterface;
18use Phexium\Plugin\Clock\Port\ClockInterface;
19use Phexium\Plugin\CommandBus\Port\CommandBusInterface;
20use Phexium\Plugin\IdGenerator\Port\IdGeneratorInterface;
21use Phexium\Plugin\Logger\Port\LoggerInterface;
22use Phexium\Presentation\ResponseBuilderInterface;
23use Psr\Http\Message\ResponseInterface;
24use Psr\Http\Message\ServerRequestInterface;
25
26final readonly class ReturnBookController extends AbstractHttpController implements ControllerInterface
27{
28    public function __construct(
29        private CommandBusInterface $commandBus,
30        private ClockInterface $clock,
31        private SessionServiceInterface $sessionService,
32        private ResponseBuilderInterface $responseBuilder,
33        private IdGeneratorInterface $idGenerator,
34        private LoggerInterface $logger,
35    ) {}
36
37    public function returnBook(ServerRequestInterface $request, ResponseInterface $response, array $args): ResponseInterface
38    {
39        $this->logger->info('ReturnBookController: Processing request');
40
41        try {
42            $loanId = $this->idGenerator->from($args['id']);
43            $userId = $this->getUserId($request);
44            $returnedAt = $this->clock->now();
45
46            $command = new ReturnBookCommand($loanId, $userId, $returnedAt);
47
48            $this->commandBus->dispatch($command);
49
50            $this->sessionService->addFlashMessage('success', 'Book returned successfully!');
51
52            return $this->responseBuilder
53                ->withResponse($response)
54                ->withStatus(302)
55                ->withHeader('Location', '/loans/my')
56                ->build()
57            ;
58        } catch (LoanAlreadyReturnedException $e) {
59            $this->logger->warning('ReturnBookController: Loan already returned', ['error' => $e->getMessage()]);
60            $this->sessionService->addFlashMessage('warning', 'This book has already been returned');
61        } catch (LoanNotOwnedByUserException $e) {
62            $this->logger->warning('ReturnBookController: Unauthorized return attempt', ['error' => $e->getMessage()]);
63            $this->sessionService->addFlashMessage('danger', 'You are not authorized to return this book');
64        }
65
66        return $this->responseBuilder
67            ->withResponse($response)
68            ->withStatus(302)
69            ->withHeader('Location', '/loans/my')
70            ->build()
71        ;
72    }
73}