diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Detail/GameDetail.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Detail/GameDetail.razor new file mode 100644 index 0000000..382e0d7 --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Detail/GameDetail.razor @@ -0,0 +1,59 @@ +@page "/Detail/{GameId:int}" +@using GameIdeas.BlazorApp.Shared.Components.Header +@layout MainLayout + + + + + +
+

@Game.Title

+
@Game.Description
+
+ +
+

+
+ @foreach (var property in Game.Properties ?? []) + { +
@property.Label
+ } +
+

+
+ @foreach (var property in Game.Tags ?? []) + { +
@property.Label
+ } +
+
+ +
+
+ +
+

@ResourcesKey.About

+
+ +
+
+ @ResourcesKey.Platforms + @foreach (var platform in Game.Platforms ?? []) + { + @platform.Label + } +
+
+ @ResourcesKey.Platforms + @Game.Developer +
+ +
+ @ResourcesKey.Platforms + @Game.Publisher +
+
+ +
+
+
\ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Detail/GameDetail.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Detail/GameDetail.razor.cs new file mode 100644 index 0000000..035976c --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Detail/GameDetail.razor.cs @@ -0,0 +1,19 @@ +using GameIdeas.BlazorApp.Pages.Games.Gateways; +using GameIdeas.Shared.Dto; +using Microsoft.AspNetCore.Components; + +namespace GameIdeas.BlazorApp.Pages.Detail; + +public partial class GameDetail +{ + [Inject] private IGameGateway GameGateway { get; set; } = default!; + [Parameter] public int GameId { get; set; } + + private GameDetailDto Game = new(); + + protected override async Task OnInitializedAsync() + { + Game = await GameGateway.GetGameById(GameId); + await base.OnInitializedAsync(); + } +} \ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Detail/GameDetail.razor.css b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Detail/GameDetail.razor.css new file mode 100644 index 0000000..5f28270 --- /dev/null +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Detail/GameDetail.razor.css @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor index 103208d..3268e75 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor @@ -1,5 +1,6 @@ @using Blazored.FluentValidation @using GameIdeas.BlazorApp.Shared.Components.CircleLoader +@using GameIdeas.BlazorApp.Shared.Components.Select.Models @using GameIdeas.BlazorApp.Shared.Components.SelectSearch @using GameIdeas.BlazorApp.Shared.Components.Slider @using GameIdeas.Shared.Dto @@ -23,14 +24,14 @@
@ResourcesKey.Developers :
+ Items="Categories?.Developers" ValuesChanged="HandleDeveloperChanged" + AddItem="@(str => new DeveloperDto() { Name = str })" SelectType="SelectType.Single" />
@ResourcesKey.Publishers :
+ Items="Categories?.Publishers" ValuesChanged="HandlePublisherChanged" + AddItem="@(str => new PublisherDto() { Name = str })" SelectType="SelectType.Single" />
diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor.cs index 9685dc5..24ec5e9 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Components/GameCreationForm.razor.cs @@ -75,4 +75,12 @@ public partial class GameCreationForm StateHasChanged(); } } + private void HandlePublisherChanged(List pubs) + { + GameDto.Publisher = pubs.FirstOrDefault(); + } + private void HandleDeveloperChanged(List devs) + { + GameDto.Developer = devs.FirstOrDefault(); + } } \ No newline at end of file diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor index 965d901..995bafd 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Games.razor @@ -1,5 +1,4 @@ @page "/" -@using GameIdeas.BlazorApp.Layouts @using GameIdeas.BlazorApp.Pages.Games.Components @using GameIdeas.BlazorApp.Pages.Games.Filter @using GameIdeas.BlazorApp.Shared.Components diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs index 978d19b..1ed7cfe 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/GameGateway.cs @@ -62,4 +62,18 @@ public class GameGateway(IHttpClientService httpClientService) : IGameGateway throw new GameNotFoundException(ResourcesKey.ErrorFetchGames); } } + + public async Task GetGameById(int gameId) + { + try + { + var result = await httpClientService.FetchDataAsync(Endpoints.Game.FetchById(gameId)); + + return result ?? throw new InvalidOperationException(ResourcesKey.ErrorFetchGames); + } + catch (Exception) + { + throw new CategoryNotFoundException(ResourcesKey.ErrorFetchGames); + } + } } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/IGameGateway.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/IGameGateway.cs index d78155f..9bc4fc4 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/IGameGateway.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Games/Gateways/IGameGateway.cs @@ -8,4 +8,5 @@ public interface IGameGateway Task FetchCategories(); Task CreateGame(GameDetailDto game); Task> FetchGames(GameFilterParams filter, int currentPage); + Task GetGameById(int gameId); } diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor index dbf9d6b..0fbc631 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Pages/Users/Users.razor @@ -1,5 +1,4 @@ @page "/Users" -@using GameIdeas.BlazorApp.Layouts @using GameIdeas.BlazorApp.Pages.Users.Components @using GameIdeas.BlazorApp.Shared.Components.Header @using GameIdeas.BlazorApp.Shared.Components.Popup diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs index 53c7180..c16d19b 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/Shared/Constants/Endpoints.cs @@ -9,6 +9,7 @@ public static class Endpoints { public const string Create = "api/Game/Create"; public static string Fetch(GameFilterDto filter) => $"api/Game?{UrlHelper.BuildUrlParams(filter)}"; + public static string FetchById(int gameId) => $"api/Game/{gameId}"; } public static class Category diff --git a/src/GameIdeas/Client/GameIdeas.BlazorApp/_Imports.razor b/src/GameIdeas/Client/GameIdeas.BlazorApp/_Imports.razor index 587e1a1..55bd956 100644 --- a/src/GameIdeas/Client/GameIdeas.BlazorApp/_Imports.razor +++ b/src/GameIdeas/Client/GameIdeas.BlazorApp/_Imports.razor @@ -7,3 +7,4 @@ @using Microsoft.AspNetCore.Components.WebAssembly.Http @using Microsoft.JSInterop @using GameIdeas.Resources +@using GameIdeas.BlazorApp.Layouts diff --git a/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs b/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs index db3cccb..510b564 100644 --- a/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs +++ b/src/GameIdeas/GameIdeas.Resources/CreateStaticResourceKey.cs @@ -64,6 +64,8 @@ public class Translations (TranslationService translationService) public string Cancel => translationService.Translate(nameof(Cancel)); public string Confirm => translationService.Translate(nameof(Confirm)); public string ConfirmDeleteDescription => translationService.Translate(nameof(ConfirmDeleteDescription)); + public string Informations => translationService.Translate(nameof(Informations)); + public string About => translationService.Translate(nameof(About)); } public static class ResourcesKey @@ -136,4 +138,6 @@ public static class ResourcesKey public static string Cancel => _instance?.Cancel ?? throw new InvalidOperationException("ResourcesKey.Cancel is not initialized."); public static string Confirm => _instance?.Confirm ?? throw new InvalidOperationException("ResourcesKey.Confirm is not initialized."); public static string ConfirmDeleteDescription => _instance?.ConfirmDeleteDescription ?? throw new InvalidOperationException("ResourcesKey.ConfirmDeleteDescription is not initialized."); + public static string Informations => _instance?.Informations ?? throw new InvalidOperationException("ResourcesKey.Informations is not initialized."); + public static string About => _instance?.About ?? throw new InvalidOperationException("ResourcesKey.About is not initialized."); } \ No newline at end of file diff --git a/src/GameIdeas/GameIdeas.Shared/Dto/GameDetailDto.cs b/src/GameIdeas/GameIdeas.Shared/Dto/GameDetailDto.cs index 78e5548..24b6d01 100644 --- a/src/GameIdeas/GameIdeas.Shared/Dto/GameDetailDto.cs +++ b/src/GameIdeas/GameIdeas.Shared/Dto/GameDetailDto.cs @@ -15,6 +15,6 @@ public class GameDetailDto public List? Platforms { get; set; } public List? Properties { get; set; } public List? Tags { get; set; } - public List? Publishers { get; set; } - public List? Developers { get; set; } + public PublisherDto? Publisher { get; set; } + public DeveloperDto? Developer { get; set; } } \ No newline at end of file diff --git a/src/GameIdeas/GameIdeas.Shared/Dto/PlatformDto.cs b/src/GameIdeas/GameIdeas.Shared/Dto/PlatformDto.cs index 472f1cc..2d2a418 100644 --- a/src/GameIdeas/GameIdeas.Shared/Dto/PlatformDto.cs +++ b/src/GameIdeas/GameIdeas.Shared/Dto/PlatformDto.cs @@ -5,4 +5,5 @@ public class PlatformDto public int? Id { get; set; } public string? Label { get; set; } public string? Url { get; set; } + public string? IconUrl { get; set; } } diff --git a/src/GameIdeas/GameIdeas.Shared/Model/Developer.cs b/src/GameIdeas/GameIdeas.Shared/Model/Developer.cs index 97c7fdd..5d70413 100644 --- a/src/GameIdeas/GameIdeas.Shared/Model/Developer.cs +++ b/src/GameIdeas/GameIdeas.Shared/Model/Developer.cs @@ -4,11 +4,11 @@ public partial class Developer { public Developer() { - GameDevelopers = new HashSet(); + Games = new HashSet(); } public int Id { get; set; } public string Name { get; set; } = null!; - public virtual ICollection GameDevelopers { get; set; } + public virtual ICollection Games { get; set; } } diff --git a/src/GameIdeas/GameIdeas.Shared/Model/Game.cs b/src/GameIdeas/GameIdeas.Shared/Model/Game.cs index 680a7cc..fee8508 100644 --- a/src/GameIdeas/GameIdeas.Shared/Model/Game.cs +++ b/src/GameIdeas/GameIdeas.Shared/Model/Game.cs @@ -7,8 +7,6 @@ public partial class Game GamePlatforms = new HashSet(); GameProperties = new HashSet(); GameTags = new HashSet(); - GamePublishers = new HashSet(); - GameDevelopers = new HashSet(); } public int Id { get; set; } @@ -21,14 +19,16 @@ public partial class Game public double? StorageSpace { get; set; } public string? Description { get; set; } public int Interest { get; set; } + public int? PublisherId { get; set; } + public int? DeveloperId { get; set; } public virtual User CreationUser { get; set; } = null!; public virtual User? ModificationUser { get; set; } + public virtual Publisher? Publisher { get; set; } + public virtual Developer? Developer { get; set; } public virtual ICollection GamePlatforms { get; set; } public virtual ICollection GameProperties { get; set; } public virtual ICollection GameTags { get; set; } - public virtual ICollection GamePublishers { get; set; } - public virtual ICollection GameDevelopers { get; set; } } \ No newline at end of file diff --git a/src/GameIdeas/GameIdeas.Shared/Model/GameDeveloper.cs b/src/GameIdeas/GameIdeas.Shared/Model/GameDeveloper.cs deleted file mode 100644 index a4d4c56..0000000 --- a/src/GameIdeas/GameIdeas.Shared/Model/GameDeveloper.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace GameIdeas.Shared.Model; - -public partial class GameDeveloper -{ - public int GameId { get; set; } - public int DeveloperId { get; set; } - - public virtual Game Game { get; set; } = null!; - public virtual Developer Developer { get; set; } = null!; -} diff --git a/src/GameIdeas/GameIdeas.Shared/Model/GamePublisher.cs b/src/GameIdeas/GameIdeas.Shared/Model/GamePublisher.cs deleted file mode 100644 index fa255fd..0000000 --- a/src/GameIdeas/GameIdeas.Shared/Model/GamePublisher.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace GameIdeas.Shared.Model; - -public partial class GamePublisher -{ - public int GameId { get; set; } - public int PublisherId { get; set; } - - public virtual Game Game { get; set; } = null!; - public virtual Publisher Publisher { get; set; } = null!; -} diff --git a/src/GameIdeas/GameIdeas.Shared/Model/Platform.cs b/src/GameIdeas/GameIdeas.Shared/Model/Platform.cs index b1557c9..decde48 100644 --- a/src/GameIdeas/GameIdeas.Shared/Model/Platform.cs +++ b/src/GameIdeas/GameIdeas.Shared/Model/Platform.cs @@ -9,6 +9,7 @@ public partial class Platform public int Id { get; set; } public string Label { get; set; } = null!; + public string? IconUrl { get; set; } public virtual ICollection GamePlatforms { get; set; } diff --git a/src/GameIdeas/GameIdeas.Shared/Model/Publisher.cs b/src/GameIdeas/GameIdeas.Shared/Model/Publisher.cs index 1bf0c9c..bae4c71 100644 --- a/src/GameIdeas/GameIdeas.Shared/Model/Publisher.cs +++ b/src/GameIdeas/GameIdeas.Shared/Model/Publisher.cs @@ -4,11 +4,11 @@ public partial class Publisher { public Publisher() { - GamePublishers = new HashSet(); + Games = new HashSet(); } public int Id { get; set; } public string Name { get; set; } = null!; - public virtual ICollection GamePublishers { get; set; } + public virtual ICollection Games { get; set; } } diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Context/GameIdeasContext.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Context/GameIdeasContext.cs index 836b915..ece2f8c 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Context/GameIdeasContext.cs +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Context/GameIdeasContext.cs @@ -19,10 +19,8 @@ public class GameIdeasContext : IdentityDbContext public virtual DbSet Publishers { get; set; } = null!; public virtual DbSet Tags { get; set; } = null!; public virtual DbSet Games { get; set; } = null!; - public virtual DbSet GameDevelopers { get; set; } = null!; public virtual DbSet GamePlatforms { get; set; } = null!; public virtual DbSet GameProperties { get; set; } = null!; - public virtual DbSet GamePublishers { get; set; } = null!; public virtual DbSet GameTags { get; set; } = null!; protected override void OnModelCreating(ModelBuilder modelBuilder) @@ -76,6 +74,10 @@ public class GameIdeasContext : IdentityDbContext entity.HasIndex(e => e.ModificationUserId); + entity.HasIndex(e => e.PublisherId); + + entity.HasIndex(e => e.DeveloperId); + entity.Property(e => e.CreationDate) .HasDefaultValueSql("now()"); @@ -92,23 +94,16 @@ public class GameIdeasContext : IdentityDbContext .WithMany(p => p.ModificationGames) .HasForeignKey(d => d.ModificationUserId) .OnDelete(DeleteBehavior.ClientSetNull); - }); - - modelBuilder.Entity(entity => - { - entity.ToTable("GameDeveloper"); - - entity.HasKey(e => new { e.GameId, e.DeveloperId }); - - entity.HasOne(d => d.Game) - .WithMany(p => p.GameDevelopers) - .HasForeignKey(d => d.GameId) - .OnDelete(DeleteBehavior.Cascade); entity.HasOne(d => d.Developer) - .WithMany(p => p.GameDevelopers) + .WithMany(p => p.Games) .HasForeignKey(d => d.DeveloperId) - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.ClientSetNull); + + entity.HasOne(d => d.Publisher) + .WithMany(p => p.Games) + .HasForeignKey(d => d.PublisherId) + .OnDelete(DeleteBehavior.ClientSetNull); }); modelBuilder.Entity(entity => @@ -145,23 +140,6 @@ public class GameIdeasContext : IdentityDbContext .OnDelete(DeleteBehavior.Cascade); }); - modelBuilder.Entity(entity => - { - entity.ToTable("GamePublisher"); - - entity.HasKey(e => new { e.GameId, e.PublisherId }); - - entity.HasOne(d => d.Game) - .WithMany(p => p.GamePublishers) - .HasForeignKey(d => d.GameId) - .OnDelete(DeleteBehavior.Cascade); - - entity.HasOne(d => d.Publisher) - .WithMany(p => p.GamePublishers) - .HasForeignKey(d => d.PublisherId) - .OnDelete(DeleteBehavior.Cascade); - }); - modelBuilder.Entity(entity => { entity.ToTable("GameTag"); diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json b/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json index 43a1047..91cd16e 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Files/GameIdeas.fr.json @@ -59,5 +59,7 @@ "ErrorDeleteUser": "Erreur lors de la suppression d'un utilisateur", "Cancel": "Annuler", "Confirm": "Confirmer", - "ConfirmDeleteDescription": "Êtes-vous sur de vouloir supprimer cet élément ?" + "ConfirmDeleteDescription": "Êtes-vous sur de vouloir supprimer cet élément ?", + "Informations": "Informations", + "About": "À propos" } \ No newline at end of file diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Migrations/20250502200035_One-Many-Categories.Designer.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Migrations/20250502200035_One-Many-Categories.Designer.cs new file mode 100644 index 0000000..a61a96a --- /dev/null +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Migrations/20250502200035_One-Many-Categories.Designer.cs @@ -0,0 +1,616 @@ +// +using System; +using GameIdeas.WebAPI.Context; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace GameIdeas.WebAPI.Migrations +{ + [DbContext(typeof(GameIdeasContext))] + [Migration("20250502200035_One-Many-Categories")] + partial class OneManyCategories + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("GameIdeas.Shared.Model.Developer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Developer", (string)null); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.Game", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + NpgsqlPropertyBuilderExtensions.HasIdentityOptions(b.Property("Id"), 100000L, null, null, null, null, null); + + b.Property("CreationDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp without time zone") + .HasDefaultValueSql("now()"); + + b.Property("CreationUserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("Description") + .HasColumnType("text"); + + b.Property("DeveloperId") + .HasColumnType("integer"); + + b.Property("Interest") + .HasColumnType("integer"); + + b.Property("ModificationDate") + .HasColumnType("timestamp without time zone"); + + b.Property("ModificationUserId") + .HasColumnType("text"); + + b.Property("PublisherId") + .HasColumnType("integer"); + + b.Property("ReleaseDate") + .HasColumnType("timestamp without time zone"); + + b.Property("StorageSpace") + .HasColumnType("double precision"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CreationUserId"); + + b.HasIndex("DeveloperId"); + + b.HasIndex("ModificationUserId"); + + b.HasIndex("PublisherId"); + + b.HasIndex("Title") + .IsUnique(); + + b.ToTable("Game", (string)null); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.GamePlatform", b => + { + b.Property("GameId") + .HasColumnType("integer"); + + b.Property("PlatformId") + .HasColumnType("integer"); + + b.Property("Url") + .HasColumnType("text"); + + b.HasKey("GameId", "PlatformId"); + + b.HasIndex("PlatformId"); + + b.ToTable("GamePlatform", (string)null); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.GameProperty", b => + { + b.Property("GameId") + .HasColumnType("integer"); + + b.Property("PropertyId") + .HasColumnType("integer"); + + b.HasKey("GameId", "PropertyId"); + + b.HasIndex("PropertyId"); + + b.ToTable("GameProperty", (string)null); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.GameTag", b => + { + b.Property("GameId") + .HasColumnType("integer"); + + b.Property("TagId") + .HasColumnType("integer"); + + b.HasKey("GameId", "TagId"); + + b.HasIndex("TagId"); + + b.ToTable("GameTag", (string)null); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.Platform", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IconUrl") + .HasColumnType("text"); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Label") + .IsUnique(); + + b.ToTable("Platform", (string)null); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.Property", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Label") + .IsUnique(); + + b.ToTable("Property", (string)null); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.Publisher", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Publisher", (string)null); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.Tag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Label") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Label") + .IsUnique(); + + b.ToTable("Tag", (string)null); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.User", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.Game", b => + { + b.HasOne("GameIdeas.Shared.Model.User", "CreationUser") + .WithMany("CreationGames") + .HasForeignKey("CreationUserId") + .IsRequired(); + + b.HasOne("GameIdeas.Shared.Model.Developer", "Developer") + .WithMany("Games") + .HasForeignKey("DeveloperId"); + + b.HasOne("GameIdeas.Shared.Model.User", "ModificationUser") + .WithMany("ModificationGames") + .HasForeignKey("ModificationUserId"); + + b.HasOne("GameIdeas.Shared.Model.Publisher", "Publisher") + .WithMany("Games") + .HasForeignKey("PublisherId"); + + b.Navigation("CreationUser"); + + b.Navigation("Developer"); + + b.Navigation("ModificationUser"); + + b.Navigation("Publisher"); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.GamePlatform", b => + { + b.HasOne("GameIdeas.Shared.Model.Game", "Game") + .WithMany("GamePlatforms") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GameIdeas.Shared.Model.Platform", "Platform") + .WithMany("GamePlatforms") + .HasForeignKey("PlatformId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Game"); + + b.Navigation("Platform"); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.GameProperty", b => + { + b.HasOne("GameIdeas.Shared.Model.Game", "Game") + .WithMany("GameProperties") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GameIdeas.Shared.Model.Property", "Property") + .WithMany("GameProperties") + .HasForeignKey("PropertyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Game"); + + b.Navigation("Property"); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.GameTag", b => + { + b.HasOne("GameIdeas.Shared.Model.Game", "Game") + .WithMany("GameTags") + .HasForeignKey("GameId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GameIdeas.Shared.Model.Tag", "Tag") + .WithMany("GameTags") + .HasForeignKey("TagId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Game"); + + b.Navigation("Tag"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("GameIdeas.Shared.Model.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("GameIdeas.Shared.Model.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("GameIdeas.Shared.Model.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("GameIdeas.Shared.Model.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.Developer", b => + { + b.Navigation("Games"); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.Game", b => + { + b.Navigation("GamePlatforms"); + + b.Navigation("GameProperties"); + + b.Navigation("GameTags"); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.Platform", b => + { + b.Navigation("GamePlatforms"); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.Property", b => + { + b.Navigation("GameProperties"); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.Publisher", b => + { + b.Navigation("Games"); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.Tag", b => + { + b.Navigation("GameTags"); + }); + + modelBuilder.Entity("GameIdeas.Shared.Model.User", b => + { + b.Navigation("CreationGames"); + + b.Navigation("ModificationGames"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Migrations/20250502200035_One-Many-Categories.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Migrations/20250502200035_One-Many-Categories.cs new file mode 100644 index 0000000..14f15c8 --- /dev/null +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Migrations/20250502200035_One-Many-Categories.cs @@ -0,0 +1,152 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace GameIdeas.WebAPI.Migrations +{ + /// + public partial class OneManyCategories : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "GameDeveloper"); + + migrationBuilder.DropTable( + name: "GamePublisher"); + + migrationBuilder.AddColumn( + name: "IconUrl", + table: "Platform", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "DeveloperId", + table: "Game", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn( + name: "PublisherId", + table: "Game", + type: "integer", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_Game_DeveloperId", + table: "Game", + column: "DeveloperId"); + + migrationBuilder.CreateIndex( + name: "IX_Game_PublisherId", + table: "Game", + column: "PublisherId"); + + migrationBuilder.AddForeignKey( + name: "FK_Game_Developer_DeveloperId", + table: "Game", + column: "DeveloperId", + principalTable: "Developer", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_Game_Publisher_PublisherId", + table: "Game", + column: "PublisherId", + principalTable: "Publisher", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Game_Developer_DeveloperId", + table: "Game"); + + migrationBuilder.DropForeignKey( + name: "FK_Game_Publisher_PublisherId", + table: "Game"); + + migrationBuilder.DropIndex( + name: "IX_Game_DeveloperId", + table: "Game"); + + migrationBuilder.DropIndex( + name: "IX_Game_PublisherId", + table: "Game"); + + migrationBuilder.DropColumn( + name: "IconUrl", + table: "Platform"); + + migrationBuilder.DropColumn( + name: "DeveloperId", + table: "Game"); + + migrationBuilder.DropColumn( + name: "PublisherId", + table: "Game"); + + migrationBuilder.CreateTable( + name: "GameDeveloper", + columns: table => new + { + GameId = table.Column(type: "integer", nullable: false), + DeveloperId = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_GameDeveloper", x => new { x.GameId, x.DeveloperId }); + table.ForeignKey( + name: "FK_GameDeveloper_Developer_DeveloperId", + column: x => x.DeveloperId, + principalTable: "Developer", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_GameDeveloper_Game_GameId", + column: x => x.GameId, + principalTable: "Game", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "GamePublisher", + columns: table => new + { + GameId = table.Column(type: "integer", nullable: false), + PublisherId = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_GamePublisher", x => new { x.GameId, x.PublisherId }); + table.ForeignKey( + name: "FK_GamePublisher_Game_GameId", + column: x => x.GameId, + principalTable: "Game", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_GamePublisher_Publisher_PublisherId", + column: x => x.PublisherId, + principalTable: "Publisher", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_GameDeveloper_DeveloperId", + table: "GameDeveloper", + column: "DeveloperId"); + + migrationBuilder.CreateIndex( + name: "IX_GamePublisher_PublisherId", + table: "GamePublisher", + column: "PublisherId"); + } + } +} diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Migrations/GameIdeasContextModelSnapshot.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Migrations/GameIdeasContextModelSnapshot.cs index bab569b..951f3c5 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Migrations/GameIdeasContextModelSnapshot.cs +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Migrations/GameIdeasContextModelSnapshot.cs @@ -63,6 +63,9 @@ namespace GameIdeas.WebAPI.Migrations b.Property("Description") .HasColumnType("text"); + b.Property("DeveloperId") + .HasColumnType("integer"); + b.Property("Interest") .HasColumnType("integer"); @@ -72,6 +75,9 @@ namespace GameIdeas.WebAPI.Migrations b.Property("ModificationUserId") .HasColumnType("text"); + b.Property("PublisherId") + .HasColumnType("integer"); + b.Property("ReleaseDate") .HasColumnType("timestamp without time zone"); @@ -86,29 +92,18 @@ namespace GameIdeas.WebAPI.Migrations b.HasIndex("CreationUserId"); + b.HasIndex("DeveloperId"); + b.HasIndex("ModificationUserId"); + b.HasIndex("PublisherId"); + b.HasIndex("Title") .IsUnique(); b.ToTable("Game", (string)null); }); - modelBuilder.Entity("GameIdeas.Shared.Model.GameDeveloper", b => - { - b.Property("GameId") - .HasColumnType("integer"); - - b.Property("DeveloperId") - .HasColumnType("integer"); - - b.HasKey("GameId", "DeveloperId"); - - b.HasIndex("DeveloperId"); - - b.ToTable("GameDeveloper", (string)null); - }); - modelBuilder.Entity("GameIdeas.Shared.Model.GamePlatform", b => { b.Property("GameId") @@ -142,21 +137,6 @@ namespace GameIdeas.WebAPI.Migrations b.ToTable("GameProperty", (string)null); }); - modelBuilder.Entity("GameIdeas.Shared.Model.GamePublisher", b => - { - b.Property("GameId") - .HasColumnType("integer"); - - b.Property("PublisherId") - .HasColumnType("integer"); - - b.HasKey("GameId", "PublisherId"); - - b.HasIndex("PublisherId"); - - b.ToTable("GamePublisher", (string)null); - }); - modelBuilder.Entity("GameIdeas.Shared.Model.GameTag", b => { b.Property("GameId") @@ -180,6 +160,9 @@ namespace GameIdeas.WebAPI.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property("IconUrl") + .HasColumnType("text"); + b.Property("Label") .IsRequired() .HasColumnType("text"); @@ -455,32 +438,25 @@ namespace GameIdeas.WebAPI.Migrations .HasForeignKey("CreationUserId") .IsRequired(); + b.HasOne("GameIdeas.Shared.Model.Developer", "Developer") + .WithMany("Games") + .HasForeignKey("DeveloperId"); + b.HasOne("GameIdeas.Shared.Model.User", "ModificationUser") .WithMany("ModificationGames") .HasForeignKey("ModificationUserId"); + b.HasOne("GameIdeas.Shared.Model.Publisher", "Publisher") + .WithMany("Games") + .HasForeignKey("PublisherId"); + b.Navigation("CreationUser"); - b.Navigation("ModificationUser"); - }); - - modelBuilder.Entity("GameIdeas.Shared.Model.GameDeveloper", b => - { - b.HasOne("GameIdeas.Shared.Model.Developer", "Developer") - .WithMany("GameDevelopers") - .HasForeignKey("DeveloperId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("GameIdeas.Shared.Model.Game", "Game") - .WithMany("GameDevelopers") - .HasForeignKey("GameId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - b.Navigation("Developer"); - b.Navigation("Game"); + b.Navigation("ModificationUser"); + + b.Navigation("Publisher"); }); modelBuilder.Entity("GameIdeas.Shared.Model.GamePlatform", b => @@ -521,25 +497,6 @@ namespace GameIdeas.WebAPI.Migrations b.Navigation("Property"); }); - modelBuilder.Entity("GameIdeas.Shared.Model.GamePublisher", b => - { - b.HasOne("GameIdeas.Shared.Model.Game", "Game") - .WithMany("GamePublishers") - .HasForeignKey("GameId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("GameIdeas.Shared.Model.Publisher", "Publisher") - .WithMany("GamePublishers") - .HasForeignKey("PublisherId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Game"); - - b.Navigation("Publisher"); - }); - modelBuilder.Entity("GameIdeas.Shared.Model.GameTag", b => { b.HasOne("GameIdeas.Shared.Model.Game", "Game") @@ -612,19 +569,15 @@ namespace GameIdeas.WebAPI.Migrations modelBuilder.Entity("GameIdeas.Shared.Model.Developer", b => { - b.Navigation("GameDevelopers"); + b.Navigation("Games"); }); modelBuilder.Entity("GameIdeas.Shared.Model.Game", b => { - b.Navigation("GameDevelopers"); - b.Navigation("GamePlatforms"); b.Navigation("GameProperties"); - b.Navigation("GamePublishers"); - b.Navigation("GameTags"); }); @@ -640,7 +593,7 @@ namespace GameIdeas.WebAPI.Migrations modelBuilder.Entity("GameIdeas.Shared.Model.Publisher", b => { - b.Navigation("GamePublishers"); + b.Navigation("Games"); }); modelBuilder.Entity("GameIdeas.Shared.Model.Tag", b => diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Profiles/CategoryProfile.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Profiles/CategoryProfile.cs index 5ad0516..e4bf5da 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Profiles/CategoryProfile.cs +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Profiles/CategoryProfile.cs @@ -21,11 +21,6 @@ public class CategoryProfile : Profile .ForMember(d => d.Id, o => o.MapFrom(s => s.Id)) .ForMember(d => d.Name, o => o.MapFrom(s => s.Name)) .ReverseMap(); - - CreateMap() - .ForMember(d => d.PublisherId, o => o.MapFrom(s => s.Id)) - .ForPath(d => d.Publisher.Id, o => o.MapFrom(s => s.Id)) - .ForPath(d => d.Publisher.Name, o => o.MapFrom(s => s.Name)); } private void CreateDeveloperMap() @@ -34,11 +29,6 @@ public class CategoryProfile : Profile .ForMember(d => d.Id, o => o.MapFrom(s => s.Id)) .ForMember(d => d.Name, o => o.MapFrom(s => s.Name)) .ReverseMap(); - - CreateMap() - .ForMember(d => d.DeveloperId, o => o.MapFrom(s => s.Id)) - .ForPath(d => d.Developer.Id, o => o.MapFrom(s => s.Id)) - .ForPath(d => d.Developer.Name, o => o.MapFrom(s => s.Name)); } private void CreateTagMap() diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Profiles/GameProfile.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Profiles/GameProfile.cs index e0e5d73..0621bf6 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Profiles/GameProfile.cs +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Profiles/GameProfile.cs @@ -19,18 +19,20 @@ public class GameProfile : Profile .ForMember(d => d.StorageSpace, o => o.MapFrom(s => s.StorageSpace)) .ForMember(d => d.Description, o => o.MapFrom(s => s.Description)) .ForMember(d => d.Interest, o => o.MapFrom(s => s.Interest)) - .ForMember(d => d.Platforms, o => o.MapFrom(s => s.GamePlatforms.Select(p => new PlatformDto() { Id = p.Platform.Id, Label = p.Platform.Label, Url = p.Url }))) + .ForMember(d => d.Platforms, o => o.MapFrom(s => s.GamePlatforms.Select(p => new PlatformDto() { Id = p.Platform.Id, Label = p.Platform.Label, Url = p.Url, IconUrl = p.Platform.IconUrl }))) .ForMember(d => d.Properties, o => o.MapFrom(s => s.GameProperties.Select(p => p.Property))) .ForMember(d => d.Tags, o => o.MapFrom(s => s.GameTags.Select(t => t.Tag))) - .ForMember(d => d.Publishers, o => o.MapFrom(s => s.GamePublishers.Select(p => p.Publisher))) - .ForMember(d => d.Developers, o => o.MapFrom(s => s.GameDevelopers.Select(gd => gd.Developer))) + .ForMember(d => d.Publisher, o => o.MapFrom(s => s.Publisher)) + .ForMember(d => d.Publisher!.Id, o => o.MapFrom(s => s.PublisherId)) + .ForMember(d => d.Developer, o => o.MapFrom(s => s.Developer)) + .ForMember(d => d.Developer!.Id, o => o.MapFrom(s => s.DeveloperId)) .ReverseMap(); CreateMap() .ForMember(d => d.Id, o => o.MapFrom(s => s.Id)) .ForMember(d => d.Title, o => o.MapFrom(s => s.Title)) .ForMember(d => d.ReleaseDate, o => o.MapFrom(s => s.ReleaseDate)) - .ForMember(d => d.Platforms, o => o.MapFrom(s => s.GamePlatforms.Select(p => new PlatformDto() { Id = p.Platform.Id, Label = p.Platform.Label, Url = p.Url }))) + .ForMember(d => d.Platforms, o => o.MapFrom(s => s.GamePlatforms.Select(p => new PlatformDto() { Id = p.Platform.Id, Label = p.Platform.Label, Url = p.Url, IconUrl = p.Platform.IconUrl }))) .ForMember(d => d.Tags, o => o.MapFrom(s => s.GameTags.Select(t => t.Tag))) .ForMember(d => d.StorageSpace, o => o.MapFrom(s => s.StorageSpace)) .ForMember(d => d.Interest, o => o.MapFrom(s => s.Interest)) diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Games/GameReadService.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Games/GameReadService.cs index 6b2a3b2..aefaf0d 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Games/GameReadService.cs +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Games/GameReadService.cs @@ -18,8 +18,8 @@ public class GameReadService(GameIdeasContext context, IMapper mapper, ICategory .Include(g => g.GamePlatforms).ThenInclude(gp => gp.Platform) .Include(g => g.GameProperties) .Include(g => g.GameTags).ThenInclude(gt => gt.Tag) - .Include(g => g.GamePublishers) - .Include(g => g.GameDevelopers) + .Include(g => g.Publisher) + .Include(g => g.Developer) .AsQueryable(); ApplyFilter(ref query, filter); @@ -43,8 +43,8 @@ public class GameReadService(GameIdeasContext context, IMapper mapper, ICategory .Include(g => g.GamePlatforms).ThenInclude(p => p.Platform) .Include(g => g.GameProperties).ThenInclude(p => p.Property) .Include(g => g.GameTags).ThenInclude(p => p.Tag) - .Include(g => g.GamePublishers).ThenInclude(p => p.Publisher) - .Include(g => g.GameDevelopers).ThenInclude(p => p.Developer) + .Include(g => g.Publisher) + .Include(g => g.Developer) .FirstOrDefaultAsync(g => g.Id == gameId); return game == null @@ -94,14 +94,14 @@ public class GameReadService(GameIdeasContext context, IMapper mapper, ICategory if (filter.PublisherIds != null) { - query = query.Where(game => filter.PublisherIds.All(pub => - game.GamePublishers.Any(gp => gp.PublisherId == pub))); + query = query.Where(game => game.Publisher != null && + filter.PublisherIds.Contains(game.Publisher!.Id)); } if (filter.DeveloperIds != null) { - query = query.Where(game => filter.DeveloperIds.All(dev => - game.GameDevelopers.Any(gd => gd.DeveloperId == dev))); + query = query.Where(game => game.Developer != null && + filter.DeveloperIds.Contains(game.Developer.Id)); } if (filter.MinInterest != null) diff --git a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Games/GameWriteService.cs b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Games/GameWriteService.cs index b1b05b1..d5c0166 100644 --- a/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Games/GameWriteService.cs +++ b/src/GameIdeas/Server/GameIdeas.WebAPI/Services/Games/GameWriteService.cs @@ -14,14 +14,15 @@ public class GameWriteService(GameIdeasContext context, IMapper mapper) : IGameW { var gameToCreate = mapper.Map(gameDto); + await HandlePublisherCreation(gameDto.Publisher); + await HandleDeveloperCreation(gameDto.Developer); + await context.Games.AddAsync(gameToCreate); await context.SaveChangesAsync(); await HandlePlatformsCreation(gameDto.Platforms, gameToCreate.Id); await HandlePropertiesCreation(gameDto.Properties, gameToCreate.Id); await HandleTagsCreation(gameDto.Tags, gameToCreate.Id); - await HandlePublishersCreation(gameDto.Publishers, gameToCreate.Id); - await HandleDevelopersCreation(gameDto.Developers, gameToCreate.Id); await context.SaveChangesAsync(); @@ -37,11 +38,12 @@ public class GameWriteService(GameIdeasContext context, IMapper mapper) : IGameW var gameToUpdate = mapper.Map(gameDto); + await HandlePublisherCreation(gameDto.Publisher); + await HandleDeveloperCreation(gameDto.Developer); + await HandlePlatformsCreation(gameDto.Platforms, gameToUpdate.Id); await HandlePropertiesCreation(gameDto.Properties, gameToUpdate.Id); await HandleTagsCreation(gameDto.Tags, gameToUpdate.Id); - await HandlePublishersCreation(gameDto.Publishers, gameToUpdate.Id); - await HandleDevelopersCreation(gameDto.Developers, gameToUpdate.Id); context.Games.Update(gameToUpdate); await context.SaveChangesAsync(); @@ -107,35 +109,23 @@ public class GameWriteService(GameIdeasContext context, IMapper mapper) : IGameW } } - private async Task HandlePublishersCreation(IEnumerable? categoriesToCreate, int gameId) + private async Task HandlePublisherCreation(PublisherDto? categoriesToCreate) { if (categoriesToCreate != null) { - var gps = mapper.Map>(categoriesToCreate); - - foreach (var gp in gps) - { - gp.GameId = gameId; - } - - context.Publishers.AttachRange(gps.Select(gp => gp.Publisher)); - await context.GamePublishers.AddRangeAsync(gps); + var pub = mapper.Map(categoriesToCreate); + context.Publishers.Attach(pub); + await context.Publishers.AddAsync(pub); } } - private async Task HandleDevelopersCreation(IEnumerable? categoriesToCreate, int gameId) + private async Task HandleDeveloperCreation(DeveloperDto? categoriesToCreate) { if (categoriesToCreate != null) { - var gds = mapper.Map>(categoriesToCreate); - - foreach (var gd in gds) - { - gd.GameId = gameId; - } - - context.Developers.AttachRange(gds.Select(gd => gd.Developer)); - await context.GameDevelopers.AddRangeAsync(gds); + var dev = mapper.Map(categoriesToCreate); + context.Developers.Attach(dev); + await context.Developers.AddAsync(dev); } } }