Add handling for recipes by weight in ingredient amounts and journal entries

This commit is contained in:
Christopher C. Wells 2021-02-11 20:25:36 -08:00
parent 836c40abf2
commit 33a8591c72
5 changed files with 75 additions and 18 deletions

View File

@ -135,7 +135,12 @@ class JournalEntryController extends Controller
elseif ($ingredient['type'] == Recipe::class) { elseif ($ingredient['type'] == Recipe::class) {
$item = Recipe::whereId($ingredient['id'])->first(); $item = Recipe::whereId($ingredient['id'])->first();
foreach (Nutrients::$all as $nutrient) { foreach (Nutrients::$all as $nutrient) {
$entries[$entry_key]->{$nutrient['value']} += $item->{"{$nutrient['value']}PerServing"}() * Number::floatFromString($ingredient['amount']); $entries[$entry_key]->{$nutrient['value']} += Nutrients::calculateRecipeNutrientAmount(
$item,
$nutrient['value'],
Number::floatFromString($ingredient['amount']),
$ingredient['unit']
);
} }
$entries[$entry_key]->recipes->add($item); $entries[$entry_key]->recipes->add($item);
} }
@ -144,14 +149,14 @@ class JournalEntryController extends Controller
} }
// Update summary // Update summary
if (empty($item->serving_unit) && empty($item->serving_unit_name)) { $unit = $ingredient['unit'];
$unit = null; if ($item instanceof Food) {
} if (empty($item->serving_unit) && empty($item->serving_unit_name)) {
elseif (!empty($item->serving_unit_name)) { $unit = null;
$unit = $item->serving_unit_formatted; }
} elseif (!empty($item->serving_unit_name)) {
else { $unit = $item->serving_unit_formatted;
$unit = $ingredient['unit']; }
} }
$entries[$entry_key]->summary .= (!empty($entries[$entry_key]->summary) ? ', ' : null); $entries[$entry_key]->summary .= (!empty($entries[$entry_key]->summary) ? ', ' : null);
$entries[$entry_key]->summary .= "{$ingredient['amount']} {$unit} {$item->name}"; $entries[$entry_key]->summary .= "{$ingredient['amount']} {$unit} {$item->name}";

View File

@ -146,7 +146,12 @@ final class IngredientAmount extends Model
$this->amount, $this->amount,
$this->unit $this->unit
), ),
Recipe::class => $this->ingredient->{"{$method}PerServing"}() * $this->amount, Recipe::class => Nutrients::calculateRecipeNutrientAmount(
$this->ingredient,
$method,
$this->amount,
$this->unit
),
default => 0 default => 0
}; };
} }

View File

@ -53,6 +53,9 @@ use Spatie\Tags\HasTags;
* @method static \Illuminate\Database\Eloquent\Builder|Recipe withAllTagsOfAnyType($tags) * @method static \Illuminate\Database\Eloquent\Builder|Recipe withAllTagsOfAnyType($tags)
* @method static \Illuminate\Database\Eloquent\Builder|Recipe withAnyTags($tags, ?string $type = null) * @method static \Illuminate\Database\Eloquent\Builder|Recipe withAnyTags($tags, ?string $type = null)
* @method static \Illuminate\Database\Eloquent\Builder|Recipe withAnyTagsOfAnyType($tags) * @method static \Illuminate\Database\Eloquent\Builder|Recipe withAnyTagsOfAnyType($tags)
* @property float|null $weight
* @property-read float|null $serving_weight
* @method static \Illuminate\Database\Eloquent\Builder|Recipe whereWeight($value)
*/ */
final class Recipe extends Model final class Recipe extends Model
{ {

View File

@ -3,12 +3,12 @@
namespace App\Support; namespace App\Support;
use App\Models\Food; use App\Models\Food;
use App\Models\Recipe;
/**
* TODO: Refactor for more general use.
*/
class Nutrients class Nutrients
{ {
public static float $gramsPerOunce = 28.349523125;
public static array $all = [ public static array $all = [
['value' => 'calories', 'unit' => null], ['value' => 'calories', 'unit' => null],
['value' => 'fat', 'unit' => 'g'], ['value' => 'fat', 'unit' => 'g'],
@ -27,13 +27,22 @@ class Nutrients
['value' => 'serving', 'label' => 'servings'], ['value' => 'serving', 'label' => 'servings'],
]; ];
/**
* Calculate a nutrient multiplier for a Food.
*
* @param \App\Models\Food $food
* @param float $amount
* @param string|null $fromUnit
*
* @return float
*/
public static function calculateFoodNutrientMultiplier( public static function calculateFoodNutrientMultiplier(
Food $food, Food $food,
float $amount, float $amount,
string|null $fromUnit string|null $fromUnit
): float { ): float {
if ($fromUnit === 'oz') { if ($fromUnit === 'oz') {
return $amount * 28.349523125 / $food->serving_weight; return $amount * self::$gramsPerOunce / $food->serving_weight;
} }
elseif ($fromUnit === 'serving') { elseif ($fromUnit === 'serving') {
return $amount; return $amount;
@ -76,4 +85,34 @@ class Nutrients
return $multiplier / $food->serving_size * $amount; return $multiplier / $food->serving_size * $amount;
} }
/**
* Calculate a nutrient amount for a recipe.
*
* @param \App\Models\Recipe $recipe
* @param string $nutrient
* @param float $amount
* @param string $fromUnit
*
* @return float
*/
public static function calculateRecipeNutrientAmount(
Recipe $recipe,
string $nutrient,
float $amount,
string $fromUnit
): float {
if ($fromUnit === 'oz') {
return $amount * self::$gramsPerOunce / $recipe->weight * $recipe->{"{$nutrient}Total"}();
}
elseif ($fromUnit === 'serving') {
return $recipe->{"{$nutrient}PerServing"}() * $amount;
}
elseif ($fromUnit === 'gram') {
return $amount / $recipe->weight * $recipe->{"{$nutrient}Total"}();
}
else {
throw new \DomainException("Unsupported recipe unit: {$fromUnit}");
}
}
} }

View File

@ -29,12 +29,17 @@
x-bind:data-detail="result.detail"> x-bind:data-detail="result.detail">
<div class="pointer-events-none"> <div class="pointer-events-none">
<div> <div>
<span x-text="result.name"></span><span class="text-gray-600" x-text="', ' + result.detail" x-show="result.detail"></span> <span class="font-bold" x-text="result.name"></span><span class="text-gray-600" x-text="', ' + result.detail" x-show="result.detail"></span>
</div> </div>
<div x-show="result.servings"> <div x-show="result.type === 'App\\Models\\Recipe'">
<div class="text-sm">Servings: <span x-text="result.servings"></span></div> <div x-show="result.serving_weight">
<div class="text-sm">Serving weight <span x-text="result.serving_weight"></span>g</div>
</div>
<div x-show="result.servings">
<div class="text-sm">Servings: <span x-text="result.servings"></span></div>
</div>
</div> </div>
<div x-show="result.serving_size"> <div x-show="result.type === 'App\\Models\\Food'">
<div class="text-sm text-gray-600" x-text="result.brand" x-show="result.brand"></div> <div class="text-sm text-gray-600" x-text="result.brand" x-show="result.brand"></div>
<div class="text-sm"> <div class="text-sm">
Serving size <span x-text="result.serving_size_formatted"></span> Serving size <span x-text="result.serving_size_formatted"></span>