mirror of https://github.com/kcal-app/kcal.git
Add support for recording recipe volume (WIP)
This offers no practical value yet -- ultimately it will be used to support volume-based servings for recipes with a total volume set.
This commit is contained in:
parent
6b68d61385
commit
809e3ca7d7
|
@ -201,6 +201,7 @@ class RecipeController extends Controller
|
|||
'description_delta' => $input['description_delta'],
|
||||
'servings' => (int) $input['servings'],
|
||||
'weight' => $input['weight'],
|
||||
'volume' => Number::floatFromString($input['volume']),
|
||||
'time_prep' => (int) $input['time_prep'],
|
||||
'time_cook' => (int) $input['time_cook'],
|
||||
'source' => $input['source'],
|
||||
|
|
|
@ -24,7 +24,8 @@ class UpdateRecipeRequest extends FormRequest
|
|||
'servings' => ['required', 'numeric'],
|
||||
'time_prep' => ['nullable', 'numeric'],
|
||||
'time_cook' => ['nullable', 'numeric'],
|
||||
'weight' => ['nullable', 'numeric'],
|
||||
'weight' => ['nullable', 'numeric', 'min:0'],
|
||||
'volume' => ['nullable', new StringIsPositiveDecimalOrFraction],
|
||||
'source' => ['nullable', 'string'],
|
||||
'ingredients.amount' => ['required', 'array', new ArrayNotEmpty],
|
||||
'ingredients.amount.*' => ['required_with:ingredients.id.*', 'nullable', new StringIsPositiveDecimalOrFraction],
|
||||
|
|
|
@ -36,6 +36,8 @@ class RecipeSchema extends SchemaProvider
|
|||
'servings' => $resource->servings,
|
||||
'weight' => $resource->weight,
|
||||
'serving_weight' => $resource->serving_weight,
|
||||
'volume' => $resource->volume,
|
||||
'volumeFormatted' => $resource->volume_formatted,
|
||||
'units_supported' => $resource->units_supported->pluck('label'),
|
||||
'caloriesPerServing' => $resource->caloriesPerServing(),
|
||||
'carbohydratesPerServing' => $resource->carbohydratesPerServing(),
|
||||
|
|
|
@ -7,6 +7,7 @@ use App\Models\Traits\Ingredient;
|
|||
use App\Models\Traits\Journalable;
|
||||
use App\Models\Traits\Sluggable;
|
||||
use App\Models\Traits\Taggable;
|
||||
use App\Support\Number;
|
||||
use App\Support\Nutrients;
|
||||
use ElasticScoutDriverPlus\QueryDsl;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
@ -104,6 +105,7 @@ final class Recipe extends Model implements HasMedia
|
|||
'source',
|
||||
'servings',
|
||||
'weight',
|
||||
'volume',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -114,6 +116,7 @@ final class Recipe extends Model implements HasMedia
|
|||
'time_prep' => 'int',
|
||||
'time_cook' => 'int',
|
||||
'weight' => 'float',
|
||||
'volume' => 'float',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -133,6 +136,7 @@ final class Recipe extends Model implements HasMedia
|
|||
*/
|
||||
protected $appends = [
|
||||
'serving_weight',
|
||||
'volume_formatted',
|
||||
'time_total',
|
||||
'units_supported'
|
||||
];
|
||||
|
@ -169,6 +173,17 @@ final class Recipe extends Model implements HasMedia
|
|||
return round($this->weight / $this->servings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the volume as a formatted string (e.g. 0.5 = 1/2).
|
||||
*/
|
||||
public function getVolumeFormattedAttribute(): ?string {
|
||||
$result = null;
|
||||
if (!empty($this->volume)) {
|
||||
$result = Number::rationalStringFromFloat($this->volume);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ingredients list (ingredient amounts and separators).
|
||||
*/
|
||||
|
|
|
@ -23,6 +23,7 @@ class RecipeFactory extends Factory
|
|||
public function definition(): array
|
||||
{
|
||||
$description = htmlspecialchars($this->faker->realText(500));
|
||||
$volumes = [1/4, 1/3, 1/2, 2/3, 3/4, 1, 1 + 1/2, 1 + 3/4, 2, 2 + 1/2, 3, 3 + 1/2, 4, 5];
|
||||
return [
|
||||
'name' => Words::randomWords(Arr::random(['npan', 'npn', 'anpn'])),
|
||||
'description' => "<p>{$description}</p>",
|
||||
|
@ -31,7 +32,8 @@ class RecipeFactory extends Factory
|
|||
'time_cook' => $this->faker->numberBetween(0, 90),
|
||||
'source' => $this->faker->optional()->url,
|
||||
'servings' => $this->faker->numberBetween(1, 10),
|
||||
'weight' => $this->faker->randomFloat(1, 60, 2000),
|
||||
'weight' => $this->faker->optional()->randomFloat(1, 60, 2000),
|
||||
'volume' => $this->faker->optional()->randomElement($volumes),
|
||||
'tags' => Words::randomWords(Arr::random(['a', 'aa', 'aaa']), TRUE),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ class CreateRecipesTable extends Migration
|
|||
$table->string('source')->nullable();
|
||||
$table->unsignedInteger('servings');
|
||||
$table->unsignedFloat('weight')->nullable();
|
||||
//$table->decimal('volume', 10, 8)->unsigned()->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddVolumeToRecipes extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('recipes', function (Blueprint $table) {
|
||||
$table->decimal('volume', 10, 8)->unsigned()->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('recipes', function (Blueprint $table) {
|
||||
$table->dropColumn('volume');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
<!-- Weight -->
|
||||
<div class="flex-auto">
|
||||
<x-inputs.label for="weight" value="Total weight (g)" />
|
||||
<x-inputs.label for="weight" value="Weight (g)" />
|
||||
|
||||
<x-inputs.input name="weight"
|
||||
type="number"
|
||||
|
@ -42,9 +42,19 @@
|
|||
:value="old('weight', $recipe->weight)" />
|
||||
</div>
|
||||
|
||||
<!-- Volume -->
|
||||
<div class="flex-auto">
|
||||
<x-inputs.label for="volume" value="Volume (cups)" />
|
||||
|
||||
<x-inputs.input name="volume"
|
||||
type="text"
|
||||
class="block mt-1 w-full"
|
||||
:value="old('volume', $recipe->volume_formatted)" />
|
||||
</div>
|
||||
|
||||
<!-- Prep Time -->
|
||||
<div class="flex-auto">
|
||||
<x-inputs.label for="time_prep" value="Prep time (minutes)" />
|
||||
<x-inputs.label for="time_prep" value="Prep time (min.)" />
|
||||
|
||||
<x-inputs.input name="time_prep"
|
||||
type="number"
|
||||
|
@ -56,7 +66,7 @@
|
|||
|
||||
<!-- Cooke Time -->
|
||||
<div class="flex-auto">
|
||||
<x-inputs.label for="time_cook" value="Cook time (minutes)" />
|
||||
<x-inputs.label for="time_cook" value="Cook time (min.)" />
|
||||
|
||||
<x-inputs.input name="time_cook"
|
||||
type="number"
|
||||
|
|
Loading…
Reference in New Issue