diff --git a/src/Domain/Manga/Application/Query/GetMangaBySlug.php b/src/Domain/Manga/Application/Query/GetMangaBySlug.php new file mode 100644 index 0000000..64292b2 --- /dev/null +++ b/src/Domain/Manga/Application/Query/GetMangaBySlug.php @@ -0,0 +1,10 @@ +mangaRepository->findBySlug(new MangaSlug($query->slug)); + + if (!$manga) { + throw new MangaNotFoundException(); + } + + return new MangaResponse( + id: $manga->getId()->getValue(), + title: $manga->getTitle()->getValue(), + slug: $manga->getSlug()->getValue(), + description: $manga->getDescription(), + author: $manga->getAuthor(), + publicationYear: $manga->getPublicationYear(), + genres: $manga->getGenres(), + status: $manga->getStatus(), + externalId: $manga->getExternalId()?->getValue(), + imageUrl: $manga->getImageUrl(), + rating: $manga->getRating() + ); + } +} \ No newline at end of file diff --git a/src/Domain/Manga/Domain/Contract/Repository/MangaRepositoryInterface.php b/src/Domain/Manga/Domain/Contract/Repository/MangaRepositoryInterface.php index 303299d..459898e 100644 --- a/src/Domain/Manga/Domain/Contract/Repository/MangaRepositoryInterface.php +++ b/src/Domain/Manga/Domain/Contract/Repository/MangaRepositoryInterface.php @@ -5,6 +5,7 @@ namespace App\Domain\Manga\Domain\Contract\Repository; use App\Domain\Manga\Domain\Model\Manga; use App\Domain\Manga\Domain\Model\Chapter; use App\Domain\Manga\Domain\Model\ValueObject\ExternalId; +use App\Domain\Manga\Domain\Model\ValueObject\MangaSlug; interface MangaRepositoryInterface { @@ -17,4 +18,5 @@ interface MangaRepositoryInterface public function countChapters(string $mangaId): int; public function findByExternalId(ExternalId $externalId): ?Manga; public function saveChapter(Chapter $chapter): void; + public function findBySlug(MangaSlug $slug): ?Manga; } \ No newline at end of file diff --git a/src/Domain/Manga/Infrastructure/ApiPlatform/Resource/GetMangaBySlugResource.php b/src/Domain/Manga/Infrastructure/ApiPlatform/Resource/GetMangaBySlugResource.php new file mode 100644 index 0000000..6dddb31 --- /dev/null +++ b/src/Domain/Manga/Infrastructure/ApiPlatform/Resource/GetMangaBySlugResource.php @@ -0,0 +1,26 @@ +handler->handle($query); + + return new MangaDetail( + id: $response->id, + title: $response->title, + slug: $response->slug, + description: $response->description, + author: $response->author, + publicationYear: $response->publicationYear, + genres: $response->genres, + status: $response->status, + externalId: $response->externalId, + imageUrl: $response->imageUrl, + rating: $response->rating + ); + } +} \ No newline at end of file diff --git a/src/Domain/Manga/Infrastructure/Persistence/LegacyMangaRepository.php b/src/Domain/Manga/Infrastructure/Persistence/LegacyMangaRepository.php index fdbcbd8..d01f291 100644 --- a/src/Domain/Manga/Infrastructure/Persistence/LegacyMangaRepository.php +++ b/src/Domain/Manga/Infrastructure/Persistence/LegacyMangaRepository.php @@ -53,6 +53,14 @@ readonly class LegacyMangaRepository implements MangaRepositoryInterface return $entity ? $this->toDomain($entity) : null; } + public function findBySlug(MangaSlug $slug): ?DomainManga + { + $entity = $this->entityManager->getRepository(EntityManga::class) + ->findOneBy(['slug' => $slug->getValue()]); + + return $entity ? $this->toDomain($entity) : null; + } + public function save(DomainManga $manga): void { $entity = new EntityManga(); diff --git a/tests/Domain/Manga/Adapter/InMemoryMangaRepository.php b/tests/Domain/Manga/Adapter/InMemoryMangaRepository.php index 0f8f6b7..b99b3b7 100644 --- a/tests/Domain/Manga/Adapter/InMemoryMangaRepository.php +++ b/tests/Domain/Manga/Adapter/InMemoryMangaRepository.php @@ -7,6 +7,7 @@ use App\Domain\Manga\Domain\Model\Chapter; use App\Domain\Manga\Domain\Model\Manga; use App\Domain\Manga\Domain\Model\ValueObject\ChapterId; use App\Domain\Manga\Domain\Model\ValueObject\ExternalId; +use App\Domain\Manga\Domain\Model\ValueObject\MangaSlug; class InMemoryMangaRepository implements MangaRepositoryInterface { @@ -47,6 +48,16 @@ class InMemoryMangaRepository implements MangaRepositoryInterface return $this->mangas[$id] ?? null; } + public function findBySlug(MangaSlug $slug): ?Manga + { + foreach ($this->mangas as $manga) { + if ($manga->getSlug()->getValue() === $slug->getValue()) { + return $manga; + } + } + return null; + } + public function save(Manga $manga): void { $this->mangas[$manga->getId()->getValue()] = $manga; diff --git a/tests/Domain/Manga/Application/QueryHandler/GetMangaBySlugHandlerTest.php b/tests/Domain/Manga/Application/QueryHandler/GetMangaBySlugHandlerTest.php new file mode 100644 index 0000000..fb1fcbc --- /dev/null +++ b/tests/Domain/Manga/Application/QueryHandler/GetMangaBySlugHandlerTest.php @@ -0,0 +1,74 @@ +repository = new InMemoryMangaRepository(); + $this->handler = new GetMangaBySlugHandler($this->repository); + } + + public function testHandleReturnsCorrectMangaResponse(): void + { + // Arrange + $manga = new Manga( + new MangaId('1'), + new MangaTitle('One Piece'), + new MangaSlug('one-piece'), + 'Description', + 'Eiichiro Oda', + 1997, + ['Action', 'Adventure'], + 'ongoing', + null, + 'https://example.com/image.jpg', + 4.5 + ); + $this->repository->save($manga); + + // Act + $response = $this->handler->handle(new GetMangaBySlug('one-piece')); + + // Assert + $this->assertEquals('1', $response->id); + $this->assertEquals('One Piece', $response->title); + $this->assertEquals('one-piece', $response->slug); + $this->assertEquals('Description', $response->description); + $this->assertEquals('Eiichiro Oda', $response->author); + $this->assertEquals(1997, $response->publicationYear); + $this->assertEquals(['Action', 'Adventure'], $response->genres); + $this->assertEquals('ongoing', $response->status); + $this->assertNull($response->externalId); + $this->assertEquals('https://example.com/image.jpg', $response->imageUrl); + $this->assertEquals(4.5, $response->rating); + } + + public function testHandleThrowsExceptionWhenMangaNotFound(): void + { + // Assert + $this->expectException(MangaNotFoundException::class); + + // Act + $this->handler->handle(new GetMangaBySlug('non-existent-manga')); + } + + protected function tearDown(): void + { + $this->repository->clear(); + } +} \ No newline at end of file diff --git a/tests/Feature/Manga/GetMangaBySlugTest.php b/tests/Feature/Manga/GetMangaBySlugTest.php new file mode 100644 index 0000000..bd4aa3e --- /dev/null +++ b/tests/Feature/Manga/GetMangaBySlugTest.php @@ -0,0 +1,59 @@ + 'One Piece', + 'slug' => 'one-piece', + 'description' => 'Description', + 'author' => 'Eiichiro Oda', + 'publicationYear' => 1997, + 'genres' => ['Action', 'Adventure'], + 'status' => 'ongoing', + 'imageUrl' => 'https://example.com/image.jpg', + 'rating' => 4.5, + 'monitored' => true + ]); + + // Act + static::createClient()->request('GET', '/api/mangas/by-slug/one-piece'); + + // Assert + $this->assertResponseIsSuccessful(); + $this->assertJsonContains([ + 'id' => (string) $manga->getId(), + 'title' => 'One Piece', + 'slug' => 'one-piece', + 'description' => 'Description', + 'author' => 'Eiichiro Oda', + 'publicationYear' => 1997, + 'genres' => ['Action', 'Adventure'], + 'status' => 'ongoing', + 'imageUrl' => 'https://example.com/image.jpg', + 'rating' => 4.5 + ]); + } + + public function testGetMangaBySlugReturns404WhenMangaNotFound(): void + { + // Act + static::createClient()->request('GET', '/api/mangas/by-slug/non-existent-manga'); + + // Assert + $this->assertResponseStatusCodeSame(Response::HTTP_NOT_FOUND); + } +} \ No newline at end of file