From 2acb4b4117254126be303af44abf22a796c18c94 Mon Sep 17 00:00:00 2001 From: Francesco D'Amico Date: Sun, 30 Mar 2025 15:56:39 +0200 Subject: [PATCH] change some logic to the repositories --- .../Category/CategoryRepository.cs | 45 +++++ .../Category/ICategoryRepository.cs | 17 ++ .../Ingredient/IIngredientRepository.cs | 21 +++ .../Ingredient/IngredientRepository.cs | 109 +++++++++++ .../Instruction/IInstructionRepository.cs | 9 + .../Instruction/InstructionRepository.cs | 21 +++ .../MediaFile/IMediaFileRepository.cs | 13 ++ .../MediaFile/MediaFileRepository.cs | 101 ++++++++++ .../Repositories/Recipe/IRecipeRepository.cs | 23 +++ .../Repositories/Recipe/RecipeRepository.cs | 175 ++++++++++++++++++ .../ShoppingList/IShoppingListRepository.cs | 19 ++ .../ShoppingList/ShoppingListRepository.cs | 106 +++++++++++ .../Repositories/Unit/IUnitRepository.cs | 13 ++ .../Repositories/Unit/UnitRepository.cs | 43 +++++ 14 files changed, 715 insertions(+) create mode 100644 Francesco.Recipes.World/Repositories/Category/CategoryRepository.cs create mode 100644 Francesco.Recipes.World/Repositories/Category/ICategoryRepository.cs create mode 100644 Francesco.Recipes.World/Repositories/Ingredient/IIngredientRepository.cs create mode 100644 Francesco.Recipes.World/Repositories/Ingredient/IngredientRepository.cs create mode 100644 Francesco.Recipes.World/Repositories/Instruction/IInstructionRepository.cs create mode 100644 Francesco.Recipes.World/Repositories/Instruction/InstructionRepository.cs create mode 100644 Francesco.Recipes.World/Repositories/MediaFile/IMediaFileRepository.cs create mode 100644 Francesco.Recipes.World/Repositories/MediaFile/MediaFileRepository.cs create mode 100644 Francesco.Recipes.World/Repositories/Recipe/IRecipeRepository.cs create mode 100644 Francesco.Recipes.World/Repositories/Recipe/RecipeRepository.cs create mode 100644 Francesco.Recipes.World/Repositories/ShoppingList/IShoppingListRepository.cs create mode 100644 Francesco.Recipes.World/Repositories/ShoppingList/ShoppingListRepository.cs create mode 100644 Francesco.Recipes.World/Repositories/Unit/IUnitRepository.cs create mode 100644 Francesco.Recipes.World/Repositories/Unit/UnitRepository.cs diff --git a/Francesco.Recipes.World/Repositories/Category/CategoryRepository.cs b/Francesco.Recipes.World/Repositories/Category/CategoryRepository.cs new file mode 100644 index 0000000..6e372f8 --- /dev/null +++ b/Francesco.Recipes.World/Repositories/Category/CategoryRepository.cs @@ -0,0 +1,45 @@ +namespace Francesco.Recipes.World.Repositories.Category +{ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + using Francesco.Recipes.World.Data; + using Francesco.Recipes.World.Models.BackendModels.Category; + using Francesco.Recipes.World.Models.BackendModels.Recipe; + using Microsoft.EntityFrameworkCore; + + public class CategoryRepository : ICategoryRepository + { + private readonly FrancescosRecipesWorldDbContext _context; + + public CategoryRepository(FrancescosRecipesWorldDbContext context) + { + _context = context; + } + + public async Task GetCategoryByIdAsync(Guid categoryId) + { + var category = await _context.Categories.FindAsync(categoryId); + return category ?? throw new InvalidDataException($"Category {categoryId} not found."); + } + + public async Task> GetAllCategoriesAsync() + { + return await _context.Categories.ToListAsync(); + } + + public async Task> GetRecipesByCategoryAsync(Guid categoryId) + { + var category = await _context.Categories + .Include(c => c.Recipes) + .FirstOrDefaultAsync(c => c.Id == categoryId); + + if (category == null) + { + throw new InvalidDataException($"Category {categoryId} not found."); + } + + return category.Recipes; + } + } +} diff --git a/Francesco.Recipes.World/Repositories/Category/ICategoryRepository.cs b/Francesco.Recipes.World/Repositories/Category/ICategoryRepository.cs new file mode 100644 index 0000000..04491aa --- /dev/null +++ b/Francesco.Recipes.World/Repositories/Category/ICategoryRepository.cs @@ -0,0 +1,17 @@ +namespace Francesco.Recipes.World.Repositories.Category +{ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + using Francesco.Recipes.World.Models.BackendModels.Category; + using Francesco.Recipes.World.Models.BackendModels.Recipe; + + public interface ICategoryRepository + { + Task GetCategoryByIdAsync(Guid categoryId); + + Task> GetAllCategoriesAsync(); + + Task> GetRecipesByCategoryAsync(Guid categoryId); + } +} diff --git a/Francesco.Recipes.World/Repositories/Ingredient/IIngredientRepository.cs b/Francesco.Recipes.World/Repositories/Ingredient/IIngredientRepository.cs new file mode 100644 index 0000000..013e2d1 --- /dev/null +++ b/Francesco.Recipes.World/Repositories/Ingredient/IIngredientRepository.cs @@ -0,0 +1,21 @@ +namespace Francesco.Recipes.World.Repositories.Ingredient +{ + using Francesco.Recipes.World.Models.BackendModels.Ingredient; + using Francesco.Recipes.World.Models.BackendModels.Recipe; + using Francesco.Recipes.World.Models.BackendModels.RecipeIngredient; + + public interface IIngredientRepository + { + Task CreateIngredientToRecipeAsync(Recipe recipe, string ingredientName); + + Task UpdateIngredientAsync(Ingredient ingredient); + + Task> GetIngredientsByRecipeIdAsync(Guid recipeId); + + Task> GetIngredientsByNameAsync(string name); + + Task RemoveIngredientFromRecipeAsync(Recipe recipe, Guid ingredientId); + + Task GetIngredientByIdAsync(Guid ingredientId); + } +} diff --git a/Francesco.Recipes.World/Repositories/Ingredient/IngredientRepository.cs b/Francesco.Recipes.World/Repositories/Ingredient/IngredientRepository.cs new file mode 100644 index 0000000..f573a81 --- /dev/null +++ b/Francesco.Recipes.World/Repositories/Ingredient/IngredientRepository.cs @@ -0,0 +1,109 @@ +namespace Francesco.Recipes.World.Repositories.Ingredient +{ + using Francesco.Recipes.World.Data; + using Francesco.Recipes.World.Models.BackendModels.Ingredient; + using Francesco.Recipes.World.Models.BackendModels.Recipe; + using Francesco.Recipes.World.Models.BackendModels.RecipeIngredient; + using Microsoft.EntityFrameworkCore; + + public class IngredientRepository : IIngredientRepository + { + private readonly FrancescosRecipesWorldDbContext _context; + + public IngredientRepository( + FrancescosRecipesWorldDbContext context) + { + _context = context; + } + + public async Task CreateIngredientToRecipeAsync(Recipe recipe, string ingredientName) + { + if (recipe is null) + { + throw new ArgumentException("Recipe not found", nameof(recipe)); + } + + var newIngredient = new Ingredient + { + Id = Guid.NewGuid(), + Name = ingredientName, + }; + + var recipeIngredient = new RecipeIngredient + { + Id = Guid.NewGuid(), + Recipe = recipe, + Ingredient = newIngredient, + Quantity = 1, + }; + + _context.Ingredients.Add(newIngredient); + _context.RecipeIngredients.Add(recipeIngredient); + await _context.SaveChangesAsync(); + + return newIngredient; + } + + public async Task RemoveIngredientFromRecipeAsync(Recipe recipe, Guid ingredientId) + { + if (recipe == null) + { + throw new ArgumentNullException(nameof(recipe)); + } + + var ingredientToRemove = recipe.RecipeIngredients.FirstOrDefault(i => i.Id == ingredientId); + + if (ingredientToRemove != null) + { + recipe.RecipeIngredients.Remove(ingredientToRemove); + await _context.SaveChangesAsync(); + } + } + + public async Task> GetIngredientsByNameAsync(string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + return await _context.Ingredients.ToListAsync(); + } + + return await _context.Ingredients + .Where(i => i.Name.ToLower().Contains(name.ToLower())) + .ToListAsync(); + } + + public async Task> GetIngredientsByRecipeIdAsync(Guid recipeId) + { + return await _context.RecipeIngredients + .Include(ri => ri.Ingredient) + .Include(ri => ri.Unit) + .Where(ri => ri.Recipe.Id == recipeId) + .ToListAsync(); + } + + public async Task UpdateIngredientAsync(Ingredient ingredient) + { + if (ingredient == null) + { + throw new ArgumentNullException(nameof(ingredient)); + } + + var existingIngredient = await _context.Ingredients.FindAsync(ingredient.Id); + if (existingIngredient == null) + { + throw new InvalidOperationException($"Ingredient with ID {ingredient.Id} not found."); + } + + existingIngredient.Name = ingredient.Name; + + _context.Ingredients.Update(existingIngredient); + await _context.SaveChangesAsync(); + } + + public async Task GetIngredientByIdAsync(Guid ingredientId) + { + var ingredient = await _context.Ingredients.FindAsync(ingredientId); + return ingredient ?? throw new InvalidDataException($"Address {ingredientId} not found."); + } + } +} diff --git a/Francesco.Recipes.World/Repositories/Instruction/IInstructionRepository.cs b/Francesco.Recipes.World/Repositories/Instruction/IInstructionRepository.cs new file mode 100644 index 0000000..7969570 --- /dev/null +++ b/Francesco.Recipes.World/Repositories/Instruction/IInstructionRepository.cs @@ -0,0 +1,9 @@ +namespace Francesco.Recipes.World.Repositories.Instruction +{ + using Francesco.Recipes.World.Models.BackendModels.Instruction; + + public interface IInstructionRepository + { + Task GetInstructionAsync(Guid instructionId); + } +} diff --git a/Francesco.Recipes.World/Repositories/Instruction/InstructionRepository.cs b/Francesco.Recipes.World/Repositories/Instruction/InstructionRepository.cs new file mode 100644 index 0000000..b772813 --- /dev/null +++ b/Francesco.Recipes.World/Repositories/Instruction/InstructionRepository.cs @@ -0,0 +1,21 @@ +namespace Francesco.Recipes.World.Repositories.Instruction +{ + using Francesco.Recipes.World.Data; + using Francesco.Recipes.World.Models.BackendModels.Instruction; + + public class InstructionRepository : IInstructionRepository + { + private readonly FrancescosRecipesWorldDbContext _context; + + public InstructionRepository(FrancescosRecipesWorldDbContext context) + { + _context = context; + } + + public async Task GetInstructionAsync(Guid instructionId) + { + var instruction = await _context.Instructions.FindAsync(instructionId); + return instruction ?? throw new InvalidDataException($"Instruction {instructionId} not found."); + } + } +} diff --git a/Francesco.Recipes.World/Repositories/MediaFile/IMediaFileRepository.cs b/Francesco.Recipes.World/Repositories/MediaFile/IMediaFileRepository.cs new file mode 100644 index 0000000..0fc5d91 --- /dev/null +++ b/Francesco.Recipes.World/Repositories/MediaFile/IMediaFileRepository.cs @@ -0,0 +1,13 @@ +namespace Francesco.Recipes.World.Repositories.MediaFile +{ + public interface IMediaFileRepository + { + Task ReplaceInstructionImageAsync(Guid instructionId, IFormFile? newPhoto); + + Task UploadInstructionImageAsync(Guid instructionId, IFormFile? photo); + + Task ReplaceRecipeImageAsync(Guid recipeId, IFormFile? newPhoto); + + Task UploadRecipeImageAsync(Guid recipeId, IFormFile? photo); + } +} diff --git a/Francesco.Recipes.World/Repositories/MediaFile/MediaFileRepository.cs b/Francesco.Recipes.World/Repositories/MediaFile/MediaFileRepository.cs new file mode 100644 index 0000000..742e4c4 --- /dev/null +++ b/Francesco.Recipes.World/Repositories/MediaFile/MediaFileRepository.cs @@ -0,0 +1,101 @@ +namespace Francesco.Recipes.World.Repositories.MediaFile +{ + using Francesco.Recipes.World.Data; + using Francesco.Recipes.World.Models.BackendModels.MediaFile; + using Francesco.Recipes.World.Repositories.Instruction; + using Francesco.Recipes.World.Repositories.Recipe; + + public class MediaFileRepository : IMediaFileRepository + { + private readonly IInstructionRepository _instructionRepository; + private readonly FrancescosRecipesWorldDbContext _context; + private readonly IRecipeRepository _recipeRepository; + + public MediaFileRepository(IInstructionRepository instructionRepository, FrancescosRecipesWorldDbContext context, IRecipeRepository recipeRepository) + { + _instructionRepository = instructionRepository; + _context = context; + _recipeRepository = recipeRepository; + } + + public async Task ReplaceInstructionImageAsync(Guid instructionId, IFormFile? newPhoto) + { + if (newPhoto is null) + { + throw new ArgumentNullException(nameof(newPhoto)); + } + + var instruction = await _instructionRepository.GetInstructionAsync(instructionId); + _context.RemoveRange(instruction.MediaFiles); + await _context.SaveChangesAsync(); + + await UploadInstructionImageAsync(instructionId, newPhoto); + } + + public async Task ReplaceRecipeImageAsync(Guid recipeId, IFormFile? newPhoto) + { + if (newPhoto is null) + { + throw new ArgumentNullException(nameof(newPhoto)); + } + + var recipe = await _recipeRepository.GetRecipeAsync(recipeId); + _context.RemoveRange(recipe.MediaFiles); + await _context.SaveChangesAsync(); + + await UploadInstructionImageAsync(recipeId, newPhoto); + } + + public async Task UploadInstructionImageAsync(Guid instructionId, IFormFile? photo) + { + if (photo is null) + { + throw new ArgumentNullException(nameof(photo)); + } + + var instruction = await _instructionRepository.GetInstructionAsync(instructionId); + + using (var memoryStream = new MemoryStream()) + { + await photo.CopyToAsync(memoryStream); + + var instructionImage = new MediaFile + { + FileName = photo.FileName, + MimeType = photo.ContentType, + Data = memoryStream.ToArray(), + Instruction = instruction, + }; + + _context.Add(instructionImage); + await _context.SaveChangesAsync(); + } + } + + public async Task UploadRecipeImageAsync(Guid recipeId, IFormFile? photo) + { + if (photo is null) + { + throw new ArgumentNullException(nameof(photo)); + } + + var recipe = await _recipeRepository.GetRecipeAsync(recipeId); + + using (var memoryStream = new MemoryStream()) + { + await photo.CopyToAsync(memoryStream); + + var recipeImage = new MediaFile + { + FileName = photo.FileName, + MimeType = photo.ContentType, + Data = memoryStream.ToArray(), + Recipe = recipe, + }; + + _context.Add(recipeImage); + await _context.SaveChangesAsync(); + } + } + } +} diff --git a/Francesco.Recipes.World/Repositories/Recipe/IRecipeRepository.cs b/Francesco.Recipes.World/Repositories/Recipe/IRecipeRepository.cs new file mode 100644 index 0000000..43a652d --- /dev/null +++ b/Francesco.Recipes.World/Repositories/Recipe/IRecipeRepository.cs @@ -0,0 +1,23 @@ +namespace Francesco.Recipes.World.Repositories.Recipe +{ + using Francesco.Recipes.World.Models.BackendModels.Category; + using Francesco.Recipes.World.Models.BackendModels.Recipe; + using Francesco.Recipes.World.Models.BackendModels.Unit; + + public interface IRecipeRepository + { + Task GetRecipeAsync(Guid recipeId); + + Task AddOrCreateIngredientToRecipeAsync(Guid recipeId, string ingredientName, int quantity, Guid unitId); + + Task RemoveIngredientFromRecipeAsync(Guid recipeId, Guid ingredientId); + + Task> GetRecipesByNameOrIngredientAsync(string name, string ingredient); + + Task> GetRecipesByDifficultyAsync(Difficulty difficulty); + + Task AddUnitToRecipeAsync(string name, string symbol); + + Task CreateRecipeForCategoryAsync(Category category, string name, string description, Difficulty difficulty, int servings, TimeSpan preparationTime, TimeSpan cookingTime); + } +} diff --git a/Francesco.Recipes.World/Repositories/Recipe/RecipeRepository.cs b/Francesco.Recipes.World/Repositories/Recipe/RecipeRepository.cs new file mode 100644 index 0000000..300c467 --- /dev/null +++ b/Francesco.Recipes.World/Repositories/Recipe/RecipeRepository.cs @@ -0,0 +1,175 @@ +namespace Francesco.Recipes.World.Repositories.Recipe +{ + using Francesco.Recipes.World.Data; + using Francesco.Recipes.World.Models.BackendModels.Category; + using Francesco.Recipes.World.Models.BackendModels.Ingredient; + using Francesco.Recipes.World.Models.BackendModels.Recipe; + using Francesco.Recipes.World.Models.BackendModels.RecipeIngredient; + using Francesco.Recipes.World.Models.BackendModels.Unit; + using Francesco.Recipes.World.Repositories.Ingredient; + using Francesco.Recipes.World.Repositories.Unit; + using Microsoft.EntityFrameworkCore; + + public class RecipeRepository : IRecipeRepository + { + private readonly FrancescosRecipesWorldDbContext _context; + private readonly IIngredientRepository _ingredientRepository; + private readonly IUnitRepository _unitRepository; + + public RecipeRepository( + FrancescosRecipesWorldDbContext context, IIngredientRepository ingredientRepository, IUnitRepository unitRepository) + { + _context = context; + _ingredientRepository = ingredientRepository; + _unitRepository = unitRepository; + } + + public async Task GetRecipeAsync(Guid recipeId) + { + var recipe = await _context.Recipes.FindAsync(recipeId); + return recipe ?? throw new InvalidDataException($"Address {recipeId} not found."); + } + + public async Task AddOrCreateIngredientToRecipeAsync(Guid recipeId, string ingredientName, int quantity, Guid unitId) + { + var recipe = await GetRecipeAsync(recipeId); + var unit = await _unitRepository.GetUnitByIdAsync(unitId); + + if (quantity <= 0) + { + throw new ArgumentOutOfRangeException(nameof(quantity), "Die Menge muss größer als 0 sein."); + } + + var ingredients = await _ingredientRepository.GetIngredientsByNameAsync(ingredientName); + Ingredient ingredient; + + if (ingredients == null || !ingredients.Any()) + { + ingredient = new Ingredient + { + Id = Guid.NewGuid(), + Name = ingredientName, + }; + _context.Ingredients.Add(ingredient); + await _context.SaveChangesAsync(); + } + else + { + ingredient = ingredients.First(); + } + + var existingEntry = await _context.RecipeIngredients + .FirstOrDefaultAsync(ri => ri.Recipe.Id == recipeId && ri.Ingredient.Id == ingredient.Id); + if (existingEntry != null) + { + throw new InvalidOperationException("Das Rezept enthält diese Zutat bereits."); + } + + var recipeIngredient = new RecipeIngredient + { + Recipe = recipe, + Ingredient = ingredient, + Unit = unit, + Quantity = quantity, + }; + _context.Add(recipeIngredient); + await _context.SaveChangesAsync(); + } + + public async Task CreateRecipeForCategoryAsync( + Category category, + string name, + string description, + Difficulty difficulty, + int servings, + TimeSpan preparationTime, + TimeSpan cookingTime) + { + if (category == null) + { + throw new ArgumentNullException(nameof(category), "Category cannot be null."); + } + + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentNullException(nameof(name), "Name cannot be empty."); + } + + if (servings <= 0) + { + throw new ArgumentOutOfRangeException(nameof(servings), "Servings must be greater than 0."); + } + + var recipe = new Recipe + { + Id = Guid.NewGuid(), + Name = name, + Description = description, + Difficulty = difficulty, + Servings = servings, + PreparationTime = preparationTime, + CookingTime = cookingTime, + CreatedAt = DateTime.UtcNow, + Category = category, + }; + + _context.Recipes.Add(recipe); + await _context.SaveChangesAsync(); + + return recipe; + } + + public async Task RemoveIngredientFromRecipeAsync(Guid recipeId, Guid ingredientId) + { + var recipe = await GetRecipeAsync(recipeId); + var recipeIngredient = await _context.RecipeIngredients + .FirstOrDefaultAsync(ri => ri.Recipe.Id == recipeId && ri.Ingredient.Id == ingredientId); + + if (recipeIngredient == null) + { + throw new ArgumentException("Diese Zutat ist nicht mit dem Rezept verknüpft."); + } + + _context.RecipeIngredients.Remove(recipeIngredient); + await _context.SaveChangesAsync(); + } + + public async Task> GetRecipesByNameOrIngredientAsync(string name, string ingredient) + { + var query = _context.Recipes + .Include(r => r.RecipeIngredients) + .ThenInclude(ri => ri.Ingredient) + .AsQueryable(); + + if (!string.IsNullOrWhiteSpace(name)) + { + query = query.Where(r => r.Name.Contains(name, StringComparison.OrdinalIgnoreCase)); + } + + if (!string.IsNullOrWhiteSpace(ingredient)) + { + var ingredientMatches = await _ingredientRepository.GetIngredientsByNameAsync(ingredient); + var ingredientIds = ingredientMatches.Select(i => i.Id).ToList(); + + if (ingredientIds.Any()) + { + query = query.Where(r => r.RecipeIngredients.Any(ri => ingredientIds.Contains(ri.Ingredient.Id))); + } + } + + return await query.ToListAsync(); + } + + public async Task> GetRecipesByDifficultyAsync(Difficulty difficulty) + { + return await _context.Recipes + .Where(r => r.Difficulty == difficulty) + .ToListAsync(); + } + + public async Task AddUnitToRecipeAsync(string name, string symbol) + { + return await _unitRepository.AddUnitAsync(name, symbol); + } + } +} diff --git a/Francesco.Recipes.World/Repositories/ShoppingList/IShoppingListRepository.cs b/Francesco.Recipes.World/Repositories/ShoppingList/IShoppingListRepository.cs new file mode 100644 index 0000000..2205dbc --- /dev/null +++ b/Francesco.Recipes.World/Repositories/ShoppingList/IShoppingListRepository.cs @@ -0,0 +1,19 @@ + +namespace Francesco.Recipes.World.Repositories.ShoppingList +{ + using Francesco.Recipes.World.Models.BackendModels.Recipe; + using Francesco.Recipes.World.Models.BackendModels.Shoppinglist; + + public interface IShoppingListRepository + { + Task AddIngredientToShoppingListAsync(Guid recipeIngredientId, Guid shoppingListId); + + Task RemoveRecipeIngredientFromShoppingListAsync(Guid recipeIngredientId); + + Task RemoveRecipeFromShoppingListIfEmptyAsync(Guid recipeId, Guid shoppingListId); + + Task> GetShoppingListsByIngredientOfRecipeAsync(Guid ingredientId, Guid recipeId); + + Task GetRecipeByNameAndImageAsync(string recipeName, string imageFileName); + } +} diff --git a/Francesco.Recipes.World/Repositories/ShoppingList/ShoppingListRepository.cs b/Francesco.Recipes.World/Repositories/ShoppingList/ShoppingListRepository.cs new file mode 100644 index 0000000..5609143 --- /dev/null +++ b/Francesco.Recipes.World/Repositories/ShoppingList/ShoppingListRepository.cs @@ -0,0 +1,106 @@ +namespace Francesco.Recipes.World.Repositories.ShoppingList +{ + using Francesco.Recipes.World.Data; + using Francesco.Recipes.World.Models.BackendModels.IngredientShoppingList; + using Francesco.Recipes.World.Models.BackendModels.Recipe; + using Francesco.Recipes.World.Models.BackendModels.Shoppinglist; + using Microsoft.EntityFrameworkCore; + + public class ShoppingListRepository : IShoppingListRepository + { + private readonly FrancescosRecipesWorldDbContext _context; + + public ShoppingListRepository(FrancescosRecipesWorldDbContext context) + { + _context = context; + } + + public async Task AddIngredientToShoppingListAsync(Guid recipeIngredientId, Guid shoppingListId) + { + var existingEntry = await _context.RecipeIngredientsShoppingLists + .FirstOrDefaultAsync(risl => risl.RecipeIngredient.Id == recipeIngredientId && risl.ShoppingList.Id == shoppingListId); + + if (existingEntry != null) + { + return; + } + + var recipeIngredient = await _context.RecipeIngredients + .FirstOrDefaultAsync(ri => ri.Id == recipeIngredientId); + + if (recipeIngredient == null) + { + throw new InvalidOperationException($"Recipe ingredient with ID {recipeIngredientId} not found."); + } + + var shoppingList = await _context.ShoppingLists + .FirstOrDefaultAsync(sl => sl.Id == shoppingListId); + + if (shoppingList == null) + { + throw new InvalidOperationException($"Shopping list with ID {shoppingListId} not found."); + } + + var newEntry = new RecipeIngredientShoppingList + { + RecipeIngredient = recipeIngredient, + ShoppingList = shoppingList, + }; + + _context.RecipeIngredientsShoppingLists.Add(newEntry); + await _context.SaveChangesAsync(); + } + + public async Task RemoveRecipeIngredientFromShoppingListAsync(Guid recipeIngredientId) + { + var entry = await _context.RecipeIngredientsShoppingLists + .FirstOrDefaultAsync(risl => risl.RecipeIngredient.Id == recipeIngredientId); + + if (entry != null) + { + _context.RecipeIngredientsShoppingLists.Remove(entry); + await _context.SaveChangesAsync(); + } + } + + public async Task RemoveRecipeFromShoppingListIfEmptyAsync(Guid recipeId, Guid shoppingListId) + { + var hasIngredients = await _context.RecipeIngredientsShoppingLists + .AnyAsync(risl => risl.RecipeIngredient.Recipe.Id == recipeId && risl.ShoppingList.Id == shoppingListId); + + if (!hasIngredients) + { + var shoppingList = await _context.ShoppingLists + .Include(sl => sl.RecipeIngredientShoppingLists) + .FirstOrDefaultAsync(sl => sl.Id == shoppingListId); + + if (shoppingList != null) + { + var recipeToRemove = shoppingList.RecipeIngredientShoppingLists + .FirstOrDefault(risl => risl.RecipeIngredient.Recipe.Id == recipeId); + + if (recipeToRemove != null) + { + _context.RecipeIngredientsShoppingLists.Remove(recipeToRemove); + await _context.SaveChangesAsync(); + } + } + } + } + + public async Task> GetShoppingListsByIngredientOfRecipeAsync(Guid ingredientId, Guid recipeId) + { + return await _context.ShoppingLists + .Where(sl => sl.RecipeIngredientShoppingLists.Any(risl => risl.RecipeIngredient.Ingredient.Id == ingredientId && risl.RecipeIngredient.Recipe.Id == recipeId)) + .ToListAsync(); + } + + public async Task GetRecipeByNameAndImageAsync(string recipeName, string imageFileName) + { + return await _context.Recipes + .Where(r => r.Name == recipeName && r.MediaFiles.Any(mf => mf.FileName == imageFileName)) + .Include(r => r.MediaFiles.Where(mf => mf.FileName == imageFileName)) + .FirstOrDefaultAsync(); + } + } +} diff --git a/Francesco.Recipes.World/Repositories/Unit/IUnitRepository.cs b/Francesco.Recipes.World/Repositories/Unit/IUnitRepository.cs new file mode 100644 index 0000000..a8244cd --- /dev/null +++ b/Francesco.Recipes.World/Repositories/Unit/IUnitRepository.cs @@ -0,0 +1,13 @@ +namespace Francesco.Recipes.World.Repositories.Unit +{ + using Francesco.Recipes.World.Models.BackendModels.Unit; + + public interface IUnitRepository + { + Task GetUnitByIdAsync(Guid unitId); + + Task AddUnitAsync(string name, string symbol); + + Task> GetAllUnitsAsync(); + } +} diff --git a/Francesco.Recipes.World/Repositories/Unit/UnitRepository.cs b/Francesco.Recipes.World/Repositories/Unit/UnitRepository.cs new file mode 100644 index 0000000..04297cd --- /dev/null +++ b/Francesco.Recipes.World/Repositories/Unit/UnitRepository.cs @@ -0,0 +1,43 @@ +namespace Francesco.Recipes.World.Repositories.Unit +{ + using Francesco.Recipes.World.Data; + using Francesco.Recipes.World.Models.BackendModels.Unit; + using Microsoft.EntityFrameworkCore; + + public class UnitRepository : IUnitRepository + { + private readonly FrancescosRecipesWorldDbContext _context; + + public UnitRepository( + FrancescosRecipesWorldDbContext context) + { + _context = context; + } + + public async Task GetUnitByIdAsync(Guid unitId) + { + var unit = await _context.Units.FindAsync(unitId); + return unit ?? throw new InvalidDataException($"Address {unitId} not found."); + } + + public async Task AddUnitAsync(string name, string symbol) + { + var unit = new Unit + { + Id = Guid.NewGuid(), + Name = name, + Symbol = symbol, + }; + + _context.Units.Add(unit); + await _context.SaveChangesAsync(); + + return unit; + } + + public async Task> GetAllUnitsAsync() + { + return await _context.Units.ToListAsync(); + } + } +}