diff --git a/config/packages/doctrine.yaml b/config/packages/doctrine.yaml index 5001e01..2ddcaab 100644 --- a/config/packages/doctrine.yaml +++ b/config/packages/doctrine.yaml @@ -29,6 +29,11 @@ doctrine: dir: '%kernel.project_dir%/src/Domain/Shared/Infrastructure/Persistence/Entity' prefix: 'App\Domain\Shared\Infrastructure\Persistence\Entity' alias: Shared + Scraping: + is_bundle: false + dir: '%kernel.project_dir%/src/Domain/Scraping/Infrastructure/Persistence/Entity' + prefix: 'App\Domain\Scraping\Infrastructure\Persistence\Entity' + alias: Scraping when@test: doctrine: diff --git a/migrations/Version20250626123300.php b/migrations/Version20250626123300.php new file mode 100644 index 0000000..56aa299 --- /dev/null +++ b/migrations/Version20250626123300.php @@ -0,0 +1,46 @@ +addSql('DROP SEQUENCE manga_preferred_source_order_id_seq CASCADE'); + $this->addSql('CREATE SEQUENCE manga_preferred_sources_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE TABLE manga_preferred_sources (id INT NOT NULL, manga_id INT NOT NULL, ordered_source_ids JSON NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, updated_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_manga_preferred_sources_manga_id ON manga_preferred_sources (manga_id)'); + $this->addSql('COMMENT ON COLUMN manga_preferred_sources.created_at IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('COMMENT ON COLUMN manga_preferred_sources.updated_at IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('DROP TABLE manga_preferred_source_order'); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql('CREATE SCHEMA public'); + $this->addSql('DROP SEQUENCE manga_preferred_sources_id_seq CASCADE'); + $this->addSql('CREATE SEQUENCE manga_preferred_source_order_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE TABLE manga_preferred_source_order (id INT NOT NULL, manga_id INT NOT NULL, source_id INT NOT NULL, "position" INT NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX idx_source_id ON manga_preferred_source_order (source_id)'); + $this->addSql('CREATE INDEX idx_manga_id ON manga_preferred_source_order (manga_id)'); + $this->addSql('CREATE UNIQUE INDEX unique_manga_source ON manga_preferred_source_order (manga_id, source_id)'); + $this->addSql('COMMENT ON COLUMN manga_preferred_source_order.created_at IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('DROP INDEX UNIQ_manga_preferred_sources_manga_id'); + $this->addSql('DROP TABLE manga_preferred_sources'); + } +} diff --git a/src/Domain/Scraping/Application/CommandHandler/ScrapeChapterHandler.php b/src/Domain/Scraping/Application/CommandHandler/ScrapeChapterHandler.php index 95e898f..20f4357 100644 --- a/src/Domain/Scraping/Application/CommandHandler/ScrapeChapterHandler.php +++ b/src/Domain/Scraping/Application/CommandHandler/ScrapeChapterHandler.php @@ -56,7 +56,6 @@ readonly class ScrapeChapterHandler // 3. Détermination des sources à utiliser $sources = $this->getSourcesToTry($manga); - dd($sources); if (empty($sources)) { throw new \InvalidArgumentException("No sources available for scraping"); } diff --git a/src/Domain/Scraping/Application/QueryHandler/GetMangaPreferredSourcesHandler.php b/src/Domain/Scraping/Application/QueryHandler/GetMangaPreferredSourcesHandler.php index 73f6385..094052e 100644 --- a/src/Domain/Scraping/Application/QueryHandler/GetMangaPreferredSourcesHandler.php +++ b/src/Domain/Scraping/Application/QueryHandler/GetMangaPreferredSourcesHandler.php @@ -30,17 +30,20 @@ readonly class GetMangaPreferredSourcesHandler if ($manga->hasPreferredSources()) { $preferredSourceIds = $manga->getPreferredSources(); + // Créer un index des sources par ID pour optimiser les performances + $sourcesByIds = []; + foreach ($allActiveSources as $source) { + $sourcesByIds[$source->getId()->getValue()] = $source; + } + // Séparer les sources préférées et les autres $preferredSources = []; $otherSources = []; - // D'abord, ajouter les sources préférées dans l'ordre de préférence + // D'abord, ajouter les sources préférées dans l'ordre de préférence exactement foreach ($preferredSourceIds as $preferredId) { - foreach ($allActiveSources as $source) { - if ($source->getId()->getValue() === $preferredId) { - $preferredSources[] = $source; - break; - } + if (isset($sourcesByIds[$preferredId])) { + $preferredSources[] = $sourcesByIds[$preferredId]; } } diff --git a/src/Domain/Scraping/Infrastructure/Persistence/Entity/MangaPreferredSourceEntity.php b/src/Domain/Scraping/Infrastructure/Persistence/Entity/MangaPreferredSourceEntity.php new file mode 100644 index 0000000..9418874 --- /dev/null +++ b/src/Domain/Scraping/Infrastructure/Persistence/Entity/MangaPreferredSourceEntity.php @@ -0,0 +1,76 @@ +createdAt = new \DateTimeImmutable(); + $this->updatedAt = new \DateTimeImmutable(); + } + + public function getId(): ?int + { + return $this->id; + } + + public function getMangaId(): int + { + return $this->mangaId; + } + + public function setMangaId(int $mangaId): self + { + $this->mangaId = $mangaId; + $this->updatedAt = new \DateTimeImmutable(); + + return $this; + } + + public function getOrderedSourceIds(): array + { + return $this->orderedSourceIds; + } + + public function setOrderedSourceIds(array $orderedSourceIds): self + { + $this->orderedSourceIds = $orderedSourceIds; + $this->updatedAt = new \DateTimeImmutable(); + + return $this; + } + + public function getCreatedAt(): \DateTimeImmutable + { + return $this->createdAt; + } + + public function getUpdatedAt(): \DateTimeImmutable + { + return $this->updatedAt; + } +} diff --git a/src/Domain/Scraping/Infrastructure/Persistence/LegacyMangaRepository.php b/src/Domain/Scraping/Infrastructure/Persistence/LegacyMangaRepository.php index a101a82..dd921f3 100644 --- a/src/Domain/Scraping/Infrastructure/Persistence/LegacyMangaRepository.php +++ b/src/Domain/Scraping/Infrastructure/Persistence/LegacyMangaRepository.php @@ -4,6 +4,7 @@ namespace App\Domain\Scraping\Infrastructure\Persistence; use App\Domain\Scraping\Domain\Contract\Repository\MangaRepositoryInterface; use App\Domain\Scraping\Domain\Model\Manga; +use App\Domain\Scraping\Infrastructure\Persistence\Entity\MangaPreferredSourceEntity; use App\Entity\Manga as EntityManga; use App\Entity\ContentSource; use Doctrine\ORM\EntityManagerInterface; @@ -24,10 +25,13 @@ readonly class LegacyMangaRepository implements MangaRepositoryInterface return null; } - // Récupération des sources préférées + // Récupération des sources préférées avec l'ordre depuis la nouvelle entité $preferredSourceIds = []; - foreach ($mangaEntity->getPreferredSources() as $source) { - $preferredSourceIds[] = (string) $source->getId(); + $mangaPreferredSource = $this->entityManager->getRepository(MangaPreferredSourceEntity::class) + ->findOneBy(['mangaId' => $mangaEntity->getId()]); + + if ($mangaPreferredSource) { + $preferredSourceIds = $mangaPreferredSource->getOrderedSourceIds(); } return new Manga( @@ -50,26 +54,30 @@ readonly class LegacyMangaRepository implements MangaRepositoryInterface throw new \InvalidArgumentException("Manga not found with ID: {$mangaId}"); } - // Si pas de sources, vider les sources préférées - if (empty($sourceIds)) { - $mangaEntity->setPreferredSources([]); - $this->entityManager->flush(); - return; + // Récupérer ou créer l'entité MangaPreferredSource + $mangaPreferredSource = $this->entityManager->getRepository(MangaPreferredSourceEntity::class) + ->findOneBy(['mangaId' => (int) $mangaId]); + + if (!$mangaPreferredSource) { + $mangaPreferredSource = new MangaPreferredSourceEntity(); + $mangaPreferredSource->setMangaId((int) $mangaId); } - // Récupérer les sources existantes - $sources = $this->entityManager->getRepository(ContentSource::class)->findBy(['id' => $sourceIds]); + // Si pas de sources, vider les sources préférées + if (empty($sourceIds)) { + $mangaPreferredSource->setOrderedSourceIds([]); + } else { + // Valider que toutes les sources existent avant de les sauvegarder + $sources = $this->entityManager->getRepository(ContentSource::class)->findBy(['id' => $sourceIds]); + $existingSourceIds = array_map(fn($source) => (string) $source->getId(), $sources); - // Maintenir l'ordre exact des sources comme dans l'ancien controller - $orderedPreferredSources = array_map( - fn ($id) => current(array_filter($sources, fn ($s) => $s->getId() == $id)), - $sourceIds - ); + // Garder uniquement les sources qui existent et maintenir l'ordre + $validSourceIds = array_values(array_intersect($sourceIds, $existingSourceIds)); - // Filtrer les sources nulles (au cas où certaines n'existeraient pas) - $validSources = array_filter($orderedPreferredSources); + $mangaPreferredSource->setOrderedSourceIds($validSourceIds); + } - $mangaEntity->setPreferredSources($validSources); + $this->entityManager->persist($mangaPreferredSource); $this->entityManager->flush(); } }