feat: analyse import + all tests fixed
This commit is contained in:
parent
fbe9619224
commit
3170a7c60e
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\Manga\Application\QueryHandler;
|
||||
|
||||
use App\Domain\Manga\Application\Query\FindMangaMatchByFilename;
|
||||
use App\Domain\Manga\Application\Response\MangaMatchItem;
|
||||
use App\Domain\Manga\Application\Response\MangaMatchResponse;
|
||||
use App\Domain\Manga\Domain\Contract\Repository\MangaRepositoryInterface;
|
||||
use App\Domain\Manga\Domain\Contract\Service\FilenameAnalyzerInterface;
|
||||
use App\Domain\Manga\Domain\Model\Manga;
|
||||
|
||||
readonly class FindMangaMatchByFilenameHandler
|
||||
{
|
||||
public function __construct(
|
||||
private FilenameAnalyzerInterface $filenameAnalyzer,
|
||||
private MangaRepositoryInterface $mangaRepository
|
||||
) {
|
||||
}
|
||||
|
||||
public function handle(FindMangaMatchByFilename $query): MangaMatchResponse
|
||||
{
|
||||
// Analyser le nom de fichier pour extraire les informations
|
||||
$analyzedFilename = $this->filenameAnalyzer->analyze($query->filename);
|
||||
|
||||
$searchedTitle = $analyzedFilename->getTitle()->getValue();
|
||||
$chapterNumber = $analyzedFilename->hasChapterNumber()
|
||||
? $analyzedFilename->getChapterNumber()->getValue()
|
||||
: null;
|
||||
$volumeNumber = $analyzedFilename->hasVolumeNumber()
|
||||
? $analyzedFilename->getVolumeNumber()->getValue()
|
||||
: null;
|
||||
|
||||
// Rechercher les mangas correspondants
|
||||
$foundMangas = $this->mangaRepository->search($searchedTitle, 1, 10);
|
||||
$matches = [];
|
||||
|
||||
foreach ($foundMangas as $manga) {
|
||||
$mangaId = $manga->getId()->getValue();
|
||||
|
||||
// Calculer un score de correspondance
|
||||
$matchScore = $this->calculateMatchScore(
|
||||
$manga,
|
||||
$searchedTitle
|
||||
);
|
||||
|
||||
$matches[] = new MangaMatchItem(
|
||||
id: $mangaId,
|
||||
title: $manga->getTitle()->getValue(),
|
||||
slug: $manga->getSlug()->getValue(),
|
||||
alternativeSlugs: $manga->getAlternativeSlugs(),
|
||||
thumbnailUrl: $manga->getImageUrls()->getThumbnail(),
|
||||
matchScore: $matchScore,
|
||||
chapterNumber: $chapterNumber,
|
||||
volumeNumber: $volumeNumber
|
||||
);
|
||||
}
|
||||
|
||||
// Trier les résultats par score de correspondance (du plus élevé au plus faible)
|
||||
usort($matches, fn($a, $b) => $b->matchScore <=> $a->matchScore);
|
||||
|
||||
return new MangaMatchResponse(
|
||||
matches: $matches,
|
||||
chapterNumber: $chapterNumber,
|
||||
volumeNumber: $volumeNumber,
|
||||
possibleTitles: [$searchedTitle]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calcule un score de correspondance entre le manga et le titre recherché
|
||||
* Score plus élevé = meilleure correspondance
|
||||
*/
|
||||
private function calculateMatchScore(Manga $manga, string $searchedTitle): int
|
||||
{
|
||||
$score = 0;
|
||||
$mangaTitle = $manga->getTitle()->getValue();
|
||||
$mangaSlug = $manga->getSlug()->getValue();
|
||||
|
||||
// Correspondance exacte avec le titre
|
||||
if (strtolower($mangaTitle) === strtolower($searchedTitle)) {
|
||||
$score += 100;
|
||||
}
|
||||
|
||||
// Correspondance exacte avec le slug
|
||||
if (strtolower($mangaSlug) === strtolower($searchedTitle)) {
|
||||
$score += 90;
|
||||
}
|
||||
|
||||
// Correspondance avec les slugs alternatifs
|
||||
foreach ($manga->getAlternativeSlugs() as $altSlug) {
|
||||
if (strtolower($altSlug) === strtolower($searchedTitle)) {
|
||||
$score += 80;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Le titre du manga contient le terme recherché
|
||||
if (stripos($mangaTitle, $searchedTitle) !== false) {
|
||||
$score += 50;
|
||||
}
|
||||
|
||||
// Le terme recherché contient le titre du manga
|
||||
if (stripos($searchedTitle, $mangaTitle) !== false) {
|
||||
$score += 40;
|
||||
}
|
||||
|
||||
// Similarité de Levenshtein (pour les fautes de frappe)
|
||||
$levenshteinDistance = levenshtein(
|
||||
strtolower($mangaTitle),
|
||||
strtolower($searchedTitle)
|
||||
);
|
||||
|
||||
if ($levenshteinDistance <= 3) {
|
||||
$score += (3 - $levenshteinDistance) * 10;
|
||||
}
|
||||
|
||||
return $score;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ readonly class GetMangaBySlugHandler
|
||||
id: $manga->getId()->getValue(),
|
||||
title: $manga->getTitle()->getValue(),
|
||||
slug: $manga->getSlug()->getValue(),
|
||||
alternativeSlugs: $manga->getAlternativeSlugs(),
|
||||
description: $manga->getDescription(),
|
||||
author: $manga->getAuthor(),
|
||||
publicationYear: $manga->getPublicationYear(),
|
||||
@@ -34,7 +35,8 @@ readonly class GetMangaBySlugHandler
|
||||
externalId: $manga->getExternalId()?->getValue(),
|
||||
imageUrl: $manga->getImageUrl(),
|
||||
thumbnailUrl: $manga->getImageUrls()?->getThumbnail(),
|
||||
rating: $manga->getRating()
|
||||
rating: $manga->getRating(),
|
||||
monitored: $manga->isMonitored()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ readonly class SearchLocalMangaHandler
|
||||
id: $manga->getId()->getValue(),
|
||||
title: $manga->getTitle()->getValue(),
|
||||
slug: $manga->getSlug()->getValue(),
|
||||
alternativeSlugs: $manga->getAlternativeSlugs(),
|
||||
description: $manga->getDescription(),
|
||||
author: $manga->getAuthor(),
|
||||
publicationYear: $manga->getPublicationYear(),
|
||||
@@ -33,7 +34,8 @@ readonly class SearchLocalMangaHandler
|
||||
externalId: $manga->getExternalId()?->getValue() ?? '',
|
||||
imageUrl: $manga->getImageUrls()->getFull(),
|
||||
thumbnailUrl: $manga->getImageUrls()->getThumbnail(),
|
||||
rating: $manga->getRating()
|
||||
rating: $manga->getRating(),
|
||||
monitored: $manga->isMonitored()
|
||||
),
|
||||
$mangas
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user