Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
97.22% covered (success)
97.22%
35 / 36
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
UpdateBookController
97.22% covered (success)
97.22%
35 / 36
75.00% covered (warning)
75.00%
3 / 4
6
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 showEditForm
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 updateBook
100.00% covered (success)
100.00%
22 / 22
100.00% covered (success)
100.00%
1 / 1
2
 renderFormResponse
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
2.00
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\Library\Presentation\Http;
11
12use AppDemo\Library\Application\Command\UpdateBookCommand;
13use AppDemo\Library\Application\Query\UpdateBookQuery;
14use AppDemo\Library\Application\Query\UpdateBookResponse;
15use AppDemo\Library\Presentation\Request\UpdateBookRequest;
16use AppDemo\Library\Presentation\UpdateBookHtmlPresenter;
17use AppDemo\Shared\Domain\Interface\SessionServiceInterface;
18use AppDemo\Shared\Presentation\AbstractHttpController;
19use Phexium\Domain\Id\IdInterface;
20use Phexium\Plugin\CommandBus\Port\CommandBusInterface;
21use Phexium\Plugin\IdGenerator\Port\IdGeneratorInterface;
22use Phexium\Plugin\Logger\Port\LoggerInterface;
23use Phexium\Plugin\QueryBus\Port\Exception\UnexpectedQueryResponseException;
24use Phexium\Plugin\QueryBus\Port\QueryBusInterface;
25use Phexium\Presentation\ControllerInterface;
26use Phexium\Presentation\ResponseBuilderInterface;
27use Psr\Http\Message\ResponseInterface;
28use Psr\Http\Message\ServerRequestInterface;
29use Twig\Environment;
30
31final readonly class UpdateBookController extends AbstractHttpController implements ControllerInterface
32{
33    public function __construct(
34        private CommandBusInterface $commandBus,
35        private QueryBusInterface $queryBus,
36        private SessionServiceInterface $sessionService,
37        private UpdateBookHtmlPresenter $presenter,
38        private Environment $twig,
39        private ResponseBuilderInterface $responseBuilder,
40        private IdGeneratorInterface $idGenerator,
41        private LoggerInterface $logger,
42    ) {}
43
44    public function showEditForm(
45        ServerRequestInterface $request, // NOSONAR - Slim Framework PSR-15 contract
46        ResponseInterface $response,
47        array $args = []
48    ): ResponseInterface {
49        $bookId = $this->idGenerator->from($args['id']);
50
51        return $this->renderFormResponse($response, $bookId);
52    }
53
54    public function updateBook(ServerRequestInterface $request, ResponseInterface $response, array $args = []): ResponseInterface
55    {
56        $this->logger->info('UpdateBookController: Processing request');
57
58        $bookId = $this->idGenerator->from($args['id']);
59
60        $requestDto = UpdateBookRequest::fromHttpRequest($request);
61        $errors = $requestDto->validate();
62
63        if ($errors === []) {
64            $command = new UpdateBookCommand(
65                $bookId,
66                $requestDto->title,
67                $requestDto->author,
68                $requestDto->isbn,
69                $requestDto->status
70            );
71
72            $this->commandBus->dispatch($command);
73
74            $this->sessionService->addFlashMessage('success', 'Book updated successfully!');
75
76            return $this->responseBuilder
77                ->withResponse($response)
78                ->withStatus(302)
79                ->withHeader('Location', '/books/'.$bookId->getValue())
80                ->build()
81            ;
82        }
83
84        $this->logger->warning('UpdateBookController: Validation errors', ['errors' => $errors]);
85
86        return $this->renderFormResponse($response, $bookId, $requestDto->toArray(), $errors);
87    }
88
89    private function renderFormResponse(ResponseInterface $response, IdInterface $bookId, array $formValues = [], array $errors = []): ResponseInterface
90    {
91        $query = new UpdateBookQuery($bookId, $formValues, $errors);
92        $queryResponse = $this->queryBus->dispatch($query);
93
94        if (!$queryResponse instanceof UpdateBookResponse) {
95            throw UnexpectedQueryResponseException::withExpectedType(UpdateBookResponse::class, $queryResponse);
96        }
97        $viewModel = $this->presenter->present($queryResponse)->getViewModel();
98
99        $html = $this->twig->render('UpdateBook.html.twig', (array) $viewModel);
100
101        return $this->responseBuilder
102            ->withResponse($response)
103            ->withHtml($html)
104            ->build()
105        ;
106    }
107}