slug)->first(); if ($recipe) { // Update existing recipe $this->updateRecipe($recipe, $recipeDto); } else { // Create new recipe $recipe = $this->createRecipe($recipeDto); } // Sync related data $this->syncIngredients($recipe, $recipeDto); $this->syncSteps($recipe, $recipeDto); $this->syncImages($recipe, $recipeDto); $this->syncNutrition($recipe, $recipeDto); $this->syncTags($recipe, $recipeDto); return $recipe->load(['ingredients', 'steps', 'images', 'nutrition', 'tags']); }); } /** * Create a new recipe * * @param RecipeDto $dto * @return Recipe */ protected function createRecipe(RecipeDto $dto): Recipe { return Recipe::create([ 'title' => $dto->title, 'slug' => $dto->slug, 'description' => $dto->description, 'prep_time' => $dto->prepTime, 'cook_time' => $dto->cookTime, 'total_time' => $dto->totalTime, 'servings' => $dto->servings, 'difficulty' => $dto->difficulty, 'cuisine' => $dto->cuisine, 'category' => $dto->category, 'calories' => $dto->calories, 'featured_image' => $dto->featuredImage, 'video_url' => $dto->videoUrl, 'status' => $dto->status ?? 0, 'views_count' => $dto->viewsCount ?? 0, 'rating_avg' => $dto->ratingAvg, 'rating_count' => $dto->ratingCount ?? 0, ]); } /** * Update an existing recipe * * @param Recipe $recipe * @param RecipeDto $dto * @return void */ protected function updateRecipe(Recipe $recipe, RecipeDto $dto): void { $updates = []; // Check each field for changes if ($dto->title !== null && $dto->title !== $recipe->title) { $updates['title'] = $dto->title; } if ($dto->description !== null && $dto->description !== $recipe->description) { $updates['description'] = $dto->description; } if ($dto->prepTime !== null && $dto->prepTime !== $recipe->prep_time) { $updates['prep_time'] = $dto->prepTime; } if ($dto->cookTime !== null && $dto->cookTime !== $recipe->cook_time) { $updates['cook_time'] = $dto->cookTime; } if ($dto->totalTime !== null && $dto->totalTime !== $recipe->total_time) { $updates['total_time'] = $dto->totalTime; } if ($dto->servings !== null && $dto->servings !== $recipe->servings) { $updates['servings'] = $dto->servings; } if ($dto->difficulty !== null && $dto->difficulty !== $recipe->difficulty) { $updates['difficulty'] = $dto->difficulty; } if ($dto->cuisine !== null && $dto->cuisine !== $recipe->cuisine) { $updates['cuisine'] = $dto->cuisine; } if ($dto->category !== null && $dto->category !== $recipe->category) { $updates['category'] = $dto->category; } if ($dto->calories !== null && $dto->calories !== $recipe->calories) { $updates['calories'] = $dto->calories; } if ($dto->featuredImage !== null && $dto->featuredImage !== $recipe->featured_image) { $updates['featured_image'] = $dto->featuredImage; } if ($dto->videoUrl !== null && $dto->videoUrl !== $recipe->video_url) { $updates['video_url'] = $dto->videoUrl; } if ($dto->status !== null && $dto->status !== $recipe->status) { $updates['status'] = $dto->status; } if (!empty($updates)) { $recipe->update($updates); } } /** * Sync recipe ingredients * * @param Recipe $recipe * @param RecipeDto $dto * @return void */ protected function syncIngredients(Recipe $recipe, RecipeDto $dto): void { if (empty($dto->ingredients)) { return; } $syncData = []; foreach ($dto->ingredients as $ingredientDto) { $slug = $ingredientDto->ingredientSlug; if (!$slug) { $slug = Str::slug($ingredientDto->ingredientName); } // Find or create ingredient $ingredient = Ingredient::firstOrCreate( ['slug' => $slug], [ 'name' => $ingredientDto->ingredientName, 'category' => $ingredientDto->ingredientCategory, ] ); // Prepare pivot data $syncData[$ingredient->id] = [ 'quantity' => $ingredientDto->quantity, 'unit' => $ingredientDto->unit, 'notes' => $ingredientDto->notes, 'sort_order' => $ingredientDto->sortOrder ?? 0, ]; } $recipe->ingredients()->sync($syncData); } /** * Sync recipe steps * * @param Recipe $recipe * @param RecipeDto $dto * @return void */ protected function syncSteps(Recipe $recipe, RecipeDto $dto): void { if (empty($dto->steps)) { // Remove all steps if none provided $recipe->steps()->delete(); return; } // Delete existing steps and recreate $recipe->steps()->delete(); foreach ($dto->steps as $stepDto) { RecipeStep::create([ 'recipe_id' => $recipe->id, 'step_number' => $stepDto->stepNumber, 'title' => $stepDto->title, 'instruction' => $stepDto->instruction, 'image' => $stepDto->image, 'time_estimate' => $stepDto->timeEstimate, 'sort_order' => $stepDto->sortOrder ?? 0, ]); } } /** * Sync recipe images * * @param Recipe $recipe * @param RecipeDto $dto * @return void */ protected function syncImages(Recipe $recipe, RecipeDto $dto): void { if (empty($dto->images)) { return; } // Delete existing images and recreate $recipe->images()->delete(); foreach ($dto->images as $imageDto) { RecipeImage::create([ 'recipe_id' => $recipe->id, 'image_path' => $imageDto->imagePath, 'title' => $imageDto->title, 'description' => $imageDto->description, 'is_featured' => $imageDto->isFeatured ?? false, 'sort_order' => $imageDto->sortOrder ?? 0, ]); } } /** * Sync recipe nutrition * * @param Recipe $recipe * @param RecipeDto $dto * @return void */ protected function syncNutrition(Recipe $recipe, RecipeDto $dto): void { if ($dto->nutrition === null) { return; } RecipeNutrition::updateOrCreate( ['recipe_id' => $recipe->id], [ 'calories' => $dto->nutrition->calories, 'protein' => $dto->nutrition->protein, 'carbohydrates' => $dto->nutrition->carbohydrates, 'fat' => $dto->nutrition->fat, 'saturated_fat' => $dto->nutrition->saturatedFat, 'fiber' => $dto->nutrition->fiber, 'sugar' => $dto->nutrition->sugar, 'sodium' => $dto->nutrition->sodium, 'cholesterol' => $dto->nutrition->cholesterol, ] ); } /** * Sync recipe tags * * @param Recipe $recipe * @param RecipeDto $dto * @return void */ protected function syncTags(Recipe $recipe, RecipeDto $dto): void { if (empty($dto->tags)) { return; } $tagIds = []; foreach ($dto->tags as $tagDto) { // Find or create tag $tag = Tag::firstOrCreate( ['slug' => $tagDto->slug], ['name' => $tagDto->name] ); $tagIds[] = $tag->id; } $recipe->tags()->sync($tagIds); } }