store = new JsonStore(Config::get("dataPath") . "/jeux.json"); } public function listerPourUtilisateur( string $utilisateurId, ?string $recherche = null, ?string $consoleId = null, ?string $etat = null, ?string $format = null ): array { $jeux = $this->store->filtrer( static fn (array $jeu): bool => ($jeu["utilisateurId"] ?? "") === $utilisateurId ); if ($consoleId !== null && $consoleId !== "") { $jeux = array_values(array_filter( $jeux, static fn (array $jeu): bool => ($jeu["consoleId"] ?? "") === $consoleId )); } if ($etat !== null && $etat !== "") { $jeux = array_values(array_filter( $jeux, static fn (array $jeu): bool => ($jeu["etat"] ?? "") === $etat )); } if ($format !== null && $format !== "") { $jeux = array_values(array_filter( $jeux, static function (array $jeu) use ($format): bool { $formats = $jeu["formats"] ?? []; return is_array($formats) && in_array($format, $formats, true); } )); } if ($recherche !== null && $recherche !== "") { $terme = mb_strtolower($recherche); $jeux = array_values(array_filter( $jeux, static function (array $jeu) use ($terme): bool { $champs = [ $jeu["titre"] ?? "", $jeu["description"] ?? "", $jeu["editeur"] ?? "", $jeu["developpeur"] ?? "", implode(" ", $jeu["genres"] ?? []), implode(" ", $jeu["formats"] ?? []) ]; return str_contains(mb_strtolower(implode(" ", $champs)), $terme); } )); } usort( $jeux, static fn (array $a, array $b): int => strcmp($a["titre"] ?? "", $b["titre"] ?? "") ); return $jeux; } public function trouverPourUtilisateur(string $id, string $utilisateurId): ?array { $jeu = $this->store->trouverParId($id); if ($jeu === null || ($jeu["utilisateurId"] ?? "") !== $utilisateurId) { return null; } return $jeu; } public function listerParConsole(string $consoleId, string $utilisateurId): array { return $this->listerPourUtilisateur($utilisateurId, null, $consoleId, null); } public function derniersAjouts(string $utilisateurId, int $limite = 6): array { $jeux = $this->listerPourUtilisateur($utilisateurId); usort( $jeux, static fn (array $a, array $b): int => strcmp($b["createdAt"] ?? "", $a["createdAt"] ?? "") ); return array_slice($jeux, 0, $limite); } public function creer(string $utilisateurId, array $donnees): array { $jeu = $this->preparerDonnees($utilisateurId, $donnees); $maintenant = Helpers::maintenant(); $jeu["id"] = Helpers::genererId(); $jeu["utilisateurId"] = $utilisateurId; $jeu["createdAt"] = $maintenant; $jeu["updatedAt"] = $maintenant; return $this->store->ajouter($jeu); } public function modifier(string $id, string $utilisateurId, array $donnees): array { $existant = $this->trouverPourUtilisateur($id, $utilisateurId); if ($existant === null) { throw new RuntimeException("Jeu introuvable."); } $ancienneJaquette = $existant["jaquette"] ?? null; $jeu = $this->preparerDonnees($utilisateurId, $donnees, $existant); $jeu["updatedAt"] = Helpers::maintenant(); $miseAJour = $this->store->mettreAJour($id, $jeu); if ($miseAJour === null) { throw new RuntimeException("Échec de la mise à jour."); } $nouvelleJaquette = $miseAJour["jaquette"] ?? null; if ( $ancienneJaquette !== null && $ancienneJaquette !== "" && $ancienneJaquette !== $nouvelleJaquette ) { $this->supprimerJaquetteLocale($ancienneJaquette); } return $miseAJour; } public function remplacerJaquette(string $id, string $utilisateurId, array $fichier): array { $jeu = $this->trouverPourUtilisateur($id, $utilisateurId); if ($jeu === null) { throw new RuntimeException("Jeu introuvable."); } $nouvelleJaquette = ImageUpload::enregistrer( $fichier, Config::get("jaquettesPath") ); $this->supprimerJaquetteLocale($jeu["jaquette"] ?? null); $miseAJour = $this->store->mettreAJour($id, [ "jaquette" => $nouvelleJaquette, "sourceEnrichissement" => "manuel", "updatedAt" => Helpers::maintenant() ]); if ($miseAJour === null) { throw new RuntimeException("Échec de la mise à jour de la jaquette."); } return $miseAJour; } public function supprimer(string $id, string $utilisateurId): bool { $jeu = $this->trouverPourUtilisateur($id, $utilisateurId); if ($jeu === null) { return false; } $this->supprimerJaquetteLocale($jeu["jaquette"] ?? null); return $this->store->supprimer($id); } public function compterPourUtilisateur(string $utilisateurId): int { return count($this->listerPourUtilisateur($utilisateurId)); } public function supprimerParConsole(string $consoleId, string $utilisateurId): int { $jeux = $this->listerParConsole($consoleId, $utilisateurId); $compteur = 0; foreach ($jeux as $jeu) { if ($this->supprimer($jeu["id"], $utilisateurId)) { $compteur++; } } return $compteur; } private function preparerDonnees(string $utilisateurId, array $donnees, ?array $existant = null): array { $titre = trim((string) ($donnees["titre"] ?? "")); $consoleId = trim((string) ($donnees["consoleId"] ?? "")); $etat = trim((string) ($donnees["etat"] ?? "possede")); if ($titre === "" || $consoleId === "") { throw new InvalidArgumentException("Le titre et la console sont obligatoires."); } if (!array_key_exists($etat, Helpers::etatsJeu())) { throw new InvalidArgumentException("État de jeu invalide."); } $genres = $donnees["genres"] ?? []; if (is_string($genres)) { $genres = array_values(array_filter(array_map("trim", explode(",", $genres)))); } if (!is_array($genres)) { $genres = []; } $jaquette = trim((string) ($donnees["jaquette"] ?? ($existant["jaquette"] ?? ""))); return [ "titre" => $titre, "consoleId" => $consoleId, "etat" => $etat, "formats" => Helpers::normaliserFormats($donnees["formats"] ?? []), "jaquette" => $jaquette, "description" => trim((string) ($donnees["description"] ?? "")), "dateSortie" => $this->normaliserDate($donnees["dateSortie"] ?? null), "genres" => array_values($genres), "editeur" => trim((string) ($donnees["editeur"] ?? "")), "developpeur" => trim((string) ($donnees["developpeur"] ?? "")), "notes" => trim((string) ($donnees["notes"] ?? "")), "sourceEnrichissement" => trim((string) ($donnees["sourceEnrichissement"] ?? "manuel")) ]; } private function normaliserDate(mixed $date): ?string { if ($date === null || $date === "") { return null; } $date = (string) $date; if (!preg_match("/^\d{4}-\d{2}-\d{2}$/", $date)) { return null; } return $date; } private function supprimerJaquetteLocale(?string $chemin): void { if ($chemin === null || $chemin === "") { return; } if (str_starts_with($chemin, "http://") || str_starts_with($chemin, "https://")) { return; } $fichier = Config::get("jaquettesPath") . "/" . basename($chemin); if (is_file($fichier)) { unlink($fichier); } } }