From ad7bfeaa4077fe14ad41f48765a7f6baf5588cd9 Mon Sep 17 00:00:00 2001 From: "Tizian.Breuch" Date: Thu, 31 Jul 2025 15:33:35 +0200 Subject: [PATCH] categorys --- .../DTOs/Products/AdminProductDto.cs | 1 + .../DTOs/Products/ProductDto.cs | 2 + .../Services/Admin/AdminProductService.cs | 72 +++++++++++++------ .../Services/Public/ProductService.cs | 67 +++++++++++------ 4 files changed, 97 insertions(+), 45 deletions(-) diff --git a/Webshop.Application/DTOs/Products/AdminProductDto.cs b/Webshop.Application/DTOs/Products/AdminProductDto.cs index 7d4e8d5..f7f79bd 100644 --- a/Webshop.Application/DTOs/Products/AdminProductDto.cs +++ b/Webshop.Application/DTOs/Products/AdminProductDto.cs @@ -24,5 +24,6 @@ namespace Webshop.Application.DTOs.Products public DateTimeOffset? LastModifiedDate { get; set; } public Guid? SupplierId { get; set; } public decimal? PurchasePrice { get; set; } + public List CategoryIds { get; set; } = new List(); } } diff --git a/Webshop.Application/DTOs/Products/ProductDto.cs b/Webshop.Application/DTOs/Products/ProductDto.cs index 82af6e2..2923492 100644 --- a/Webshop.Application/DTOs/Products/ProductDto.cs +++ b/Webshop.Application/DTOs/Products/ProductDto.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using Webshop.Application.DTOs.Categorys; namespace Webshop.Application.DTOs.Products @@ -18,5 +19,6 @@ namespace Webshop.Application.DTOs.Products public int StockQuantity { get; set; } public string? ImageUrl { get; set; } public string Slug { get; set; } = string.Empty; + public List Categories { get; set; } = new List(); } } diff --git a/Webshop.Application/Services/Admin/AdminProductService.cs b/Webshop.Application/Services/Admin/AdminProductService.cs index e5a21e3..29cf028 100644 --- a/Webshop.Application/Services/Admin/AdminProductService.cs +++ b/Webshop.Application/Services/Admin/AdminProductService.cs @@ -1,28 +1,35 @@ // src/Webshop.Application/Services/Admin/AdminProductService.cs +using Microsoft.EntityFrameworkCore; // << NEU: Für Include() und FirstOrDefaultAsync() >> using Webshop.Domain.Entities; using Webshop.Domain.Interfaces; using System.Collections.Generic; using System.Threading.Tasks; -using System; // Für Guid +using System; using System.Linq; -using Webshop.Application.DTOs.Products; -using Webshop.Application.Services.Admin.Interfaces; // Für Select - +using Webshop.Application.DTOs.Products; // Für AdminProductDto +using Webshop.Application.Services.Admin.Interfaces; // Für IAdminProductService +using Webshop.Infrastructure.Data; // << NEU: Für ApplicationDbContext >> namespace Webshop.Application.Services.Admin { - public class AdminProductService : IAdminProductService // Sicherstellen, dass IAdminProductService implementiert wird + public class AdminProductService : IAdminProductService { private readonly IProductRepository _productRepository; + private readonly ApplicationDbContext _context; // << NEU: Für direkten DB-Zugriff auf Join-Tabellen >> - public AdminProductService(IProductRepository productRepository) + public AdminProductService(IProductRepository productRepository, ApplicationDbContext context) // << NEU: DbContext injizieren >> { _productRepository = productRepository; + _context = context; // << NEU >> } public async Task> GetAllAdminProductsAsync() { - var products = await _productRepository.GetAllProductsAsync(); + // Wir verwenden den DbContext, um auch die Kategorien effizient mitzuladen + var products = await _context.Products + .Include(p => p.ProductCategories) + .ToListAsync(); + return products.Select(p => new AdminProductDto { Id = p.Id, @@ -40,13 +47,17 @@ namespace Webshop.Application.Services.Admin CreatedDate = p.CreatedDate, LastModifiedDate = p.LastModifiedDate, SupplierId = p.SupplierId, - PurchasePrice = p.PurchasePrice + PurchasePrice = p.PurchasePrice, + CategoryIds = p.ProductCategories.Select(pc => pc.CategoryId).ToList() // << NEU >> }).ToList(); } public async Task GetAdminProductByIdAsync(Guid id) { - var product = await _productRepository.GetProductByIdAsync(id); + var product = await _context.Products + .Include(p => p.ProductCategories) // << NEU: Lade die Join-Tabelle mit >> + .FirstOrDefaultAsync(p => p.Id == id); + if (product == null) return null; return new AdminProductDto @@ -66,16 +77,16 @@ namespace Webshop.Application.Services.Admin CreatedDate = product.CreatedDate, LastModifiedDate = product.LastModifiedDate, SupplierId = product.SupplierId, - PurchasePrice = product.PurchasePrice + PurchasePrice = product.PurchasePrice, + CategoryIds = product.ProductCategories.Select(pc => pc.CategoryId).ToList() // << NEU: Mappe die CategoryIds >> }; } public async Task CreateAdminProductAsync(AdminProductDto productDto) { - // Konvertiere DTO zu Domain-Entität var newProduct = new Product { - Id = Guid.NewGuid(), // API generiert die ID immer neu + Id = Guid.NewGuid(), Name = productDto.Name, Description = productDto.Description, SKU = productDto.SKU, @@ -87,26 +98,33 @@ namespace Webshop.Application.Services.Admin Weight = productDto.Weight, ImageUrl = productDto.ImageUrl, Slug = productDto.Slug, - CreatedDate = DateTimeOffset.UtcNow, // Server setzt Erstellungsdatum - LastModifiedDate = null, // Neues Produkt, noch nicht modifiziert + CreatedDate = DateTimeOffset.UtcNow, SupplierId = productDto.SupplierId, - PurchasePrice = productDto.PurchasePrice + PurchasePrice = productDto.PurchasePrice, + ProductCategories = new List() // Initialisiere die Collection }; - await _productRepository.AddProductAsync(newProduct); // Fügt in DB ein + // << NEU: Füge die Kategorien hinzu >> + foreach (var categoryId in productDto.CategoryIds) + { + newProduct.ProductCategories.Add(new ProductCategory { CategoryId = categoryId }); + } + + await _productRepository.AddProductAsync(newProduct); // << KORREKT: VERWENDET AddProductAsync >> - // Aktualisiere DTO mit der neuen ID und gib es zurück productDto.Id = newProduct.Id; - productDto.CreatedDate = newProduct.CreatedDate; return productDto; } public async Task UpdateAdminProductAsync(AdminProductDto productDto) { - var existingProduct = await _productRepository.GetProductByIdAsync(productDto.Id); + var existingProduct = await _context.Products + .Include(p => p.ProductCategories) // Lade die aktuellen Zuweisungen + .FirstOrDefaultAsync(p => p.Id == productDto.Id); + if (existingProduct == null) return false; - // Aktualisiere Eigenschaften des bestehenden Produkts + // Aktualisiere die direkten Eigenschaften des Produkts existingProduct.Name = productDto.Name; existingProduct.Description = productDto.Description; existingProduct.SKU = productDto.SKU; @@ -118,11 +136,19 @@ namespace Webshop.Application.Services.Admin existingProduct.Weight = productDto.Weight; existingProduct.ImageUrl = productDto.ImageUrl; existingProduct.Slug = productDto.Slug; - existingProduct.LastModifiedDate = DateTimeOffset.UtcNow; // Server setzt Modifikationsdatum existingProduct.SupplierId = productDto.SupplierId; existingProduct.PurchasePrice = productDto.PurchasePrice; + existingProduct.LastModifiedDate = DateTimeOffset.UtcNow; - await _productRepository.UpdateProductAsync(existingProduct); + // << NEU: Kategorien synchronisieren (alte löschen, neue hinzufügen) >> + existingProduct.ProductCategories.Clear(); + foreach (var categoryId in productDto.CategoryIds) + { + existingProduct.ProductCategories.Add(new ProductCategory { ProductId = existingProduct.Id, CategoryId = categoryId }); + } + // << ENDE NEUER TEIL >> + + await _productRepository.UpdateProductAsync(existingProduct); // << KORREKT: VERWENDET UpdateProductAsync >> return true; } @@ -131,7 +157,7 @@ namespace Webshop.Application.Services.Admin var product = await _productRepository.GetProductByIdAsync(id); if (product == null) return false; - await _productRepository.DeleteProductAsync(id); + await _productRepository.DeleteProductAsync(id); // << KORREKT: VERWENDET DeleteProductAsync >> return true; } } diff --git a/Webshop.Application/Services/Public/ProductService.cs b/Webshop.Application/Services/Public/ProductService.cs index aa27961..283f10d 100644 --- a/Webshop.Application/Services/Public/ProductService.cs +++ b/Webshop.Application/Services/Public/ProductService.cs @@ -1,45 +1,62 @@ +// src/Webshop.Application/Services/Public/ProductService.cs +using Microsoft.EntityFrameworkCore; // << NEU: Für Include() und ThenInclude() >> using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Webshop.Application.DTOs.Products; -using Webshop.Application.Services.Public.Interfaces; -using Webshop.Domain.Interfaces; +using Webshop.Application.DTOs.Categorys; // Für CategoryDto +using Webshop.Application.DTOs.Products; // Für ProductDto +using Webshop.Application.Services.Public.Interfaces; // Für IProductService +using Webshop.Domain.Interfaces; // Für IProductRepository +using Webshop.Infrastructure.Data; // << NEU: Für ApplicationDbContext >> namespace Webshop.Application.Services.Public { public class ProductService : IProductService { private readonly IProductRepository _productRepository; + private readonly ApplicationDbContext _context; // << NEU: Für direkten DB-Zugriff >> - public ProductService(IProductRepository productRepository) + public ProductService(IProductRepository productRepository, ApplicationDbContext context) // << NEU: DbContext injizieren >> { _productRepository = productRepository; + _context = context; // << NEU >> } - // Wir verwenden jetzt den von Ihnen vorgegebenen Namen public async Task> GetAllProductsAsync() { - var products = await _productRepository.GetAllProductsAsync(); + // Wir verwenden den DbContext, um Produkte und ihre Kategorien zu laden + var products = await _context.Products + .Include(p => p.ProductCategories) // Lade die Join-Tabelle + .ThenInclude(pc => pc.Category) // Lade die zugehörige Kategorie-Entität + .Where(p => p.IsActive) // Nur aktive Produkte + .ToListAsync(); - return products - .Where(p => p.IsActive) // Nur aktive Produkte anzeigen - .Select(p => new ProductDto + return products.Select(p => new ProductDto + { + Id = p.Id, + Name = p.Name, + Description = p.ShortDescription, // Oder p.Description, je nach Anforderung + Price = p.Price, + SKU = p.SKU, + ImageUrl = p.ImageUrl, + IsInStock = p.IsInStock, + Slug = p.Slug, + Categories = p.ProductCategories.Select(pc => new CategoryDto { - Id = p.Id, - Name = p.Name, - Description = p.ShortDescription, - Price = p.Price, - SKU = p.SKU, - ImageUrl = p.ImageUrl, - IsInStock = p.IsInStock, - Slug = p.Slug // Mapping für Slug - }).ToList(); + Id = pc.Category.Id, + Name = pc.Category.Name, + Slug = pc.Category.Slug + // ... weitere CategoryDto-Felder bei Bedarf + }).ToList() + }).ToList(); } - // Diese Methode wird hinzugefügt, um das Interface zu erfüllen public async Task GetProductBySlugAsync(string slug) { - var product = await _productRepository.GetBySlugAsync(slug); + var product = await _context.Products + .Include(p => p.ProductCategories) + .ThenInclude(pc => pc.Category) + .FirstOrDefaultAsync(p => p.Slug == slug && p.IsActive); // Nur aktives Produkt finden if (product == null) { @@ -50,12 +67,18 @@ namespace Webshop.Application.Services.Public { Id = product.Id, Name = product.Name, - Description = product.Description, + Description = product.Description, // Hier die volle Beschreibung Price = product.Price, SKU = product.SKU, ImageUrl = product.ImageUrl, IsInStock = product.IsInStock, - Slug = product.Slug // Mapping für Slug + Slug = product.Slug, + Categories = product.ProductCategories.Select(pc => new CategoryDto + { + Id = pc.Category.Id, + Name = pc.Category.Name, + Slug = pc.Category.Slug + }).ToList() }; } }