diff --git a/app/Http/Controllers/FoodController.php b/app/Http/Controllers/FoodController.php
index 4f93ea6..74a9f41 100644
--- a/app/Http/Controllers/FoodController.php
+++ b/app/Http/Controllers/FoodController.php
@@ -3,6 +3,8 @@
namespace App\Http\Controllers;
use App\Models\Food;
+use App\Rules\StringIsDecimalOrFraction;
+use App\Support\Number;
use App\Support\Nutrients;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
@@ -70,7 +72,7 @@ class FoodController extends Controller
'name' => 'required|string',
'detail' => 'nullable|string',
'brand' => 'nullable|string',
- 'serving_size' => 'required|numeric',
+ 'serving_size' => ['required', new StringIsDecimalOrFraction],
'serving_unit' => 'nullable|string',
'serving_weight' => 'required|numeric',
'calories' => 'nullable|numeric',
@@ -80,6 +82,7 @@ class FoodController extends Controller
'carbohydrates' => 'nullable|numeric',
'protein' => 'nullable|numeric',
]);
+ $attributes['serving_size'] = Number::floatFromString($attributes['serving_size']);
$food->fill(array_filter($attributes))->save();
return redirect(route('foods.show', $food))
->with('message', 'Changes saved!');
diff --git a/app/Http/Controllers/JournalEntryController.php b/app/Http/Controllers/JournalEntryController.php
index 93dd2c5..3fd44cf 100644
--- a/app/Http/Controllers/JournalEntryController.php
+++ b/app/Http/Controllers/JournalEntryController.php
@@ -9,6 +9,8 @@ use App\Models\Food;
use App\Models\JournalEntry;
use App\Models\Recipe;
use App\Rules\ArrayNotEmpty;
+use App\Rules\StringIsDecimalOrFraction;
+use App\Support\Number;
use App\Support\Nutrients;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
@@ -82,7 +84,7 @@ class JournalEntryController extends Controller
'date' => 'required|date',
'meal' => 'required|string',
'amounts' => ['required', 'array', new ArrayNotEmpty],
- 'amounts.*' => 'required_with:foods.*,recipes.*|nullable|numeric|min:0',
+ 'amounts.*' => ['required_with:foods.*,recipes.*', 'nullable', new StringIsDecimalOrFraction],
'units' => ['required', 'array', new ArrayNotEmpty],
'units.*' => 'nullable|string',
'foods' => 'required|array',
@@ -119,7 +121,7 @@ class JournalEntryController extends Controller
$food = $foods->get($id);
$nutrient_multiplier = Nutrients::calculateFoodNutrientMultiplier(
$food,
- $input['amounts'][$key],
+ Number::floatFromString($input['amounts'][$key]),
$input['units'][$key],
);
foreach ($nutrients as $nutrient => $amount) {
@@ -134,7 +136,7 @@ class JournalEntryController extends Controller
foreach ($recipes_selected as $key => $id) {
$recipe = $recipes->get($id);
foreach ($nutrients as $nutrient => $amount) {
- $nutrients[$nutrient] += $recipe->{"{$nutrient}PerServing"}() * $input['amounts'][$key];
+ $nutrients[$nutrient] += $recipe->{"{$nutrient}PerServing"}() * Number::floatFromString($input['amounts'][$key]);
}
$summary[] = "{$input['amounts'][$key]} {$input['units'][$key]} {$recipe->name}";
}
diff --git a/app/Http/Controllers/RecipeController.php b/app/Http/Controllers/RecipeController.php
index db5c691..e56309c 100644
--- a/app/Http/Controllers/RecipeController.php
+++ b/app/Http/Controllers/RecipeController.php
@@ -7,6 +7,8 @@ use App\Models\FoodAmount;
use App\Models\Recipe;
use App\Models\RecipeStep;
use App\Rules\ArrayNotEmpty;
+use App\Rules\StringIsDecimalOrFraction;
+use App\Support\Number;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
@@ -63,7 +65,7 @@ class RecipeController extends Controller
'description' => 'nullable|string',
'servings' => 'required|numeric',
'foods_amount' => ['required', 'array', new ArrayNotEmpty],
- 'foods_amount.*' => 'required_with:foods.*|nullable|numeric|min:0',
+ 'foods_amount.*' => ['required_with:foods.*', 'nullable', new StringIsDecimalOrFraction],
'foods_unit' => ['required', 'array'],
'foods_unit.*' => 'nullable|string',
'foods' => ['required', 'array', new ArrayNotEmpty],
@@ -88,7 +90,7 @@ class RecipeController extends Controller
$weight = 0;
foreach (array_filter($input['foods_amount']) as $key => $amount) {
$food_amounts[$key] = new FoodAmount([
- 'amount' => (float) $amount,
+ 'amount' => Number::floatFromString($amount),
'unit' => $input['foods_unit'][$key],
'weight' => $weight++,
]);
diff --git a/app/Rules/StringIsDecimalOrFraction.php b/app/Rules/StringIsDecimalOrFraction.php
new file mode 100644
index 0000000..ba27a33
--- /dev/null
+++ b/app/Rules/StringIsDecimalOrFraction.php
@@ -0,0 +1,38 @@
+toFloat();
+ }
+ return $result;
+ }
+
+ /**
+ * Get a string faction representation of a float.
+ *
+ * @todo Handle repeating values like 1/3, 2/3, etc.
+ *
+ * @see https://rosettacode.org/wiki/Convert_decimal_number_to_rational#PHP
+ *
+ * @param float $value
+ * Value to convert to string fraction.
+ * @return string
+ * String fraction.
+ */
+ public static function fractionStringFromFloat(float $value): string {
+ $fraction = (string) Fraction::fromFloat($value);
+ if ($fraction === '33333333/100000000') {
+ $fraction = '1/3';
+ }
+ elseif ($fraction === '66666667/100000000') {
+ $fraction = '2/3';
+ }
+ return $fraction;
+ }
+}
diff --git a/composer.json b/composer.json
index 38f67e0..cbac5de 100644
--- a/composer.json
+++ b/composer.json
@@ -10,7 +10,8 @@
"fruitcake/laravel-cors": "^2.0",
"guzzlehttp/guzzle": "^7.0.1",
"laravel/framework": "^8.12",
- "laravel/tinker": "^2.5"
+ "laravel/tinker": "^2.5",
+ "phospr/fraction": "^1.2"
},
"require-dev": {
"barryvdh/laravel-ide-helper": "^2.9",
diff --git a/composer.lock b/composer.lock
index c84e615..a391417 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "6798cd035bece9ee986d5e3a59036303",
+ "content-hash": "7054301f9158e201ddff5b37f41a1c5a",
"packages": [
{
"name": "asm89/stack-cors",
@@ -1829,6 +1829,49 @@
},
"time": "2020-11-07T02:01:34+00:00"
},
+ {
+ "name": "phospr/fraction",
+ "version": "v1.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phospr/fraction.git",
+ "reference": "3f195b920bca0ba4eac8575e397af283782c699d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phospr/fraction/zipball/3f195b920bca0ba4eac8575e397af283782c699d",
+ "reference": "3f195b920bca0ba4eac8575e397af283782c699d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "4.0.*"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Phospr\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Tom Haskins-Vaughan",
+ "email": "tom@tomhv.uk"
+ }
+ ],
+ "description": "A composer-installable fractions library",
+ "support": {
+ "issues": "https://github.com/phospr/fraction/issues",
+ "source": "https://github.com/phospr/fraction/tree/master"
+ },
+ "time": "2016-12-21T15:33:12+00:00"
+ },
{
"name": "phpoption/phpoption",
"version": "1.7.5",
diff --git a/resources/views/foods/edit.blade.php b/resources/views/foods/edit.blade.php
index c395047..c98a3a1 100644
--- a/resources/views/foods/edit.blade.php
+++ b/resources/views/foods/edit.blade.php
@@ -72,11 +72,10 @@