images and steps added to recipe

This commit is contained in:
durn
2026-03-03 05:45:39 -07:00
parent e2fb9c1034
commit fcb00b32a7
7 changed files with 243 additions and 30 deletions
@@ -0,0 +1,26 @@
package com.example.demo.dto;
public class ImageDto {
String imageUrl;
public ImageDto() {
super();
}
public ImageDto(String imageUrl) {
super();
this.imageUrl = imageUrl;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
}
@@ -13,6 +13,8 @@ public class RecipeDto {
private UserDto userDto; private UserDto userDto;
private String status; private String status;
private List<RecipeIngredientDto> ingredients; private List<RecipeIngredientDto> ingredients;
private List<StepDto> steps;
private List<ImageDto> images;
@@ -22,7 +24,7 @@ public class RecipeDto {
} }
public RecipeDto(String title, String description, Integer prepTimeMinutes, Integer cookTimeMinutes, public RecipeDto(String title, String description, Integer prepTimeMinutes, Integer cookTimeMinutes,
Integer servings, UserDto userDto, String status, List<RecipeIngredientDto> ingredients) { Integer servings, UserDto userDto, String status, List<RecipeIngredientDto> ingredients, List<StepDto> steps, List<ImageDto> images) {
super(); super();
this.title = title; this.title = title;
this.description = description; this.description = description;
@@ -32,6 +34,8 @@ public class RecipeDto {
this.userDto = userDto; this.userDto = userDto;
this.status = status; this.status = status;
this.ingredients = ingredients; this.ingredients = ingredients;
this.steps = steps;
this.images = images;
} }
// getters and setters // getters and setters
@@ -79,6 +83,23 @@ public class RecipeDto {
this.ingredients = ingredients; this.ingredients = ingredients;
} }
public List<StepDto> getSteps() {
return steps;
}
public void setSteps(List<StepDto> steps) {
this.steps = steps;
}
public List<ImageDto> getImages() {
return images;
}
public void setImages(List<ImageDto> images) {
this.images = images;
}
public UserDto getUserDto() { public UserDto getUserDto() {
return userDto; return userDto;
} }
@@ -1,5 +1,29 @@
package com.example.demo.dto; package com.example.demo.dto;
public class StepDto { public class StepDto {
private Integer stepNumber;
private String instruction;
public StepDto() {}
public StepDto(Integer stepNumber, String instruction) {
this.stepNumber = stepNumber;
this.instruction = instruction;
}
public Integer getStepNumber() {
return stepNumber;
}
public void setStepNumber(Integer stepNumber) {
this.stepNumber = stepNumber;
}
public String getInstruction() {
return instruction;
}
public void setInstruction(String instruction) {
this.instruction = instruction;
}
} }
@@ -1,6 +1,8 @@
package com.example.demo.entity; package com.example.demo.entity;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@Entity @Entity
@@ -11,8 +13,10 @@ public class Image {
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id; private Integer id;
@Column(name = "recipe_id", nullable = false) @ManyToOne(fetch = FetchType.LAZY)
private Integer recipeId; @JoinColumn(name = "recipe_id", nullable = false)
@EqualsAndHashCode.Include
private Recipe recipe;
@Column(name = "image_url", nullable = false) @Column(name = "image_url", nullable = false)
private String imageUrl; private String imageUrl;
@@ -22,8 +26,8 @@ public class Image {
public Image() {} public Image() {}
public Image(Integer recipeId, String imageUrl) { public Image(Recipe recipe, String imageUrl) {
this.recipeId = recipeId; this.recipe = recipe;
this.imageUrl = imageUrl; this.imageUrl = imageUrl;
this.createdAt = LocalDateTime.now(); this.createdAt = LocalDateTime.now();
} }
@@ -32,8 +36,13 @@ public class Image {
public Integer getId() { return id; } public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; } public void setId(Integer id) { this.id = id; }
public Integer getRecipeId() { return recipeId; } public Recipe getRecipe() {
public void setRecipeId(Integer recipeId) { this.recipeId = recipeId; } return recipe;
}
public void setRecipe(Recipe recipe) {
this.recipe = recipe;
}
public String getImageUrl() { return imageUrl; } public String getImageUrl() { return imageUrl; }
public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; }
@@ -40,6 +40,14 @@ public class Recipe {
@OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) @OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<RecipeIngredient> recipeIngredients = new HashSet<>(); private Set<RecipeIngredient> recipeIngredients = new HashSet<>();
// Recipe Steps relationship
@OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<Step> steps = new HashSet<>();
// Recipe Images relationship
@OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<Image> images = new HashSet<>();
// Default constructor // Default constructor
public Recipe() {} public Recipe() {}
@@ -123,4 +131,20 @@ public class Recipe {
public Set<RecipeIngredient> getRecipeIngredients() { return recipeIngredients; } public Set<RecipeIngredient> getRecipeIngredients() { return recipeIngredients; }
public void setRecipeIngredients(Set<RecipeIngredient> recipeIngredients) { this.recipeIngredients = recipeIngredients; } public void setRecipeIngredients(Set<RecipeIngredient> recipeIngredients) { this.recipeIngredients = recipeIngredients; }
public Set<Step> getSteps() {
return steps;
}
public void setSteps(Set<Step> steps) {
this.steps = steps;
}
public Set<Image> getImages() {
return images;
}
public void setImages(Set<Image> images) {
this.images = images;
}
} }
@@ -1,6 +1,7 @@
package com.example.demo.entity; package com.example.demo.entity;
import jakarta.persistence.*; import jakarta.persistence.*;
import lombok.EqualsAndHashCode;
@Entity @Entity
@Table(name = "steps") @Table(name = "steps")
@@ -10,20 +11,21 @@ public class Step {
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id; private Integer id;
@Column(name = "recipe_id", nullable = false)
private Integer recipeId;
@Column(name = "step_number", nullable = false) @Column(name = "step_number", nullable = false)
private Integer stepNumber; private Integer stepNumber;
@Column(name = "instruction", nullable = false, columnDefinition = "TEXT") @Column(name = "instruction", nullable = false, columnDefinition = "TEXT")
private String instruction; private String instruction;
// No-arg constructor required by JPA @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "recipe_id", nullable = false)
@EqualsAndHashCode.Include
private Recipe recipe;
public Step() {} public Step() {}
public Step(Integer recipeId, Integer stepNumber, String instruction) { public Step(Recipe recipe, Integer stepNumber, String instruction) {
this.recipeId = recipeId; this.recipe = recipe;
this.stepNumber = stepNumber; this.stepNumber = stepNumber;
this.instruction = instruction; this.instruction = instruction;
} }
@@ -32,8 +34,13 @@ public class Step {
public Integer getId() { return id; } public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; } public void setId(Integer id) { this.id = id; }
public Integer getRecipeId() { return recipeId; } public Recipe getRecipe() {
public void setRecipeId(Integer recipeId) { this.recipeId = recipeId; } return recipe;
}
public void setRecipe(Recipe recipe) {
this.recipe = recipe;
}
public Integer getStepNumber() { return stepNumber; } public Integer getStepNumber() { return stepNumber; }
public void setStepNumber(Integer stepNumber) { this.stepNumber = stepNumber; } public void setStepNumber(Integer stepNumber) { this.stepNumber = stepNumber; }
@@ -7,15 +7,21 @@ import org.springframework.stereotype.Service;
import com.example.demo.dto.RecipeDto; import com.example.demo.dto.RecipeDto;
import com.example.demo.dto.UserDto; import com.example.demo.dto.UserDto;
import com.example.demo.dto.StepDto;
import com.example.demo.dto.ImageDto;
import com.example.demo.dto.RecipeIngredientDto; import com.example.demo.dto.RecipeIngredientDto;
import com.example.demo.entity.Image;
import com.example.demo.entity.Ingredient; import com.example.demo.entity.Ingredient;
import com.example.demo.entity.Recipe; import com.example.demo.entity.Recipe;
import com.example.demo.entity.RecipeIngredient; import com.example.demo.entity.RecipeIngredient;
import com.example.demo.entity.Step;
import com.example.demo.entity.User; import com.example.demo.entity.User;
import com.example.demo.exception.NotFoundException; import com.example.demo.exception.NotFoundException;
import com.example.demo.repository.ImageRepo;
import com.example.demo.repository.IngredientRepo; import com.example.demo.repository.IngredientRepo;
import com.example.demo.repository.RecipeIngredientRepo; import com.example.demo.repository.RecipeIngredientRepo;
import com.example.demo.repository.RecipeRepo; import com.example.demo.repository.RecipeRepo;
import com.example.demo.repository.StepRepo;
import com.example.demo.repository.UserRepo; import com.example.demo.repository.UserRepo;
import com.example.demo.service.RecipeService; import com.example.demo.service.RecipeService;
@@ -29,13 +35,17 @@ private RecipeRepo recipeRepository;
private IngredientRepo ingredientRepository; private IngredientRepo ingredientRepository;
private RecipeIngredientRepo recipeIngredientRepository; private RecipeIngredientRepo recipeIngredientRepository;
private UserRepo userRepository; private UserRepo userRepository;
private StepRepo stepRepository;
private ImageRepo imageRepository;
public RecipeServiceImpl(RecipeRepo recipeRepository, IngredientRepo ingredientRepository, RecipeIngredientRepo recipeIngredientRepository, UserRepo userRepository) { public RecipeServiceImpl(RecipeRepo recipeRepository, IngredientRepo ingredientRepository, RecipeIngredientRepo recipeIngredientRepository, UserRepo userRepository, StepRepo stepRepository, ImageRepo imageRepository) {
super(); super();
this.recipeRepository = recipeRepository; this.recipeRepository = recipeRepository;
this.ingredientRepository = ingredientRepository; this.ingredientRepository = ingredientRepository;
this.recipeIngredientRepository = recipeIngredientRepository; this.recipeIngredientRepository = recipeIngredientRepository;
this.userRepository = userRepository; this.userRepository = userRepository;
this.stepRepository = stepRepository;
this.imageRepository = imageRepository;
} }
public RecipeDto convertToDto(Recipe recipe) { public RecipeDto convertToDto(Recipe recipe) {
@@ -48,7 +58,21 @@ private UserRepo userRepository;
)) ))
.toList(); .toList();
List<StepDto> stepDtos = recipe.getSteps().stream()
.map(ri -> new StepDto(
ri.getStepNumber(),
ri.getInstruction()
))
.toList();
List<ImageDto> imageDtos = recipe.getImages().stream()
.map(ri -> new ImageDto(
ri.getImageUrl()
))
.toList();
UserDto userDto = new UserDto(recipe.getUser().getId()); UserDto userDto = new UserDto(recipe.getUser().getId());
return new RecipeDto( return new RecipeDto(
recipe.getTitle(), recipe.getTitle(),
recipe.getDescription(), recipe.getDescription(),
@@ -57,7 +81,9 @@ private UserRepo userRepository;
recipe.getServings(), recipe.getServings(),
userDto, userDto,
recipe.getStatus(), recipe.getStatus(),
ingredientDtos ingredientDtos,
stepDtos,
imageDtos
); );
} }
@@ -70,7 +96,7 @@ private UserRepo userRepository;
Recipe recipe = new Recipe( Recipe recipe = new Recipe(
dto.getTitle(), dto.getTitle(),
dto.getDescription(), dto.getDescription(),
dto.getPrepTimeMinutes(),+ dto.getPrepTimeMinutes(),
dto.getCookTimeMinutes(), dto.getCookTimeMinutes(),
dto.getServings(), dto.getServings(),
user, user,
@@ -99,11 +125,24 @@ private UserRepo userRepository;
recipe.getRecipeIngredients().add(ri); recipe.getRecipeIngredients().add(ri);
} }
recipeRepository.save(recipe);
return dto; if (dto.getSteps() != null) {
for (StepDto stepDto : dto.getSteps()) {
Step step = new Step(recipe, stepDto.getStepNumber(), stepDto.getInstruction());
recipe.getSteps().add(step);
}
}
if (dto.getImages() != null) {
for (ImageDto imageDto : dto.getImages()) {
Image image = new Image(recipe, imageDto.getImageUrl());
recipe.getImages().add(image);
}
}
Recipe saved = recipeRepository.save(recipe);
return convertToDto(saved);
} }
@Override @Override
@@ -143,6 +182,12 @@ private UserRepo userRepository;
List<RecipeIngredientDto> updatedIngredients = recipeDto.getIngredients(); List<RecipeIngredientDto> updatedIngredients = recipeDto.getIngredients();
List<RecipeIngredient> ingredientsToRemove = new ArrayList<>(); List<RecipeIngredient> ingredientsToRemove = new ArrayList<>();
List<StepDto> updatedSteps = recipeDto.getSteps();
List<Step> stepsToRemove = new ArrayList<>();
List<ImageDto> updatedImages = recipeDto.getImages();
List<Image> imagesToRemove = new ArrayList<>();
for (RecipeIngredient ri : existingRecipe.getRecipeIngredients()) { for (RecipeIngredient ri : existingRecipe.getRecipeIngredients()) {
boolean existsInUpdatedList = false; boolean existsInUpdatedList = false;
@@ -202,7 +247,64 @@ private UserRepo userRepository;
} }
} }
if(updatedSteps != null) {
// find steps that weren't included
for (Step step : existingRecipe.getSteps()) {
boolean existsInUpdatedList = updatedSteps.stream()
.anyMatch(dto -> dto.getStepNumber().equals(step.getStepNumber()));
if (!existsInUpdatedList) stepsToRemove.add(step);
}
// delete those steps
existingRecipe.getSteps().removeAll(stepsToRemove);
// go through updated steps
for (StepDto stepDto : updatedSteps) {
// find matching step by step number
Step existingStep = existingRecipe.getSteps().stream()
.filter(s -> s.getStepNumber().equals(stepDto.getStepNumber()))
.findFirst()
.orElse(null);
// if there's a match update the instruction string
if (existingStep != null) {
existingStep.setInstruction(stepDto.getInstruction());
}
// if no match then make a whole new step
else {
Step newStep = new Step(existingRecipe, stepDto.getStepNumber(), stepDto.getInstruction());
existingRecipe.getSteps().add(newStep);
}
}
}
//same process as above just with images instead
if (updatedImages != null) {
for (Image image : existingRecipe.getImages()) {
boolean existsInUpdatedList = updatedImages.stream()
.anyMatch(dto -> dto.getImageUrl().equals(image.getImageUrl()));
if (!existsInUpdatedList) imagesToRemove.add(image);
}
existingRecipe.getImages().removeAll(imagesToRemove);
for (ImageDto imageDto : updatedImages) {
Image existingImage = existingRecipe.getImages().stream()
.filter(img -> img.getImageUrl().equals(imageDto.getImageUrl()))
.findFirst()
.orElse(null);
if (existingImage != null) {
existingImage.setImageUrl(imageDto.getImageUrl());
}
else {
Image newImage = new Image(existingRecipe, imageDto.getImageUrl());
existingRecipe.getImages().add(newImage);
}
}
}
recipeRepository.save(existingRecipe); recipeRepository.save(existingRecipe);
return convertToDto(existingRecipe); return convertToDto(existingRecipe);
} }