mirror of https://github.com/kcal-app/kcal.git
Improve error messages in recipe update
This commit is contained in:
parent
c9ef13a0d4
commit
dbee32dc14
|
@ -2,19 +2,16 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\UpdateRecipeRequest;
|
||||
use App\Models\Food;
|
||||
use App\Models\IngredientAmount;
|
||||
use App\Models\Recipe;
|
||||
use App\Models\RecipeSeparator;
|
||||
use App\Models\RecipeStep;
|
||||
use App\Rules\ArrayNotEmpty;
|
||||
use App\Rules\StringIsDecimalOrFraction;
|
||||
use App\Rules\UsesIngredientTrait;
|
||||
use App\Support\Number;
|
||||
use App\Support\Nutrients;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
|
@ -44,12 +41,9 @@ class RecipeController extends Controller
|
|||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
public function store(UpdateRecipeRequest $request): RedirectResponse
|
||||
{
|
||||
return $this->update($request, new Recipe());
|
||||
}
|
||||
|
@ -187,50 +181,11 @@ class RecipeController extends Controller
|
|||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \App\Models\Recipe $recipe
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function update(Request $request, Recipe $recipe): RedirectResponse
|
||||
public function update(UpdateRecipeRequest $request, Recipe $recipe): RedirectResponse
|
||||
{
|
||||
$input = $request->validate([
|
||||
'name' => ['required', 'string'],
|
||||
'description' => ['nullable', 'string'],
|
||||
'description_delta' => ['nullable', 'string'],
|
||||
'image' => ['nullable', 'file', 'mimes:jpg,png,gif'],
|
||||
'remove_image' => ['nullable', 'boolean'],
|
||||
'servings' => ['required', 'numeric'],
|
||||
'time_prep' => ['nullable', 'numeric'],
|
||||
'time_cook' => ['nullable', 'numeric'],
|
||||
'weight' => ['nullable', 'numeric'],
|
||||
'source' => ['nullable', 'string'],
|
||||
'ingredients.amount' => ['required', 'array', new ArrayNotEmpty],
|
||||
'ingredients.amount.*' => ['required_with:ingredients.id.*', 'nullable', new StringIsDecimalOrFraction],
|
||||
'ingredients.unit' => ['required', 'array'],
|
||||
'ingredients.unit.*' => ['required_with:ingredients.id.*'],
|
||||
'ingredients.detail' => ['required', 'array'],
|
||||
'ingredients.detail.*' => ['nullable', 'string'],
|
||||
'ingredients.id' => ['required', 'array', new ArrayNotEmpty],
|
||||
'ingredients.id.*' => 'required_with:ingredients.amount.*|nullable',
|
||||
'ingredients.type' => ['required', 'array', new ArrayNotEmpty],
|
||||
'ingredients.type.*' => ['required_with:ingredients.id.*', 'nullable', new UsesIngredientTrait()],
|
||||
'ingredients.key' => ['nullable', 'array'],
|
||||
'ingredients.key.*' => ['nullable', 'int'],
|
||||
'ingredients.weight' => ['required', 'array', new ArrayNotEmpty],
|
||||
'ingredients.weight.*' => ['required', 'int'],
|
||||
'separators.key' => ['nullable', 'array'],
|
||||
'separators.key.*' => ['nullable', 'int'],
|
||||
'separators.weight' => ['nullable', 'array'],
|
||||
'separators.weight.*' => ['required', 'int'],
|
||||
'separators.text' => ['nullable', 'array'],
|
||||
'separators.text.*' => ['nullable', 'string'],
|
||||
'steps.step' => ['required', 'array', new ArrayNotEmpty],
|
||||
'steps.step.*' => ['nullable', 'string'],
|
||||
'steps.key' => ['nullable', 'array'],
|
||||
]);
|
||||
$input = $request->validated();
|
||||
|
||||
// Validate that no ingredients are recursive.
|
||||
// TODO: refactor as custom validator.
|
||||
|
@ -285,7 +240,7 @@ class RecipeController extends Controller
|
|||
->usingFileName("{$recipe->slug}.{$file->extension()}")
|
||||
->toMediaCollection();
|
||||
}
|
||||
elseif (isset($input['remove_image']) && (bool) $input['remove_image']) {
|
||||
elseif (isset($input['remove_image']) && $input['remove_image']) {
|
||||
$recipe->clearMediaCollection();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use App\Rules\ArrayNotEmpty;
|
||||
use App\Rules\StringIsDecimalOrFraction;
|
||||
use App\Rules\UsesIngredientTrait;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateRecipeRequest extends FormRequest
|
||||
{
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['required', 'string'],
|
||||
'description' => ['nullable', 'string'],
|
||||
'description_delta' => ['nullable', 'string'],
|
||||
'image' => ['nullable', 'file', 'mimes:jpg,png,gif'],
|
||||
'remove_image' => ['nullable', 'boolean'],
|
||||
'servings' => ['required', 'numeric'],
|
||||
'time_prep' => ['nullable', 'numeric'],
|
||||
'time_cook' => ['nullable', 'numeric'],
|
||||
'weight' => ['nullable', 'numeric'],
|
||||
'source' => ['nullable', 'string'],
|
||||
'ingredients.amount' => ['required', 'array', new ArrayNotEmpty],
|
||||
'ingredients.amount.*' => ['required_with:ingredients.id.*', 'nullable', new StringIsDecimalOrFraction],
|
||||
'ingredients.unit' => ['required', 'array'],
|
||||
'ingredients.unit.*' => ['required_with:ingredients.id.*'],
|
||||
'ingredients.detail' => ['required', 'array'],
|
||||
'ingredients.detail.*' => ['nullable', 'string'],
|
||||
'ingredients.id' => ['required', 'array', new ArrayNotEmpty],
|
||||
'ingredients.id.*' => ['required_with:ingredients.amount.*', 'required_with:ingredients.unit.*', 'nullable'],
|
||||
'ingredients.type' => ['required', 'array', new ArrayNotEmpty],
|
||||
'ingredients.type.*' => ['required_with:ingredients.id.*', 'nullable', new UsesIngredientTrait()],
|
||||
'ingredients.key' => ['nullable', 'array'],
|
||||
'ingredients.key.*' => ['nullable', 'int'],
|
||||
'ingredients.weight' => ['required', 'array', new ArrayNotEmpty],
|
||||
'ingredients.weight.*' => ['required', 'int'],
|
||||
'separators.key' => ['nullable', 'array'],
|
||||
'separators.key.*' => ['nullable', 'int'],
|
||||
'separators.weight' => ['nullable', 'array'],
|
||||
'separators.weight.*' => ['required', 'int'],
|
||||
'separators.text' => ['nullable', 'array'],
|
||||
'separators.text.*' => ['nullable', 'string'],
|
||||
'steps.step' => ['required', 'array', new ArrayNotEmpty],
|
||||
'steps.step.*' => ['nullable', 'string'],
|
||||
'steps.key' => ['nullable', 'array'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'ingredients.id.*.required_with' => 'Missing :attribute in Ingredients.',
|
||||
'ingredients.amount.*.required_with' => 'Missing :attribute in Ingredients.',
|
||||
'ingredients.unit.*.required_with' => 'Missing :attribute in Ingredients.',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function attributes(): array
|
||||
{
|
||||
return [
|
||||
'ingredients.id.*' => 'ingredient name',
|
||||
'ingredients.amount.*' => 'ingredient amount',
|
||||
'ingredients.unit.*' => 'ingredient unit',
|
||||
];
|
||||
}
|
||||
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -1,3 +1,5 @@
|
|||
@props(['hasError' => false])
|
||||
|
||||
<div x-data="picker()">
|
||||
<div>
|
||||
<div>
|
||||
|
@ -11,7 +13,7 @@
|
|||
x-ref="ingredients_type"/>
|
||||
<x-inputs.input type="text"
|
||||
name="ingredients[name][]"
|
||||
class="w-full"
|
||||
class="w-full{{ $hasError ? ' border-red-600' : '' }}"
|
||||
value="{{ $defaultName ?? '' }}"
|
||||
placeholder="Search..."
|
||||
autocomplete="off"
|
||||
|
|
|
@ -1,5 +1,13 @@
|
|||
@php($key = $key ?? null)
|
||||
@error("ingredients.amount.{$key}")
|
||||
@php($amount_error = 'border-red-600')
|
||||
@enderror
|
||||
@error("ingredients.unit.{$key}")
|
||||
@php($unit_error = 'border-red-600')
|
||||
@enderror
|
||||
|
||||
<div class="ingredient draggable">
|
||||
<x-inputs.input type="hidden" name="ingredients[key][]" :value="$key ?? null" />
|
||||
<x-inputs.input type="hidden" name="ingredients[key][]" :value="$key" />
|
||||
<x-inputs.input type="hidden" name="ingredients[weight][]" :value="$weight ?? null" />
|
||||
<div class="flex items-center space-x-2">
|
||||
<div class="flex flex-col space-y-4 md:flex-row md:space-x-4 md:space-y-0 w-full">
|
||||
|
@ -11,16 +19,17 @@
|
|||
<div class="w-full">
|
||||
<x-ingredient-picker :default-id="$ingredient_id ?? null"
|
||||
:default-type="$ingredient_type ?? null"
|
||||
:default-name="$ingredient_name ?? null" />
|
||||
:default-name="$ingredient_name ?? null"
|
||||
:has-error="(isset($amount) || isset($unit)) && empty($ingredient_id)"/>
|
||||
</div>
|
||||
<x-inputs.input name="ingredients[amount][]"
|
||||
type="text"
|
||||
size="5"
|
||||
placeholder="Amount"
|
||||
class="block"
|
||||
class="block {{ $amount_error ?? null }}"
|
||||
:value="$amount ?? null" />
|
||||
<x-inputs.select name="ingredients[unit][]"
|
||||
class="block"
|
||||
class="block {{ $unit_error ?? null }}"
|
||||
:options="$units_supported ?? []"
|
||||
:selectedValue="$unit ?? null">
|
||||
<option value="" selected>Unit</option>
|
||||
|
|
Loading…
Reference in New Issue