diff --git a/Webzine.Repository.Contracts/IArtisteRepository.cs b/Webzine.Repository.Contracts/IArtisteRepository.cs index 2f112fc..2f26de5 100644 --- a/Webzine.Repository.Contracts/IArtisteRepository.cs +++ b/Webzine.Repository.Contracts/IArtisteRepository.cs @@ -1,17 +1,46 @@ -using Webzine.Entity; - namespace Webzine.Repository.Contracts { + using Webzine.Entity; + /// + /// Défini une interface pour gérer les opérations de base de données liées aux artistes. + /// public interface IArtisteRepository { + /// + /// Ajoute un nouvel artiste. + /// + /// L'artiste à ajouter à la collection. Ne peut pas être null. void Add(Artiste artiste); + /// + /// Supprime un artiste. + /// + /// L'artiste à supprimer. void Delete(Artiste artiste); + /// + /// Récupère un artiste par son identifiant unique. Si aucun artiste n'est trouvé, retourne null. + /// + /// L'identifiant de l'artiste. + /// Artiste Find(int id); + /// + /// Récupère un artiste par son nom. Si aucun artiste n'est trouvé, retourne null. + /// + /// Le nom de l'artiste. + /// L'artiste recherché ou null. + Artiste FindByName(string name); + /// + /// Récupère tous les artistes disponibles dans la collection. Si aucun artiste n'est trouvé, retourne une collection vide. + /// + /// Retourne une collection d'artistes. IEnumerable FindAll(); + /// + /// Met à jour les informations d'un artiste existant dans la collection. + /// + /// L'artiste à mettre à jour. void Update(Artiste artiste); } } \ No newline at end of file diff --git a/Webzine.Repository/DbArtisteRepository.cs b/Webzine.Repository/DbArtisteRepository.cs new file mode 100644 index 0000000..5aa9bb7 --- /dev/null +++ b/Webzine.Repository/DbArtisteRepository.cs @@ -0,0 +1,142 @@ +namespace Webzine.Repository +{ + using Microsoft.EntityFrameworkCore; + using Microsoft.Extensions.Logging; + using Webzine.EntitiesContext; + using Webzine.Entity; + using Webzine.Repository.Contracts; + + /// + /// Initialise une classe qui implémente l'interface pour gérer les opérations de base de données liées aux artistes. + /// Utilise en injection de dépendances. + /// + public class DbArtisteRepository : IArtisteRepository + { + private WebzineDbContext _context; + private readonly ILogger _logger; + + + /// + /// Initializes a new instance of the class. + /// + /// Le contexte de base de données à utiliser pour accéder aux entités et effectuer des opérations de + /// persistance. Ne peut pas être null. + public DbArtisteRepository(WebzineDbContext context, ILogger logger) + { + this._logger = logger; + this._context = context; + } + + /// + public void Add(Artiste artiste) + { + try + { + if (artiste == null) + { + throw new ArgumentNullException(nameof(artiste), "L'artiste à ajouter ne peut pas être null."); + } + + this._context.Artistes.Add(artiste); + this._context.SaveChanges(); + } + catch (Exception ex) + { + this._logger.LogError(ex, "Une erreur est survenue lors de l'ajout de l'artiste {Nom}.", artiste?.Nom); + throw new Exception("Une erreur est survenue lors de l'ajout de l'artiste.", ex); + } + } + + /// + public void Delete(Artiste artiste) + { + try + { + if (artiste == null) + { + throw new ArgumentNullException(nameof(artiste), "L'artiste à supprimer ne peut pas être null."); + } + + this._context.Artistes.Remove(artiste); + this._context.SaveChanges(); + } + catch (Exception ex) + { + this._logger.LogError(ex, "Une erreur est survenue lors de la suppression de l'artiste {Nom}.", artiste?.Nom); + throw new Exception("Une erreur est survenue lors de la suppression de l'artiste.", ex); + } + } + + /// + public Artiste Find(int id) + { + Artiste artiste = this._context.Artistes + .Include(a => a.Titres) + .FirstOrDefault(a => a.IdArtiste == id); + if (artiste == null) + { + this._logger.LogWarning("Aucun artiste trouvé avec l'identifiant {Id}", id); + } + return artiste; + } + + /// + public Artiste FindByName(string nom) + { + if (string.IsNullOrWhiteSpace(nom)) + { + this._logger.LogWarning("Tentative de recherche avec un nom vide ou null."); + return null; + } + + var artiste = this._context.Artistes + .Include(a => a.Titres) + .FirstOrDefault(a => a.Nom == nom); + + if (artiste == null) + { + this._logger.LogWarning("Recherche Nom : Aucun artiste trouvé pour '{Nom}'.", nom); + } + + return artiste; + } + + /// + public IEnumerable FindAll() + { + try + { + // .AsNoTracking() rend la requête beaucoup plus rapide pour de la lecture + var artistes = this._context.Artistes.AsNoTracking().ToList(); + this._logger.LogInformation("{Count} artistes récupérés de la base.", artistes.Count); + return artistes; + } + catch (Exception ex) + { + this._logger.LogError(ex, "Erreur lors de la récupération de tous les artistes."); + return Enumerable.Empty(); // Retourne une liste vide au lieu de faire crash l'UI + } + } + + /// + public void Update(Artiste artiste) + { + if (artiste == null) + { + throw new ArgumentNullException(nameof(artiste)); + } + + try + { + this._context.Artistes.Update(artiste); + this._context.SaveChanges(); + this._logger.LogInformation("Artiste {Id} ({Nom}) mis à jour avec succès.", artiste.IdArtiste, artiste.Nom); + } + catch (Exception ex) + { + this._logger.LogError(ex, "Erreur lors de la mise à jour de l'artiste {Id}.", artiste.IdArtiste); + throw; + } + } + } +} \ No newline at end of file diff --git a/Webzine.Repository/LocalArtisteRepository.cs b/Webzine.Repository/LocalArtisteRepository.cs new file mode 100644 index 0000000..0841d3c --- /dev/null +++ b/Webzine.Repository/LocalArtisteRepository.cs @@ -0,0 +1,133 @@ +namespace Webzine.Repository +{ + using Microsoft.Extensions.Logging; + using Webzine.Entity; + using Webzine.Repository.Contracts; + + /// + /// Initialise une classe qui implémente l'interface pour gérer les opérations de base de données liées aux artistes. + /// Utilise en injection de dépendances. + /// + public class LocalArtisteRepository : IArtisteRepository + { + private readonly ILogger _logger; + private readonly List _artistes; + + /// + /// Initializes a new instance of the class. + /// Est liéee à une liste d'artistes en local et utilise un logger pour enregistrer les opérations effectuées sur les artistes. + /// + /// La liste des artistes à initialiser. Ne peut pas être null. + /// Le logger à utiliser pour enregistrer les messages de journalisation. Ne peut pas être null. + public LocalArtisteRepository(List artistes, ILogger logger) + { + this._logger = logger; + this._artistes = artistes; + } + + /// + public void Add(Artiste artiste) + { + try + { + if (artiste == null) + { + this._logger.LogError("L'artiste à ajouter ne peut pas être null."); + throw new ArgumentNullException(nameof(artiste)); + } + + if (this._artistes.Any(a => a.IdArtiste == artiste.IdArtiste)) + { + this._logger.LogWarning("Un artiste avec l'ID {Id} existe déjà. L'ajout est ignoré.", artiste.IdArtiste); + return; + } + + this._artistes.Add(artiste); + this._logger.LogInformation("Artiste ajouté : {Nom}", artiste.Nom); + } + catch (Exception ex) + { + this._logger.LogError(ex, "Erreur lors de l'ajout de l'artiste : {Nom}", artiste?.Nom); + throw; + } + } + + /// + public void Delete(Artiste artiste) + { + try + { + this._artistes.Remove(artiste); + this._logger.LogInformation("Artiste supprimé : {Nom}", artiste.Nom); + } + catch (Exception ex) + { + this._logger.LogError(ex, "Erreur lors de la suppression de l'artiste : {Nom}", artiste.Nom); + throw; + } + } + + /// + public Artiste Find(int id) + { + Artiste artiste = this._artistes.FirstOrDefault(a => a.IdArtiste == id); + if (artiste == null) + { + this._logger.LogWarning("Aucun artiste trouvé avec l'identifiant {Id}", id); + } + return artiste; + } + + /// + public Artiste FindByName(string nom) + { + Artiste artiste = this._artistes.FirstOrDefault(a => a.Nom == nom); + if (artiste == null) + { + this._logger.LogWarning("Aucun artiste trouvé avec le nom {Nom}", nom); + } + return artiste; + } + + /// + /// La liste retournée est une copie de la liste interne, donc elle ne peut être nulle. + public IEnumerable FindAll() + { + return this._artistes; + } + + /// + public void Update(Artiste artiste) + { + if (artiste == null) + { + this._logger.LogError("L'artiste à mettre à jour ne peut pas être null."); + throw new ArgumentNullException(nameof(artiste)); + } + + try + { + var artisteToUpdate = this._artistes.FirstOrDefault(a => a.IdArtiste == artiste.IdArtiste); + + if (artisteToUpdate != null) + { + artisteToUpdate.Nom = artiste.Nom; + artisteToUpdate.Biographie = artiste.Biographie; + artisteToUpdate.Titres = artiste.Titres; + + this._logger.LogInformation("Artiste {Id} mis à jour avec succès.", artiste.IdArtiste); + } + else + { + this._logger.LogWarning("Mise à jour impossible : l'artiste avec l'ID {Id} n'existe pas.", artiste.IdArtiste); + throw new KeyNotFoundException($"L'artiste {artiste.IdArtiste} est introuvable."); + } + } + catch (Exception ex) + { + this._logger.LogError(ex, "Une erreur est survenue lors de la mise à jour de l'artiste {Id}.", artiste.IdArtiste); + throw; + } + } + } +} \ No newline at end of file diff --git a/Webzine.WebApplication/Areas/Administration/Controllers/ArtisteController.cs b/Webzine.WebApplication/Areas/Administration/Controllers/ArtisteController.cs index d059092..13266c2 100644 --- a/Webzine.WebApplication/Areas/Administration/Controllers/ArtisteController.cs +++ b/Webzine.WebApplication/Areas/Administration/Controllers/ArtisteController.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Mvc; using Webzine.Entity; using Webzine.Entity.Fixtures; +using Webzine.Repository.Contracts; using Webzine.WebApplication.Areas.Administration.ViewModels.Artiste; namespace Webzine.WebApplication.Areas.Administration.Controllers; @@ -10,16 +11,17 @@ public class ArtisteController : Controller { // Injection du logger via le constructeur private readonly ILogger _logger; - private readonly List _artistes; + private readonly IArtisteRepository _artisteRepository; + private readonly List _artistes = new List(); - public ArtisteController(ILogger logger) + public ArtisteController(ILogger logger, + IArtisteRepository artisteRepository) { this._logger = logger; this._logger.LogDebug(1, "initialisation du ArtisteController d'administration"); - var factory = new DataFactory(); - - _artistes = factory.GenerateArtists(10); + this._artisteRepository = artisteRepository; + this._artistes.AddRange(this._artisteRepository.FindAll()); } /// @@ -30,9 +32,7 @@ public class ArtisteController : Controller public IActionResult Index() { - var _artistes_ordre = _artistes.OrderBy(t => t.Nom).ToList(); - - this._logger.LogInformation("Initialisation du contrôleur TitreController pour l'Administration."); + var _artistes_ordre = this._artistes.OrderBy(t => t.Nom).ToList(); return View(_artistes_ordre); } @@ -60,7 +60,7 @@ public class ArtisteController : Controller /// Redirection. public IActionResult Edit(int id) { - var artiste = _artistes.First(t => t.IdArtiste == id); + var artiste = this._artistes.First(t => t.IdArtiste == id); var model = new AdminArtisteForm { @@ -79,7 +79,7 @@ public class ArtisteController : Controller /// Redirection.> public IActionResult Delete(int id) { - var artiste = _artistes.First(t => t.IdArtiste == id); + var artiste = this._artistes.First(t => t.IdArtiste == id); var model = new AdminArtisteForm { Id = id, diff --git a/Webzine.WebApplication/Controllers/ArtisteController.cs b/Webzine.WebApplication/Controllers/ArtisteController.cs index c44ff99..68b2443 100644 --- a/Webzine.WebApplication/Controllers/ArtisteController.cs +++ b/Webzine.WebApplication/Controllers/ArtisteController.cs @@ -1,21 +1,26 @@ -using Microsoft.AspNetCore.Mvc; -using Webzine.Entity.Fixtures; - -namespace Webzine.WebApplication.Controllers +namespace Webzine.WebApplication.Controllers { + using Microsoft.AspNetCore.Mvc; + using Webzine.Entity.Fixtures; + using Webzine.Repository.Contracts; + using Webzine.WebApplication.ViewModels.Artiste; + public class ArtisteController : Controller { // Injection du logger via le constructeur - private readonly ILogger logger; + private readonly ILogger _logger; + private readonly IArtisteRepository _artisteRepository; /// - /// Initialise une nouvelle instance de la classe . + /// Initialise une nouvelle instance du . avec un service de journalisation injecté. /// /// Service de journalisation injecté pour enregistrer les événements et les erreurs. - public ArtisteController(ILogger logger) + public ArtisteController(ILogger logger, + IArtisteRepository artisteRepository) { - this.logger = logger; - this.logger.LogDebug(1, "initialisation du ArtisteController"); + this._logger = logger; + this._logger.LogDebug("Initialisation du ArtisteController"); + this._artisteRepository = artisteRepository; } /// @@ -30,8 +35,8 @@ namespace Webzine.WebApplication.Controllers if (string.IsNullOrEmpty(nom)) { - this.logger.LogWarning("Nom de l'artiste manquant dans la requête."); - return this.RedirectToAction("Index", "Accueil"); + this._logger.LogWarning("Nom de l'artiste manquant dans la requête."); + return RedirectToAction("Index"); } // On transforme "fatal-bazooka" en "Fatal Bazooka" pour la factory @@ -39,11 +44,28 @@ namespace Webzine.WebApplication.Controllers .ToTitleCase(nom.Replace("-", " ")); // On appelle la factory pour obtenir l'artiste unique - var artiste = ArtisteFactory.SeedArtisteByName(nomPropre); + var artiste = this._artisteRepository.FindByName(nomPropre); - this.logger.LogInformation("Artiste trouvé : {NomArtiste}", nom); + if (artiste == null) + { + this._logger.LogWarning("Artiste non trouvé pour le nom : {NomArtiste}", nomPropre); + return RedirectToAction("Index"); + } + var viewModel = new ArtisteDetailsViewModel + { + IdArtiste = artiste.IdArtiste, + Nom = artiste.Nom, + Biographie = artiste.Biographie, + // On effectue le groupement ici une bonne fois pour toutes + AlbumsGroupes = artiste.Titres + .OrderBy(t => t.Libelle) + .GroupBy(t => t.Album) + .OrderBy(g => g.Key), + }; - return this.View(artiste); + this._logger.LogInformation("Artiste trouvé : {NomArtiste}", nom); + + return View(viewModel); } } } diff --git a/Webzine.WebApplication/Program.cs b/Webzine.WebApplication/Program.cs index 5d42025..1cd46cf 100644 --- a/Webzine.WebApplication/Program.cs +++ b/Webzine.WebApplication/Program.cs @@ -34,14 +34,14 @@ try { builder.Services.AddScoped(); builder.Services.AddScoped(); - //builder.Services.AddScoped(); + builder.Services.AddScoped(); //builder.Services.AddScoped(); } else { builder.Services.AddScoped(); builder.Services.AddScoped(); - //builder.Services.AddScoped(); + builder.Services.AddScoped(); //builder.Services.AddScoped(); } diff --git a/Webzine.WebApplication/ViewModels/Artiste/ArtisteDetailsViewModel.cs b/Webzine.WebApplication/ViewModels/Artiste/ArtisteDetailsViewModel.cs new file mode 100644 index 0000000..d1f6e0c --- /dev/null +++ b/Webzine.WebApplication/ViewModels/Artiste/ArtisteDetailsViewModel.cs @@ -0,0 +1,30 @@ +namespace Webzine.WebApplication.ViewModels.Artiste +{ + using Webzine.Entity; + /// + /// ViewModel pour afficher les informations d'un artiste et ses titres groupés par album. + /// + public class ArtisteDetailsViewModel + { + /// + /// Obtient ou définit l'identifiant de l'artiste. + /// + public int IdArtiste { get; set; } + + /// + /// Obtient ou définit le nom de l'artiste. + /// + public string Nom { get; set; } = string.Empty; + + /// + /// Obtient ou définit la biographie de l'artiste. + /// + public string Biographie { get; set; } = string.Empty; + + /// + /// Obtient ou définit défini la liste des titres de l'artiste groupés par nom d'Album. + /// + public IEnumerable> AlbumsGroupes { get; set; } + = Enumerable.Empty>(); + } +} diff --git a/Webzine.WebApplication/Views/Artiste/Index.cshtml b/Webzine.WebApplication/Views/Artiste/Index.cshtml index a95d7d2..a3de4e3 100644 --- a/Webzine.WebApplication/Views/Artiste/Index.cshtml +++ b/Webzine.WebApplication/Views/Artiste/Index.cshtml @@ -1,4 +1,4 @@ -@model Webzine.Entity.Artiste +@model Webzine.WebApplication.ViewModels.Artiste.ArtisteDetailsViewModel; @{ ViewData["Title"] = "Artiste"; @@ -8,22 +8,20 @@

@Model.Nom

-
+

@Model.Biographie

Albums

-
+
- @* On groupe les titres par nom d'album *@ - @{ - var albumsGroupes = Model.Titres - .OrderBy(t => t.Libelle) // Trie les titres par ordre alphabétique au sein de chaque groupe futur - .GroupBy(t => t.Album) // Groupe par nom d'album - .OrderBy(g => g.Key); // Trie les albums par ordre alphabétique (la clé du groupe) - } - - @foreach (var groupe in albumsGroupes) + @if (!Model.AlbumsGroupes.Any()) +{ +

Cet artiste n'a pas encore de titres répertoriés.

+} +else +{ + @foreach (var groupe in Model.AlbumsGroupes) { // On récupère le premier titre du groupe pour afficher l'image de l'album var premierTitre = groupe.First(); @@ -71,4 +69,5 @@
} +} \ No newline at end of file