Merge develop into feature/Show-ShoppingList-Of-Active-RecipeCard
This commit is contained in:
@@ -47,6 +47,7 @@
|
||||
return await _context.Categories
|
||||
.Include(c => c.Recipes)
|
||||
.ThenInclude(r => r.MediaFiles)
|
||||
.AsSplitQuery()
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
public async Task<IEnumerable<Recipe>> GetFavoriteRecipesAsync()
|
||||
{
|
||||
return await _context.Recipes
|
||||
.Where(r => r.IsFavorite)
|
||||
.ToListAsync();
|
||||
.Where(r => r.IsFavorite)
|
||||
.Include(r => r.Favorite)
|
||||
.Include(r => r.MediaFiles)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<bool> IsFavoriteAsync(Guid recipeId)
|
||||
@@ -28,12 +30,32 @@
|
||||
|
||||
public async Task AddFavoriteAsync(Guid recipeId)
|
||||
{
|
||||
var recipe = await _context.Recipes.FindAsync(recipeId);
|
||||
if (recipe != null && !recipe.IsFavorite)
|
||||
var recipe = await _context.Recipes
|
||||
.Include(r => r.Favorite)
|
||||
.FirstOrDefaultAsync(r => r.Id == recipeId);
|
||||
|
||||
if (recipe == null)
|
||||
{
|
||||
recipe.IsFavorite = true;
|
||||
await _context.SaveChangesAsync();
|
||||
throw new InvalidOperationException("Recipe not found.");
|
||||
}
|
||||
|
||||
if (recipe.IsFavorite)
|
||||
{
|
||||
throw new InvalidOperationException("Recipe is already a favorite.");
|
||||
}
|
||||
|
||||
recipe.IsFavorite = true;
|
||||
|
||||
if (recipe.Favorite == null || recipe.Favorite.Id == Guid.Empty)
|
||||
{
|
||||
recipe.Favorite = new Models.BackendModels.Favorit.Favorit
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
CreatedAt = DateTime.Now,
|
||||
};
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task RemoveFavoriteAsync(Guid recipeId)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
namespace Francesco.Recipes.World.Repositories.MediaFile
|
||||
{
|
||||
using Francesco.Recipes.World.Constants;
|
||||
using Francesco.Recipes.World.Data;
|
||||
using Francesco.Recipes.World.Models.BackendModels.MediaFile;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Recipe;
|
||||
@@ -83,7 +84,7 @@
|
||||
{
|
||||
var recipe = await _recipeRepository.GetRecipeAsync(recipeId);
|
||||
|
||||
var isImage = mediaFile.ContentType.StartsWith("image/");
|
||||
var isImage = mediaFile.ContentType.StartsWith(ContentType.Image);
|
||||
var isVideo = mediaFile.ContentType.StartsWith("video/");
|
||||
|
||||
if (!isImage && !isVideo)
|
||||
@@ -93,7 +94,7 @@
|
||||
|
||||
if (isImage)
|
||||
{
|
||||
await RemoveExistingMediaAsync(recipe, "image/");
|
||||
await RemoveExistingMediaAsync(recipe, ContentType.Image);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
namespace Francesco.Recipes.World.Repositories.Recipe
|
||||
{
|
||||
using Francesco.Recipes.World.Models;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Category;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Recipe;
|
||||
|
||||
@@ -17,6 +18,7 @@
|
||||
|
||||
Task<Recipe> CreateRecipeForCategoryAsync(Category category, string name, string description, Difficulty difficulty, int servings, TimeSpan preparationTime, TimeSpan cookingTime);
|
||||
|
||||
Task<IEnumerable<Recipe>> SearchInRecipesAndIngredients(string searchterm);
|
||||
Task<bool> DeleteRecipeAsync(Guid recipeId);
|
||||
Task<IEnumerable<SearchViewModel>> SearchInRecipesAndIngredients(string searchTerm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
namespace Francesco.Recipes.World.Repositories.Recipe
|
||||
{
|
||||
using System.Linq.Expressions;
|
||||
using Francesco.Recipes.World.Constants;
|
||||
using Francesco.Recipes.World.Data;
|
||||
using Francesco.Recipes.World.Models;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Category;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Ingredient;
|
||||
using Francesco.Recipes.World.Models.BackendModels.Recipe;
|
||||
@@ -38,6 +41,7 @@
|
||||
.ThenInclude(ri => ri.Unit)
|
||||
.Include(r => r.MediaFiles)
|
||||
.Include(r => r.Instructions)
|
||||
.ThenInclude(i => i.MediaFiles)
|
||||
.FirstOrDefaultAsync(r => r.Id == recipeId);
|
||||
}
|
||||
|
||||
@@ -147,21 +151,32 @@
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Recipe>> SearchInRecipesAndIngredients(string searchTerm)
|
||||
public async Task<IEnumerable<SearchViewModel>> SearchInRecipesAndIngredients(string searchTerm)
|
||||
{
|
||||
var queryable = _context.Recipes
|
||||
.Include(r => r.RecipeIngredients)
|
||||
.ThenInclude(ri => ri.Ingredient)
|
||||
.AsQueryable();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(searchTerm))
|
||||
try
|
||||
{
|
||||
searchTerm = searchTerm.ToLower();
|
||||
queryable = queryable.Where(r => r.Name.ToLower().Contains(searchTerm) ||
|
||||
r.RecipeIngredients.Any(ri => ri.Ingredient.Name.ToLower().Contains(searchTerm)));
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(searchTerm))
|
||||
{
|
||||
return await _context.Recipes
|
||||
.OrderByDescending(r => r.CreatedAt)
|
||||
.Take(20)
|
||||
.Select(SearchViewModelSelector())
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
return await queryable.ToListAsync();
|
||||
var normalizedSearchTerm = searchTerm.ToLower();
|
||||
|
||||
return await ApplyRecipeSearchFilter(_context.Recipes, normalizedSearchTerm)
|
||||
.OrderByDescending(r => r.CreatedAt)
|
||||
.Take(100)
|
||||
.Select(SearchViewModelSelector())
|
||||
.ToListAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error in SearchInRecipesAndIngredientsOptimized: {ex.Message}");
|
||||
return new List<SearchViewModel>();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<Recipe>> GetRecipesByDifficultyAsync(Difficulty? difficulty)
|
||||
@@ -182,5 +197,77 @@
|
||||
.ThenInclude(ri => ri.Ingredient)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
private static Expression<Func<Recipe, SearchViewModel>> SearchViewModelSelector()
|
||||
{
|
||||
return r => new SearchViewModel
|
||||
{
|
||||
Id = r.Id,
|
||||
Name = r.Name,
|
||||
Description = r.Description,
|
||||
IsFavorite = r.IsFavorite,
|
||||
ImageData = r.MediaFiles
|
||||
.Where(m => m.MimeType != null && m.MimeType.StartsWith(ContentType.Image))
|
||||
.Select(m => m.Data)
|
||||
.FirstOrDefault(),
|
||||
MimeType = r.MediaFiles
|
||||
.Where(m => m.MimeType != null && m.MimeType.StartsWith(ContentType.Image))
|
||||
.Select(m => m.MimeType)
|
||||
.FirstOrDefault(),
|
||||
Ingredients = r.RecipeIngredients.Select(ri => ri.Ingredient.Name).ToList(),
|
||||
TotalTime = r.PreparationTime.Add(r.CookingTime),
|
||||
};
|
||||
}
|
||||
|
||||
private static IQueryable<Recipe> ApplyRecipeSearchFilter(IQueryable<Recipe> query, string normalizedSearchTerm)
|
||||
{
|
||||
return query.Where(r =>
|
||||
EF.Functions.Like(r.Name.ToLower(), $"%{normalizedSearchTerm}%") ||
|
||||
(r.Description != null && EF.Functions.Like(r.Description.ToLower(), $"%{normalizedSearchTerm}%")) ||
|
||||
r.RecipeIngredients.Any(ri => EF.Functions.Like(ri.Ingredient.Name.ToLower(), $"%{normalizedSearchTerm}%")));
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteRecipeAsync(Guid recipeId)
|
||||
{
|
||||
var recipe = await GetRecipeByIdAsync(recipeId);
|
||||
|
||||
if (recipe == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (recipe.RecipeIngredients != null && recipe.RecipeIngredients.Any())
|
||||
{
|
||||
_context.RecipeIngredients.RemoveRange(recipe.RecipeIngredients);
|
||||
}
|
||||
|
||||
if (recipe.Instructions != null && recipe.Instructions.Any())
|
||||
{
|
||||
foreach (var instruction in recipe.Instructions)
|
||||
{
|
||||
if (instruction.MediaFiles != null && instruction.MediaFiles.Any())
|
||||
{
|
||||
_context.MediaFiles.RemoveRange(instruction.MediaFiles);
|
||||
}
|
||||
}
|
||||
|
||||
_context.Instructions.RemoveRange(recipe.Instructions);
|
||||
}
|
||||
|
||||
if (recipe.MediaFiles != null && recipe.MediaFiles.Any())
|
||||
{
|
||||
_context.MediaFiles.RemoveRange(recipe.MediaFiles);
|
||||
}
|
||||
|
||||
if (recipe.Favorit != null && recipe.Favorit.Id != Guid.Empty)
|
||||
{
|
||||
_context.Remove(recipe.Favorit);
|
||||
}
|
||||
|
||||
_context.Recipes.Remove(recipe);
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user