feat: ajout d'une modale de gestion des chapitres, permettant la création, l'édition et le déplacement de chapitres. Mise à jour de l'API pour gérer les modifications en lot des chapitres, ainsi que l'intégration de tests pour valider cette nouvelle fonctionnalité. Amélioration de l'interface utilisateur pour une gestion plus fluide des chapitres.
This commit is contained in:
parent
00d63dffeb
commit
551db0bf77
12
src/Domain/Manga/Application/Command/ChapterEditData.php
Normal file
12
src/Domain/Manga/Application/Command/ChapterEditData.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Manga\Application\Command;
|
||||
|
||||
readonly class ChapterEditData
|
||||
{
|
||||
public function __construct(
|
||||
public string $id,
|
||||
public ?string $title = null,
|
||||
public ?int $volume = null
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Manga\Application\Command;
|
||||
|
||||
readonly class EditMultipleChapters
|
||||
{
|
||||
/**
|
||||
* @param array<ChapterEditData> $chapters
|
||||
*/
|
||||
public function __construct(
|
||||
public array $chapters
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Manga\Application\CommandHandler;
|
||||
|
||||
use App\Domain\Manga\Application\Command\EditMultipleChapters;
|
||||
use App\Domain\Manga\Domain\Contract\Repository\ChapterRepositoryInterface;
|
||||
use App\Domain\Manga\Domain\Exception\ChapterNotFoundException;
|
||||
|
||||
readonly class EditMultipleChaptersHandler
|
||||
{
|
||||
public function __construct(
|
||||
private ChapterRepositoryInterface $chapterRepository
|
||||
) {}
|
||||
|
||||
public function handle(EditMultipleChapters $command): void
|
||||
{
|
||||
foreach ($command->chapters as $chapterData) {
|
||||
$chapter = $this->chapterRepository->findById($chapterData->id);
|
||||
|
||||
if (!$chapter) {
|
||||
throw new ChapterNotFoundException($chapterData->id);
|
||||
}
|
||||
|
||||
$updatedChapter = $chapter;
|
||||
|
||||
if ($chapterData->title !== null) {
|
||||
$updatedChapter = $updatedChapter->updateTitle($chapterData->title);
|
||||
}
|
||||
|
||||
if ($chapterData->volume !== null) {
|
||||
$updatedChapter = $updatedChapter->updateVolume($chapterData->volume);
|
||||
}
|
||||
|
||||
$this->chapterRepository->save($updatedChapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,4 +61,32 @@ readonly class Chapter
|
||||
{
|
||||
return $this->createdAt;
|
||||
}
|
||||
|
||||
public function updateTitle(?string $title): self
|
||||
{
|
||||
return new self(
|
||||
$this->id,
|
||||
$this->mangaId,
|
||||
$this->number,
|
||||
$title,
|
||||
$this->volume,
|
||||
$this->isVisible,
|
||||
$this->cbzPath,
|
||||
$this->createdAt
|
||||
);
|
||||
}
|
||||
|
||||
public function updateVolume(?int $volume): self
|
||||
{
|
||||
return new self(
|
||||
$this->id,
|
||||
$this->mangaId,
|
||||
$this->number,
|
||||
$this->title,
|
||||
$volume,
|
||||
$this->isVisible,
|
||||
$this->cbzPath,
|
||||
$this->createdAt
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Manga\Infrastructure\ApiPlatform\Resource;
|
||||
|
||||
use ApiPlatform\Metadata\ApiResource;
|
||||
use ApiPlatform\Metadata\Post;
|
||||
use App\Domain\Manga\Infrastructure\ApiPlatform\State\Processor\EditMultipleChaptersProcessor;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
|
||||
#[ApiResource(
|
||||
shortName: 'Chapters',
|
||||
operations: [
|
||||
new Post(
|
||||
uriTemplate: '/chapters/batch-edit',
|
||||
processor: EditMultipleChaptersProcessor::class,
|
||||
input: EditMultipleChaptersResource::class,
|
||||
status: 200,
|
||||
openapiContext: [
|
||||
'summary' => 'Edit multiple chapters',
|
||||
'description' => 'Updates title and/or volume for multiple chapters in a single request'
|
||||
]
|
||||
)
|
||||
]
|
||||
)]
|
||||
class EditMultipleChaptersResource
|
||||
{
|
||||
public function __construct(
|
||||
#[Assert\NotBlank(message: 'La liste des chapitres est obligatoire')]
|
||||
#[Assert\Count(min: 1, minMessage: 'Vous devez spécifier au moins un chapitre')]
|
||||
public readonly array $chapters
|
||||
) {}
|
||||
}
|
||||
|
||||
readonly class ChapterEditData
|
||||
{
|
||||
public function __construct(
|
||||
public string $id,
|
||||
public ?string $title = null,
|
||||
public ?int $volume = null
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Domain\Manga\Infrastructure\ApiPlatform\State\Processor;
|
||||
|
||||
use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\ProcessorInterface;
|
||||
use App\Domain\Manga\Application\Command\EditMultipleChapters;
|
||||
use App\Domain\Manga\Application\Command\ChapterEditData;
|
||||
use App\Domain\Manga\Application\CommandHandler\EditMultipleChaptersHandler;
|
||||
use App\Domain\Manga\Domain\Exception\ChapterNotFoundException;
|
||||
use App\Domain\Manga\Infrastructure\ApiPlatform\Resource\EditMultipleChaptersResource;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
readonly class EditMultipleChaptersProcessor implements ProcessorInterface
|
||||
{
|
||||
public function __construct(
|
||||
private EditMultipleChaptersHandler $handler
|
||||
) {}
|
||||
|
||||
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): int
|
||||
{
|
||||
if (!$data instanceof EditMultipleChaptersResource) {
|
||||
throw new \InvalidArgumentException('Invalid resource type');
|
||||
}
|
||||
|
||||
// Validation manuelle des données
|
||||
foreach ($data->chapters as $index => $chapterData) {
|
||||
if (!is_array($chapterData)) {
|
||||
throw new \InvalidArgumentException(sprintf('Chapter data at index %d must be an array', $index));
|
||||
}
|
||||
|
||||
if (!isset($chapterData['id']) || !is_string($chapterData['id'])) {
|
||||
throw new \InvalidArgumentException(sprintf('Chapter ID at index %d must be a non-empty string', $index));
|
||||
}
|
||||
|
||||
if (isset($chapterData['title']) && !is_string($chapterData['title'])) {
|
||||
throw new \InvalidArgumentException(sprintf('Chapter title at index %d must be a string', $index));
|
||||
}
|
||||
|
||||
if (isset($chapterData['volume']) && !is_integer($chapterData['volume'])) {
|
||||
throw new \InvalidArgumentException(sprintf('Chapter volume at index %d must be an integer', $index));
|
||||
}
|
||||
}
|
||||
|
||||
$chapters = array_map(
|
||||
fn (array $chapterData) => new ChapterEditData(
|
||||
id: $chapterData['id'],
|
||||
title: $chapterData['title'] ?? null,
|
||||
volume: $chapterData['volume'] ?? null
|
||||
),
|
||||
$data->chapters
|
||||
);
|
||||
|
||||
$command = new EditMultipleChapters($chapters);
|
||||
|
||||
try {
|
||||
$this->handler->handle($command);
|
||||
} catch (ChapterNotFoundException $e) {
|
||||
throw new NotFoundHttpException($e->getMessage());
|
||||
}
|
||||
|
||||
return Response::HTTP_OK;
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,8 @@ readonly class LegacyChapterRepository implements ChapterRepositoryInterface
|
||||
|
||||
$entity->setVisible($chapter->isVisible());
|
||||
$entity->setCbzPath($chapter->getCbzPath());
|
||||
$entity->setTitle($chapter->getTitle());
|
||||
$entity->setVolume($chapter->getVolume());
|
||||
|
||||
$this->entityManager->persist($entity);
|
||||
$this->entityManager->flush();
|
||||
|
||||
Reference in New Issue
Block a user