From ab28efba527cec9ae93d0254fc055c15b58ca874 Mon Sep 17 00:00:00 2001 From: "Christopher C. Wells" Date: Fri, 15 Jan 2021 20:14:35 -0800 Subject: [PATCH] Add recipe edit support (WIP) Not possible to remove existing ingredients or steps. --- app/Http/Controllers/RecipeController.php | 166 ++++++++++++---------- resources/views/recipes/edit.blade.php | 123 ++++++++++++++++ resources/views/recipes/index.blade.php | 7 + 3 files changed, 217 insertions(+), 79 deletions(-) create mode 100644 resources/views/recipes/edit.blade.php diff --git a/app/Http/Controllers/RecipeController.php b/app/Http/Controllers/RecipeController.php index e56309c..f8846ec 100644 --- a/app/Http/Controllers/RecipeController.php +++ b/app/Http/Controllers/RecipeController.php @@ -35,85 +35,20 @@ class RecipeController extends Controller */ public function create(): View { - $foods = Food::all(['id', 'name', 'detail'])->sortBy('name')->collect() - ->map(function ($food) { - return [ - 'value' => $food->id, - 'label' => "{$food->name}" . ($food->detail ? ", {$food->detail}" : ""), - ]; - }); - return view('recipes.create') - ->with('foods', $foods) - ->with('food_units', new Collection([ - ['value' => 'tsp', 'label' => 'tsp.'], - ['value' => 'tbsp', 'label' => 'tbsp.'], - ['value' => 'cup', 'label' => 'cup'], - ['value' => 'grams', 'label' => 'g'], - ])); + return $this->edit(new Recipe()); } /** * Store a newly created resource in storage. * - * @param \Illuminate\Http\Request $request + * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\RedirectResponse + * + * @throws \Throwable */ public function store(Request $request): RedirectResponse { - $input = $request->validate([ - 'name' => 'required|string', - 'description' => 'nullable|string', - 'servings' => 'required|numeric', - 'foods_amount' => ['required', 'array', new ArrayNotEmpty], - 'foods_amount.*' => ['required_with:foods.*', 'nullable', new StringIsDecimalOrFraction], - 'foods_unit' => ['required', 'array'], - 'foods_unit.*' => 'nullable|string', - 'foods' => ['required', 'array', new ArrayNotEmpty], - 'foods.*' => 'required_with:foods_amount.*|nullable|exists:App\Models\Food,id', - 'steps' => ['required', 'array', new ArrayNotEmpty], - 'steps.*' => 'nullable|string', - ]); - - $recipe = new Recipe([ - 'name' => $input['name'], - 'description' => $input['description'], - 'servings' => (int) $input['servings'], - ]); - - try { - DB::transaction(function () use ($recipe, $input) { - if (!$recipe->save()) { - return; - } - - $food_amounts = []; - $weight = 0; - foreach (array_filter($input['foods_amount']) as $key => $amount) { - $food_amounts[$key] = new FoodAmount([ - 'amount' => Number::floatFromString($amount), - 'unit' => $input['foods_unit'][$key], - 'weight' => $weight++, - ]); - $food_amounts[$key]->food()->associate($input['foods'][$key]); - } - $recipe->foodAmounts()->saveMany($food_amounts); - - $steps = []; - $number = 1; - foreach (array_filter($input['steps']) as $step) { - $steps[] = new RecipeStep([ - 'number' => $number++, - 'step' => $step, - ]); - } - $recipe->foodAmounts()->saveMany($steps); - }); - } catch (\Exception $e) { - DB::rollBack(); - return back()->withInput()->withErrors("Failed to add recipe due to database error: {$e->getMessage()}."); - } - - return back()->with('message', "Recipe {$recipe->name} added!"); + return $this->update($request, new Recipe()); } /** @@ -130,24 +65,97 @@ class RecipeController extends Controller /** * Show the form for editing the specified resource. * - * @param \App\Models\Recipe $recipe - * @return \Illuminate\Http\Response + * @param \App\Models\Recipe $recipe + * @return \Illuminate\Contracts\View\View */ - public function edit(Recipe $recipe) + public function edit(Recipe $recipe): View { - // + $foods = Food::all(['id', 'name', 'detail'])->sortBy('name')->collect() + ->map(function ($food) { + return [ + 'value' => $food->id, + 'label' => "{$food->name}" . ($food->detail ? ", {$food->detail}" : ""), + ]; + }); + return view('recipes.edit') + ->with('recipe', $recipe) + ->with('foods', $foods) + ->with('food_units', new Collection([ + ['value' => 'tsp', 'label' => 'tsp.'], + ['value' => 'tbsp', 'label' => 'tbsp.'], + ['value' => 'cup', 'label' => 'cup'], + ['value' => 'oz', 'label' => 'oz'], + ['value' => 'grams', 'label' => 'g'], + ])); } /** * Update the specified resource in storage. * - * @param \Illuminate\Http\Request $request - * @param \App\Models\Recipe $recipe - * @return \Illuminate\Http\Response + * @param \Illuminate\Http\Request $request + * @param \App\Models\Recipe $recipe + * + * @return \Illuminate\Http\RedirectResponse + * + * @throws \Throwable */ - public function update(Request $request, Recipe $recipe) + public function update(Request $request, Recipe $recipe): RedirectResponse { - // + $input = $request->validate([ + 'name' => 'required|string', + 'description' => 'nullable|string', + 'servings' => 'required|numeric', + 'foods_amount' => ['required', 'array', new ArrayNotEmpty], + 'foods_amount.*' => ['required_with:foods.*', 'nullable', new StringIsDecimalOrFraction], + 'foods_unit' => ['required', 'array'], + 'foods_unit.*' => 'nullable|string', + 'foods' => ['required', 'array', new ArrayNotEmpty], + 'foods.*' => 'required_with:foods_amount.*|nullable|exists:App\Models\Food,id', + 'steps' => ['required', 'array', new ArrayNotEmpty], + 'steps.*' => 'nullable|string', + ]); + + $recipe->fill([ + 'name' => $input['name'], + 'description' => $input['description'], + 'servings' => (int) $input['servings'], + ]); + + try { + DB::transaction(function () use ($recipe, $input) { + if (!$recipe->save()) { + return; + } + + $food_amounts = []; + $weight = 0; + // TODO: Handle removals. + foreach (array_filter($input['foods_amount']) as $key => $amount) { + $food_amounts[$key] = $recipe->foodAmounts[$key] ?? new FoodAmount(); + $food_amounts[$key]->fill([ + 'amount' => Number::floatFromString($amount), + 'unit' => $input['foods_unit'][$key], + 'weight' => $weight++, + ]); + $food_amounts[$key]->food()->associate($input['foods'][$key]); + } + $recipe->foodAmounts()->saveMany($food_amounts); + + $steps = []; + $number = 1; + // TODO: Handle removals. + foreach (array_filter($input['steps']) as $key => $step) { + $steps[$key] = $recipe->steps[$key] ?? new RecipeStep(); + $steps[$key]->fill(['number' => $number++, 'step' => $step]); + } + $recipe->foodAmounts()->saveMany($steps); + }); + } catch (\Exception $e) { + DB::rollBack(); + return back()->withInput()->withErrors("Failed to updated recipe due to database error: {$e->getMessage()}."); + } + + return back()->with('message', "Recipe {$recipe->name} updated!"); } /** diff --git a/resources/views/recipes/edit.blade.php b/resources/views/recipes/edit.blade.php new file mode 100644 index 0000000..d73eacd --- /dev/null +++ b/resources/views/recipes/edit.blade.php @@ -0,0 +1,123 @@ + + +

+ {{ ($recipe->exists ? "Edit {$recipe->name}" : 'Add Recipe') }} +

+
+ +
+
+ @if(session()->has('message')) +
+ {{ session()->get('message') }} +
+ @endif + + @if ($errors->any()) +
+ @foreach ($errors->all() as $error) +
+ {{ $error }} +
+ @endforeach +
+ @endif + +
+
+
+ @if ($recipe->exists)@method('put')@endif + @csrf +
+
+ +
+ + + +
+ + +
+ + + +
+
+ + +
+ + + +
+
+ + +

Ingredients

+ @for($i = 0; $i < 20; $i++) + @php + if (isset($recipe->foodAmounts[$i])) { + $foodAmount = $recipe->foodAmounts[$i]; + $amount = \App\Support\Number::fractionStringFromFloat($foodAmount->amount); + $unit = $foodAmount->unit; + $food_id = $foodAmount->food->id; + } else { + $foodAmount = new \App\Models\FoodAmount(); + $amount = $food_id = $unit = null; + } + @endphp +
+ + + + + + + +
+ @endfor + + +

Steps

+ @for($i = 0; $i < 20; $i++) + @php($step = $recipe->steps[$i] ?? new \App\Models\RecipeStep()) +
+
{{ $i + 1 }}
+ +
+ @endfor + +
+ + {{ ($recipe->exists ? 'Save' : 'Add') }} + +
+
+
+
+
+
+
diff --git a/resources/views/recipes/index.blade.php b/resources/views/recipes/index.blade.php index 4af243d..c4fbc15 100644 --- a/resources/views/recipes/index.blade.php +++ b/resources/views/recipes/index.blade.php @@ -17,6 +17,13 @@
@foreach ($recipes as $recipe)