mangaRepository->findById($command->mangaId); if (!$manga) { throw new MangaNotFoundException($command->mangaId); } // 2. Validate that the file is a valid CBZ if (!$this->isValidCbzFile($command->fileBinary)) { throw new \InvalidArgumentException('The provided file is not a valid CBZ file'); } // 3. Get all chapters for this volume $chapters = $this->chapterRepository->findByMangaIdAndVolume( $command->mangaId, $command->volumeNumber ); if (empty($chapters)) { throw new \InvalidArgumentException( "No chapters found for manga {$command->mangaId} in volume {$command->volumeNumber}" ); } // 4. Save the CBZ file to storage using the path manager $cbzPath = $this->saveCbzFile($command, $manga); // 5. Update all chapters with the volume CBZ path foreach ($chapters as $chapter) { $updatedChapter = new Chapter( id: new ChapterId($chapter->getId()), mangaId: $chapter->getMangaId(), number: $chapter->getNumber(), title: $chapter->getTitle(), volume: $chapter->getVolume(), isVisible: $chapter->isVisible(), cbzPath: $cbzPath, createdAt: $chapter->getCreatedAt() ); $this->chapterRepository->save($updatedChapter); } } /** * Validate that the binary data is a valid CBZ (ZIP) file */ private function isValidCbzFile(string $fileBinary): bool { // CBZ files are ZIP archives, check for ZIP magic number $zipMagicNumber = "\x50\x4b\x03\x04"; // PK\x03\x04 return strpos($fileBinary, $zipMagicNumber) === 0; } /** * Save the CBZ file to storage and return the path */ private function saveCbzFile(ImportVolume $command, \App\Domain\Manga\Domain\Model\Manga $manga): string { // Build the final CBZ path using the path manager (creates directories) $cbzPath = $this->pathManager->buildVolumeCbzPath( $manga->getTitle()->getValue(), (string)$manga->getPublicationYear(), $command->volumeNumber ); // Write the binary content directly to the CBZ path if (!file_put_contents($cbzPath, $command->fileBinary)) { throw new \RuntimeException('Failed to save CBZ file'); } return $cbzPath; } }