added imlementation of API,
updated AGENTS.md by Codex
This commit is contained in:
67
AGENTS.md
67
AGENTS.md
@ -15,12 +15,11 @@ It describes what the project is, what is already implemented, and what still ne
|
|||||||
- `backend/`
|
- `backend/`
|
||||||
- `frontend/`
|
- `frontend/`
|
||||||
|
|
||||||
## Current State (as of 2026-02-11)
|
## Current State (as of 2026-02-12)
|
||||||
|
|
||||||
- `README.md` already contains product specification in Slovak and English.
|
- `README.md` already contains product specification in Slovak and English.
|
||||||
- Backend DB migrations exist in `backend/src/Maintenance.php` up to version `7`.
|
- Backend DB migrations exist in `backend/src/Maintenance.php` up to version `7`.
|
||||||
- Backend API routes are not implemented yet:
|
- Backend API methods are implemented in `backend/src/API.php`.
|
||||||
- `backend/src/API.php` extends `APIlite` but has no endpoints.
|
|
||||||
- Frontend is still template-level:
|
- Frontend is still template-level:
|
||||||
- `frontend/src/App.vue` has placeholder content.
|
- `frontend/src/App.vue` has placeholder content.
|
||||||
- `frontend/src/router/index.ts` has empty `routes: []`.
|
- `frontend/src/router/index.ts` has empty `routes: []`.
|
||||||
@ -34,6 +33,8 @@ It describes what the project is, what is already implemented, and what still ne
|
|||||||
- Supports `mysql` and `sqlite`
|
- Supports `mysql` and `sqlite`
|
||||||
- Loads override config files from `backend/config/*.php` (except the base file itself)
|
- Loads override config files from `backend/config/*.php` (except the base file itself)
|
||||||
- Migration logic: `backend/src/Maintenance.php`
|
- Migration logic: `backend/src/Maintenance.php`
|
||||||
|
- API implementation: `backend/src/API.php`
|
||||||
|
- DB table models: `backend/src/Models/*.php`
|
||||||
|
|
||||||
## Database Schema Snapshot
|
## Database Schema Snapshot
|
||||||
|
|
||||||
@ -55,6 +56,51 @@ Migration creates these tables:
|
|||||||
- `diary_entry_id`, `diary_day_id`, `meal_type`, `meal_id`
|
- `diary_entry_id`, `diary_day_id`, `meal_type`, `meal_id`
|
||||||
- unique: `(diary_day_id, meal_type)`
|
- unique: `(diary_day_id, meal_type)`
|
||||||
|
|
||||||
|
## API Surface (Implemented)
|
||||||
|
|
||||||
|
All actions are invoked through `backend/public/API.php` with `?action=<method_name>`.
|
||||||
|
|
||||||
|
- Utility:
|
||||||
|
- `health`
|
||||||
|
- Ingredients:
|
||||||
|
- `ingredientList(user_id, query = "", include_global = true)`
|
||||||
|
- `ingredientGet(user_id, ingredient_id)`
|
||||||
|
- `ingredientCreate(user_id, name, protein_g_100, carbs_g_100, sugar_g_100, fat_g_100, fiber_g_100 = 0, kcal_100 = 0)`
|
||||||
|
- `ingredientUpdate(user_id, ingredient_id, name, protein_g_100, carbs_g_100, sugar_g_100, fat_g_100, fiber_g_100 = 0, kcal_100 = 0)`
|
||||||
|
- `ingredientDelete(user_id, ingredient_id)`
|
||||||
|
- Meals:
|
||||||
|
- `mealList(user_id, meal_type = "", with_items = false, with_totals = false)`
|
||||||
|
- `mealGet(user_id, meal_id, with_items = true, with_totals = true)`
|
||||||
|
- `mealCreate(user_id, name, meal_type)`
|
||||||
|
- `mealUpdate(user_id, meal_id, name, meal_type)`
|
||||||
|
- `mealDelete(user_id, meal_id)`
|
||||||
|
- Meal items:
|
||||||
|
- `mealItemList(user_id, meal_id, with_calculated = true)`
|
||||||
|
- `mealItemAdd(user_id, meal_id, ingredient_id, grams, position = 1)`
|
||||||
|
- `mealItemUpdate(user_id, meal_item_id, ingredient_id, grams, position)`
|
||||||
|
- `mealItemDelete(user_id, meal_item_id)`
|
||||||
|
- `mealItemReorder(user_id, meal_id, ordered_item_ids)`
|
||||||
|
- Calculations:
|
||||||
|
- `mealTotals(user_id, meal_id)`
|
||||||
|
- Diary:
|
||||||
|
- `diaryDayGet(user_id, day_date, with_totals = true)`
|
||||||
|
- `diaryDaySetMeal(user_id, day_date, meal_type, meal_id)`
|
||||||
|
- `diaryDayUnsetMeal(user_id, day_date, meal_type)`
|
||||||
|
- `diaryRange(user_id, date_from, date_to)`
|
||||||
|
|
||||||
|
## Behavior and Validation Rules
|
||||||
|
|
||||||
|
- `meal_type` must be one of: `breakfast`, `lunch`, `dinner`.
|
||||||
|
- Date params must use format `YYYY-MM-DD`.
|
||||||
|
- `grams` must be `> 0`.
|
||||||
|
- Nutrition input values are validated as non-negative.
|
||||||
|
- If `kcal_100` is `0`, API computes kcal by formula:
|
||||||
|
- `protein*4 + carbs*4 + fat*9`
|
||||||
|
- Ownership checks are enforced by `user_id`:
|
||||||
|
- meals and meal items must belong to the user
|
||||||
|
- ingredients can be user-owned or global (`user_id = null`) for read/select
|
||||||
|
- API currently requires an existing `users` record for almost all actions.
|
||||||
|
|
||||||
## Known Pitfalls and Notes
|
## Known Pitfalls and Notes
|
||||||
|
|
||||||
- The historical FK issue in `meal_items` should reference `meals(meal_id)`.
|
- The historical FK issue in `meal_items` should reference `meals(meal_id)`.
|
||||||
@ -62,6 +108,9 @@ Migration creates these tables:
|
|||||||
- If someone ran migrations before FK fix, old MySQL state may still be broken.
|
- If someone ran migrations before FK fix, old MySQL state may still be broken.
|
||||||
In that case reset affected table(s) or rebuild DB from clean state.
|
In that case reset affected table(s) or rebuild DB from clean state.
|
||||||
- Some comments in `Maintenance.php` show encoding artifacts, but SQL structure is valid.
|
- Some comments in `Maintenance.php` show encoding artifacts, but SQL structure is valid.
|
||||||
|
- Authentication is not implemented yet; `user_id` is passed as an action parameter.
|
||||||
|
- For `array` parameters (for example `ordered_item_ids`), APIlite expects JSON in request payload.
|
||||||
|
- APIlite wraps responses with a nested `data` object. Keep this in mind on frontend parsing.
|
||||||
|
|
||||||
## Local Runbook
|
## Local Runbook
|
||||||
|
|
||||||
@ -82,15 +131,15 @@ Frontend:
|
|||||||
|
|
||||||
## Product Behavior Target (what to build next)
|
## Product Behavior Target (what to build next)
|
||||||
|
|
||||||
- CRUD for ingredients with per-100g nutrition values.
|
- Implement auth and session handling (replace plain `user_id` input model).
|
||||||
- CRUD for meals grouped by day part (`breakfast`, `lunch`, `dinner`).
|
- Build frontend screens for ingredients, meals, meal item editor, diary day, diary range.
|
||||||
- Add meal items by ingredient + grams.
|
- Connect frontend to implemented backend actions.
|
||||||
- Compute per-item nutrition and calories from grams.
|
- Add API tests for validation, ownership checks, and totals calculation consistency.
|
||||||
- Compute totals for full meal.
|
- Add pagination/filter strategy where list endpoints grow.
|
||||||
- Diary day view: assign one meal per day part and compute whole-day totals.
|
|
||||||
|
|
||||||
## Practical Conventions
|
## Practical Conventions
|
||||||
|
|
||||||
- Keep IDs as `BIGINT UNSIGNED` and use `*_id` naming consistently.
|
- Keep IDs as `BIGINT UNSIGNED` and use `*_id` naming consistently.
|
||||||
- Keep MySQL + SQLite compatibility in SQL where possible (project supports both).
|
- Keep MySQL + SQLite compatibility in SQL where possible (project supports both).
|
||||||
- When changing schema, always bump DB version in `Maintenance.php` with forward-only migration steps.
|
- When changing schema, always bump DB version in `Maintenance.php` with forward-only migration steps.
|
||||||
|
- Keep API action names stable unless frontend is updated at the same time.
|
||||||
|
|||||||
@ -5,7 +5,843 @@ namespace TPsoft\Nutrio;
|
|||||||
require_once __DIR__.'/Init.php';
|
require_once __DIR__.'/Init.php';
|
||||||
|
|
||||||
use TPsoft\APIlite\APIlite;
|
use TPsoft\APIlite\APIlite;
|
||||||
|
use TPsoft\Nutrio\Models\DiaryDays;
|
||||||
|
use TPsoft\Nutrio\Models\DiaryEntries;
|
||||||
|
use TPsoft\Nutrio\Models\Ingredients;
|
||||||
|
use TPsoft\Nutrio\Models\MealItems;
|
||||||
|
use TPsoft\Nutrio\Models\Meals;
|
||||||
|
use TPsoft\Nutrio\Models\Options;
|
||||||
|
use TPsoft\Nutrio\Models\Users;
|
||||||
|
|
||||||
class API extends APIlite {
|
class API extends APIlite {
|
||||||
|
|
||||||
|
private ?Users $usersModel = null;
|
||||||
|
private ?Ingredients $ingredientsModel = null;
|
||||||
|
private ?Meals $mealsModel = null;
|
||||||
|
private ?MealItems $mealItemsModel = null;
|
||||||
|
private ?DiaryDays $diaryDaysModel = null;
|
||||||
|
private ?DiaryEntries $diaryEntriesModel = null;
|
||||||
|
private ?Options $optionsModel = null;
|
||||||
|
|
||||||
|
public function health(): array
|
||||||
|
{
|
||||||
|
$dbVersion = null;
|
||||||
|
$versionRow = $this->options()->option('version');
|
||||||
|
if (is_array($versionRow) && isset($versionRow['value'])) {
|
||||||
|
$dbVersion = (string) $versionRow['value'];
|
||||||
|
}
|
||||||
|
return array(
|
||||||
|
'service' => 'Nutrio API',
|
||||||
|
'timestamp' => date('c'),
|
||||||
|
'db_type' => $this->users()->getDBtype(),
|
||||||
|
'db_version' => $dbVersion
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ingredientList(int $user_id, string $query = '', bool $include_global = true): array
|
||||||
|
{
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
$search = array(
|
||||||
|
'user_id' => $include_global ? array($user_id, null) : $user_id
|
||||||
|
);
|
||||||
|
$query = trim($query);
|
||||||
|
if (strlen($query) > 0) {
|
||||||
|
$search['name'] = '%' . $query . '%';
|
||||||
|
}
|
||||||
|
$list = $this->ingredients()->getList($search);
|
||||||
|
$list = is_array($list) ? $list : array();
|
||||||
|
$result = array();
|
||||||
|
foreach ($list as $row) {
|
||||||
|
$result[] = $this->mapIngredient($row);
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ingredientGet(int $user_id, int $ingredient_id): array
|
||||||
|
{
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
return $this->mapIngredient($this->getIngredientAccessible($user_id, $ingredient_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ingredientCreate(
|
||||||
|
int $user_id,
|
||||||
|
string $name,
|
||||||
|
float $protein_g_100,
|
||||||
|
float $carbs_g_100,
|
||||||
|
float $sugar_g_100,
|
||||||
|
float $fat_g_100,
|
||||||
|
float $fiber_g_100 = 0,
|
||||||
|
float $kcal_100 = 0
|
||||||
|
): array {
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
$name = $this->normalizeName($name);
|
||||||
|
$this->assertNonNegative('protein_g_100', $protein_g_100);
|
||||||
|
$this->assertNonNegative('carbs_g_100', $carbs_g_100);
|
||||||
|
$this->assertNonNegative('sugar_g_100', $sugar_g_100);
|
||||||
|
$this->assertNonNegative('fat_g_100', $fat_g_100);
|
||||||
|
$this->assertNonNegative('fiber_g_100', $fiber_g_100);
|
||||||
|
if ($kcal_100 < 0) {
|
||||||
|
throw new \Exception('kcal_100 cannot be negative');
|
||||||
|
}
|
||||||
|
if ($kcal_100 == 0) {
|
||||||
|
$kcal_100 = $this->computeKcal100($protein_g_100, $carbs_g_100, $fat_g_100);
|
||||||
|
}
|
||||||
|
$ingredientId = $this->ingredients()->ingredientSave(array(
|
||||||
|
'user_id' => $user_id,
|
||||||
|
'name' => $name,
|
||||||
|
'protein_g_100' => $this->round2($protein_g_100),
|
||||||
|
'carbs_g_100' => $this->round2($carbs_g_100),
|
||||||
|
'sugar_g_100' => $this->round2($sugar_g_100),
|
||||||
|
'fat_g_100' => $this->round2($fat_g_100),
|
||||||
|
'fiber_g_100' => $this->round2($fiber_g_100),
|
||||||
|
'kcal_100' => $this->round2($kcal_100),
|
||||||
|
'created_at' => '`NOW`'
|
||||||
|
));
|
||||||
|
if ($ingredientId === false) {
|
||||||
|
throw new \Exception('Failed to create ingredient');
|
||||||
|
}
|
||||||
|
$created = $this->ingredients()->ingredient((int) $ingredientId);
|
||||||
|
if (!is_array($created)) {
|
||||||
|
throw new \Exception('Failed to load created ingredient');
|
||||||
|
}
|
||||||
|
return $this->mapIngredient($created);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ingredientUpdate(
|
||||||
|
int $user_id,
|
||||||
|
int $ingredient_id,
|
||||||
|
string $name,
|
||||||
|
float $protein_g_100,
|
||||||
|
float $carbs_g_100,
|
||||||
|
float $sugar_g_100,
|
||||||
|
float $fat_g_100,
|
||||||
|
float $fiber_g_100 = 0,
|
||||||
|
float $kcal_100 = 0
|
||||||
|
): array {
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
$this->getIngredientOwned($user_id, $ingredient_id);
|
||||||
|
$name = $this->normalizeName($name);
|
||||||
|
$this->assertNonNegative('protein_g_100', $protein_g_100);
|
||||||
|
$this->assertNonNegative('carbs_g_100', $carbs_g_100);
|
||||||
|
$this->assertNonNegative('sugar_g_100', $sugar_g_100);
|
||||||
|
$this->assertNonNegative('fat_g_100', $fat_g_100);
|
||||||
|
$this->assertNonNegative('fiber_g_100', $fiber_g_100);
|
||||||
|
if ($kcal_100 < 0) {
|
||||||
|
throw new \Exception('kcal_100 cannot be negative');
|
||||||
|
}
|
||||||
|
if ($kcal_100 == 0) {
|
||||||
|
$kcal_100 = $this->computeKcal100($protein_g_100, $carbs_g_100, $fat_g_100);
|
||||||
|
}
|
||||||
|
$updated = $this->ingredients()->ingredient($ingredient_id, array(
|
||||||
|
'name' => $name,
|
||||||
|
'protein_g_100' => $this->round2($protein_g_100),
|
||||||
|
'carbs_g_100' => $this->round2($carbs_g_100),
|
||||||
|
'sugar_g_100' => $this->round2($sugar_g_100),
|
||||||
|
'fat_g_100' => $this->round2($fat_g_100),
|
||||||
|
'fiber_g_100' => $this->round2($fiber_g_100),
|
||||||
|
'kcal_100' => $this->round2($kcal_100)
|
||||||
|
));
|
||||||
|
if ($updated === false) {
|
||||||
|
throw new \Exception('Failed to update ingredient');
|
||||||
|
}
|
||||||
|
$row = $this->ingredients()->ingredient($ingredient_id);
|
||||||
|
if (!is_array($row)) {
|
||||||
|
throw new \Exception('Failed to load updated ingredient');
|
||||||
|
}
|
||||||
|
return $this->mapIngredient($row);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ingredientDelete(int $user_id, int $ingredient_id): array
|
||||||
|
{
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
$this->getIngredientOwned($user_id, $ingredient_id);
|
||||||
|
$deleted = $this->ingredients()->ingredient($ingredient_id, null);
|
||||||
|
if ($deleted === false) {
|
||||||
|
throw new \Exception('Failed to delete ingredient');
|
||||||
|
}
|
||||||
|
return array(
|
||||||
|
'deleted' => true,
|
||||||
|
'ingredient_id' => $ingredient_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mealList(int $user_id, string $meal_type = '', bool $with_items = false, bool $with_totals = false): array
|
||||||
|
{
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
$search = array('user_id' => $user_id);
|
||||||
|
$meal_type = trim($meal_type);
|
||||||
|
if (strlen($meal_type) > 0) {
|
||||||
|
$this->assertMealType($meal_type);
|
||||||
|
$search['meal_type'] = $meal_type;
|
||||||
|
}
|
||||||
|
$rows = $this->meals()->getList($search);
|
||||||
|
$rows = is_array($rows) ? $rows : array();
|
||||||
|
$result = array();
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$meal = $this->mapMeal($row);
|
||||||
|
$calculatedItems = null;
|
||||||
|
if ($with_items) {
|
||||||
|
$calculatedItems = $this->buildMealItems((int) $meal['meal_id'], true);
|
||||||
|
$meal['items'] = $calculatedItems;
|
||||||
|
}
|
||||||
|
if ($with_totals) {
|
||||||
|
if (is_array($calculatedItems)) {
|
||||||
|
$meal['totals'] = $this->totalsFromCalculatedItems($calculatedItems);
|
||||||
|
} else {
|
||||||
|
$meal['totals'] = $this->calculateMealTotals((int) $meal['meal_id']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$result[] = $meal;
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mealGet(int $user_id, int $meal_id, bool $with_items = true, bool $with_totals = true): array
|
||||||
|
{
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
$meal = $this->mapMeal($this->getMealOwned($user_id, $meal_id));
|
||||||
|
$calculatedItems = null;
|
||||||
|
if ($with_items) {
|
||||||
|
$calculatedItems = $this->buildMealItems($meal_id, true);
|
||||||
|
$meal['items'] = $calculatedItems;
|
||||||
|
}
|
||||||
|
if ($with_totals) {
|
||||||
|
if (is_array($calculatedItems)) {
|
||||||
|
$meal['totals'] = $this->totalsFromCalculatedItems($calculatedItems);
|
||||||
|
} else {
|
||||||
|
$meal['totals'] = $this->calculateMealTotals($meal_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $meal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mealCreate(int $user_id, string $name, string $meal_type): array
|
||||||
|
{
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
$name = $this->normalizeName($name);
|
||||||
|
$this->assertMealType($meal_type);
|
||||||
|
$mealId = $this->meals()->mealSave(array(
|
||||||
|
'user_id' => $user_id,
|
||||||
|
'name' => $name,
|
||||||
|
'meal_type' => $meal_type,
|
||||||
|
'created_at' => '`NOW`'
|
||||||
|
));
|
||||||
|
if ($mealId === false) {
|
||||||
|
throw new \Exception('Failed to create meal');
|
||||||
|
}
|
||||||
|
return $this->mealGet($user_id, (int) $mealId, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mealUpdate(int $user_id, int $meal_id, string $name, string $meal_type): array
|
||||||
|
{
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
$this->getMealOwned($user_id, $meal_id);
|
||||||
|
$name = $this->normalizeName($name);
|
||||||
|
$this->assertMealType($meal_type);
|
||||||
|
$updated = $this->meals()->meal($meal_id, array(
|
||||||
|
'name' => $name,
|
||||||
|
'meal_type' => $meal_type
|
||||||
|
));
|
||||||
|
if ($updated === false) {
|
||||||
|
throw new \Exception('Failed to update meal');
|
||||||
|
}
|
||||||
|
return $this->mealGet($user_id, $meal_id, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mealDelete(int $user_id, int $meal_id): array
|
||||||
|
{
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
$this->getMealOwned($user_id, $meal_id);
|
||||||
|
$deleted = $this->meals()->meal($meal_id, null);
|
||||||
|
if ($deleted === false) {
|
||||||
|
throw new \Exception('Failed to delete meal');
|
||||||
|
}
|
||||||
|
return array(
|
||||||
|
'deleted' => true,
|
||||||
|
'meal_id' => $meal_id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mealItemList(int $user_id, int $meal_id, bool $with_calculated = true): array
|
||||||
|
{
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
$this->getMealOwned($user_id, $meal_id);
|
||||||
|
return array(
|
||||||
|
'meal_id' => $meal_id,
|
||||||
|
'items' => $this->buildMealItems($meal_id, $with_calculated)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mealItemAdd(int $user_id, int $meal_id, int $ingredient_id, float $grams, int $position = 1): array
|
||||||
|
{
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
$this->getMealOwned($user_id, $meal_id);
|
||||||
|
$this->assertPositive('grams', $grams);
|
||||||
|
if ($position < 1) {
|
||||||
|
throw new \Exception('position must be >= 1');
|
||||||
|
}
|
||||||
|
$this->getIngredientAccessible($user_id, $ingredient_id);
|
||||||
|
$mealItemId = $this->mealItems()->mealItemSave(array(
|
||||||
|
'meal_id' => $meal_id,
|
||||||
|
'ingredient_id' => $ingredient_id,
|
||||||
|
'grams' => $this->round2($grams),
|
||||||
|
'position' => $position
|
||||||
|
));
|
||||||
|
if ($mealItemId === false) {
|
||||||
|
throw new \Exception('Failed to add meal item');
|
||||||
|
}
|
||||||
|
$item = $this->getMealItemOwned($user_id, (int) $mealItemId);
|
||||||
|
return $this->enrichMealItem($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mealItemUpdate(int $user_id, int $meal_item_id, int $ingredient_id, float $grams, int $position): array
|
||||||
|
{
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
$item = $this->getMealItemOwned($user_id, $meal_item_id);
|
||||||
|
$this->assertPositive('grams', $grams);
|
||||||
|
if ($position < 1) {
|
||||||
|
throw new \Exception('position must be >= 1');
|
||||||
|
}
|
||||||
|
$this->getIngredientAccessible($user_id, $ingredient_id);
|
||||||
|
$updated = $this->mealItems()->mealItem($meal_item_id, array(
|
||||||
|
'ingredient_id' => $ingredient_id,
|
||||||
|
'grams' => $this->round2($grams),
|
||||||
|
'position' => $position
|
||||||
|
));
|
||||||
|
if ($updated === false) {
|
||||||
|
throw new \Exception('Failed to update meal item');
|
||||||
|
}
|
||||||
|
$item = $this->getMealItemOwned($user_id, (int) $item['meal_item_id']);
|
||||||
|
return $this->enrichMealItem($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mealItemDelete(int $user_id, int $meal_item_id): array
|
||||||
|
{
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
$item = $this->getMealItemOwned($user_id, $meal_item_id);
|
||||||
|
$deleted = $this->mealItems()->mealItem($meal_item_id, null);
|
||||||
|
if ($deleted === false) {
|
||||||
|
throw new \Exception('Failed to delete meal item');
|
||||||
|
}
|
||||||
|
return array(
|
||||||
|
'deleted' => true,
|
||||||
|
'meal_item_id' => $meal_item_id,
|
||||||
|
'meal_id' => (int) $item['meal_id']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mealItemReorder(int $user_id, int $meal_id, array $ordered_item_ids): array
|
||||||
|
{
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
$this->getMealOwned($user_id, $meal_id);
|
||||||
|
if (count($ordered_item_ids) <= 0) {
|
||||||
|
throw new \Exception('ordered_item_ids cannot be empty');
|
||||||
|
}
|
||||||
|
$currentItems = $this->buildMealItems($meal_id, false);
|
||||||
|
$currentIds = array();
|
||||||
|
foreach ($currentItems as $item) {
|
||||||
|
$currentIds[] = (int) $item['meal_item_id'];
|
||||||
|
}
|
||||||
|
$orderedIds = array_values(array_map('intval', $ordered_item_ids));
|
||||||
|
sort($currentIds);
|
||||||
|
$checkIds = $orderedIds;
|
||||||
|
sort($checkIds);
|
||||||
|
if ($currentIds !== $checkIds) {
|
||||||
|
throw new \Exception('ordered_item_ids must match all meal item ids exactly');
|
||||||
|
}
|
||||||
|
foreach ($orderedIds as $index => $mealItemId) {
|
||||||
|
$ok = $this->mealItems()->mealItem($mealItemId, array('position' => $index + 1));
|
||||||
|
if ($ok === false) {
|
||||||
|
throw new \Exception('Failed to reorder meal items');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return array(
|
||||||
|
'meal_id' => $meal_id,
|
||||||
|
'items' => $this->buildMealItems($meal_id, true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mealTotals(int $user_id, int $meal_id): array
|
||||||
|
{
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
$this->getMealOwned($user_id, $meal_id);
|
||||||
|
return array(
|
||||||
|
'meal_id' => $meal_id,
|
||||||
|
'totals' => $this->calculateMealTotals($meal_id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function diaryDayGet(int $user_id, string $day_date, bool $with_totals = true): array
|
||||||
|
{
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
$this->assertDate($day_date);
|
||||||
|
return $this->buildDiaryDay($user_id, $day_date, $with_totals);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function diaryDaySetMeal(int $user_id, string $day_date, string $meal_type, int $meal_id): array
|
||||||
|
{
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
$this->assertDate($day_date);
|
||||||
|
$this->assertMealType($meal_type);
|
||||||
|
$meal = $this->getMealOwned($user_id, $meal_id);
|
||||||
|
if ($meal['meal_type'] !== $meal_type) {
|
||||||
|
throw new \Exception('meal_type does not match selected meal');
|
||||||
|
}
|
||||||
|
$day = $this->ensureDiaryDay($user_id, $day_date);
|
||||||
|
$existing = $this->diaryEntries()->search('diaryEntries')
|
||||||
|
->where(array(
|
||||||
|
'diary_day_id' => (int) $day['diary_day_id'],
|
||||||
|
'meal_type' => $meal_type
|
||||||
|
))
|
||||||
|
->toArrayFirst();
|
||||||
|
if (is_array($existing) && isset($existing['diary_entry_id'])) {
|
||||||
|
$updated = $this->diaryEntries()->diaryEntry((int) $existing['diary_entry_id'], array(
|
||||||
|
'meal_id' => $meal_id
|
||||||
|
));
|
||||||
|
if ($updated === false) {
|
||||||
|
throw new \Exception('Failed to update diary entry');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$inserted = $this->diaryEntries()->diaryEntrySave(array(
|
||||||
|
'diary_day_id' => (int) $day['diary_day_id'],
|
||||||
|
'meal_type' => $meal_type,
|
||||||
|
'meal_id' => $meal_id,
|
||||||
|
'created_at' => '`NOW`'
|
||||||
|
));
|
||||||
|
if ($inserted === false) {
|
||||||
|
throw new \Exception('Failed to create diary entry');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this->buildDiaryDay($user_id, $day_date, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function diaryDayUnsetMeal(int $user_id, string $day_date, string $meal_type): array
|
||||||
|
{
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
$this->assertDate($day_date);
|
||||||
|
$this->assertMealType($meal_type);
|
||||||
|
$day = $this->getDiaryDay($user_id, $day_date);
|
||||||
|
if ($day === false) {
|
||||||
|
return $this->buildDiaryDay($user_id, $day_date, true);
|
||||||
|
}
|
||||||
|
$existing = $this->diaryEntries()->search('diaryEntries')
|
||||||
|
->where(array(
|
||||||
|
'diary_day_id' => (int) $day['diary_day_id'],
|
||||||
|
'meal_type' => $meal_type
|
||||||
|
))
|
||||||
|
->toArrayFirst();
|
||||||
|
if (is_array($existing) && isset($existing['diary_entry_id'])) {
|
||||||
|
$deleted = $this->diaryEntries()->diaryEntry((int) $existing['diary_entry_id'], null);
|
||||||
|
if ($deleted === false) {
|
||||||
|
throw new \Exception('Failed to delete diary entry');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this->buildDiaryDay($user_id, $day_date, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function diaryRange(int $user_id, string $date_from, string $date_to): array
|
||||||
|
{
|
||||||
|
$this->assertUserExists($user_id);
|
||||||
|
$this->assertDate($date_from);
|
||||||
|
$this->assertDate($date_to);
|
||||||
|
if ($date_from > $date_to) {
|
||||||
|
throw new \Exception('date_from must be before or equal to date_to');
|
||||||
|
}
|
||||||
|
$rows = $this->diaryDays()->search('diaryDays')
|
||||||
|
->where(array('user_id' => $user_id))
|
||||||
|
->where(array('day_date' => '>=' . $date_from))
|
||||||
|
->where(array('day_date' => '<=' . $date_to))
|
||||||
|
->order(array('day_date' => 'ASC'))
|
||||||
|
->toArray();
|
||||||
|
$rows = is_array($rows) ? $rows : array();
|
||||||
|
$days = array();
|
||||||
|
$totals = $this->emptyTotals();
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$day = $this->buildDiaryDay($user_id, (string) $row['day_date'], true);
|
||||||
|
$days[] = $day;
|
||||||
|
if (isset($day['totals']) && is_array($day['totals'])) {
|
||||||
|
$totals = $this->addTotals($totals, $day['totals']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return array(
|
||||||
|
'user_id' => $user_id,
|
||||||
|
'date_from' => $date_from,
|
||||||
|
'date_to' => $date_to,
|
||||||
|
'days' => $days,
|
||||||
|
'totals' => $totals
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function users(): Users
|
||||||
|
{
|
||||||
|
if ($this->usersModel === null) {
|
||||||
|
$this->usersModel = new Users();
|
||||||
|
}
|
||||||
|
return $this->usersModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function ingredients(): Ingredients
|
||||||
|
{
|
||||||
|
if ($this->ingredientsModel === null) {
|
||||||
|
$this->ingredientsModel = new Ingredients();
|
||||||
|
}
|
||||||
|
return $this->ingredientsModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function meals(): Meals
|
||||||
|
{
|
||||||
|
if ($this->mealsModel === null) {
|
||||||
|
$this->mealsModel = new Meals();
|
||||||
|
}
|
||||||
|
return $this->mealsModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mealItems(): MealItems
|
||||||
|
{
|
||||||
|
if ($this->mealItemsModel === null) {
|
||||||
|
$this->mealItemsModel = new MealItems();
|
||||||
|
}
|
||||||
|
return $this->mealItemsModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function diaryDays(): DiaryDays
|
||||||
|
{
|
||||||
|
if ($this->diaryDaysModel === null) {
|
||||||
|
$this->diaryDaysModel = new DiaryDays();
|
||||||
|
}
|
||||||
|
return $this->diaryDaysModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function diaryEntries(): DiaryEntries
|
||||||
|
{
|
||||||
|
if ($this->diaryEntriesModel === null) {
|
||||||
|
$this->diaryEntriesModel = new DiaryEntries();
|
||||||
|
}
|
||||||
|
return $this->diaryEntriesModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function options(): Options
|
||||||
|
{
|
||||||
|
if ($this->optionsModel === null) {
|
||||||
|
$this->optionsModel = new Options();
|
||||||
|
}
|
||||||
|
return $this->optionsModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertUserExists(int $user_id): void
|
||||||
|
{
|
||||||
|
if ($user_id <= 0) {
|
||||||
|
throw new \Exception('user_id must be > 0');
|
||||||
|
}
|
||||||
|
if (!$this->users()->exist($user_id)) {
|
||||||
|
throw new \Exception('User not found');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertMealType(string $meal_type): void
|
||||||
|
{
|
||||||
|
$allowed = array('breakfast', 'lunch', 'dinner');
|
||||||
|
if (!in_array($meal_type, $allowed, true)) {
|
||||||
|
throw new \Exception('Invalid meal_type');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertDate(string $day_date): void
|
||||||
|
{
|
||||||
|
$dt = \DateTime::createFromFormat('Y-m-d', $day_date);
|
||||||
|
if (!$dt || $dt->format('Y-m-d') !== $day_date) {
|
||||||
|
throw new \Exception('Invalid date format, expected YYYY-MM-DD');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertNonNegative(string $name, float $value): void
|
||||||
|
{
|
||||||
|
if ($value < 0) {
|
||||||
|
throw new \Exception($name . ' cannot be negative');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function assertPositive(string $name, float $value): void
|
||||||
|
{
|
||||||
|
if ($value <= 0) {
|
||||||
|
throw new \Exception($name . ' must be > 0');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function normalizeName(string $name): string
|
||||||
|
{
|
||||||
|
$name = trim($name);
|
||||||
|
if (strlen($name) <= 0) {
|
||||||
|
throw new \Exception('name is required');
|
||||||
|
}
|
||||||
|
if (strlen($name) > 255) {
|
||||||
|
throw new \Exception('name is too long');
|
||||||
|
}
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function round2(float $value): float
|
||||||
|
{
|
||||||
|
return round($value, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function computeKcal100(float $protein_g_100, float $carbs_g_100, float $fat_g_100): float
|
||||||
|
{
|
||||||
|
return $this->round2($protein_g_100 * 4 + $carbs_g_100 * 4 + $fat_g_100 * 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMealOwned(int $user_id, int $meal_id): array
|
||||||
|
{
|
||||||
|
$meal = $this->meals()->meal($meal_id);
|
||||||
|
if (!is_array($meal)) {
|
||||||
|
throw new \Exception('Meal not found');
|
||||||
|
}
|
||||||
|
if ((int) $meal['user_id'] !== $user_id) {
|
||||||
|
throw new \Exception('Meal does not belong to user');
|
||||||
|
}
|
||||||
|
return $meal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getIngredientAccessible(int $user_id, int $ingredient_id): array
|
||||||
|
{
|
||||||
|
$ingredient = $this->ingredients()->ingredient($ingredient_id);
|
||||||
|
if (!is_array($ingredient)) {
|
||||||
|
throw new \Exception('Ingredient not found');
|
||||||
|
}
|
||||||
|
if (!is_null($ingredient['user_id']) && (int) $ingredient['user_id'] !== $user_id) {
|
||||||
|
throw new \Exception('Ingredient is not accessible for user');
|
||||||
|
}
|
||||||
|
return $ingredient;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getIngredientOwned(int $user_id, int $ingredient_id): array
|
||||||
|
{
|
||||||
|
$ingredient = $this->ingredients()->ingredient($ingredient_id);
|
||||||
|
if (!is_array($ingredient)) {
|
||||||
|
throw new \Exception('Ingredient not found');
|
||||||
|
}
|
||||||
|
if (is_null($ingredient['user_id']) || (int) $ingredient['user_id'] !== $user_id) {
|
||||||
|
throw new \Exception('Ingredient does not belong to user');
|
||||||
|
}
|
||||||
|
return $ingredient;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMealItemOwned(int $user_id, int $meal_item_id): array
|
||||||
|
{
|
||||||
|
$item = $this->mealItems()->mealItem($meal_item_id);
|
||||||
|
if (!is_array($item)) {
|
||||||
|
throw new \Exception('Meal item not found');
|
||||||
|
}
|
||||||
|
$this->getMealOwned($user_id, (int) $item['meal_id']);
|
||||||
|
return $this->mapMealItem($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildMealItems(int $meal_id, bool $with_calculated = true): array
|
||||||
|
{
|
||||||
|
$rows = $this->mealItems()->search('mealItems')
|
||||||
|
->where(array('meal_id' => $meal_id))
|
||||||
|
->order(array('position' => 'ASC', 'meal_item_id' => 'ASC'))
|
||||||
|
->toArray();
|
||||||
|
$rows = is_array($rows) ? $rows : array();
|
||||||
|
$result = array();
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$item = $this->mapMealItem($row);
|
||||||
|
if ($with_calculated) {
|
||||||
|
$item = $this->enrichMealItem($item);
|
||||||
|
}
|
||||||
|
$result[] = $item;
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function enrichMealItem(array $item): array
|
||||||
|
{
|
||||||
|
$ingredient = $this->ingredients()->ingredient((int) $item['ingredient_id']);
|
||||||
|
if (!is_array($ingredient)) {
|
||||||
|
throw new \Exception('Ingredient for meal item was not found');
|
||||||
|
}
|
||||||
|
$ingredient = $this->mapIngredient($ingredient);
|
||||||
|
$factor = ((float) $item['grams']) / 100;
|
||||||
|
$kcal100 = (float) $ingredient['kcal_100'];
|
||||||
|
if ($kcal100 <= 0) {
|
||||||
|
$kcal100 = $this->computeKcal100(
|
||||||
|
(float) $ingredient['protein_g_100'],
|
||||||
|
(float) $ingredient['carbs_g_100'],
|
||||||
|
(float) $ingredient['fat_g_100']
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$item['ingredient'] = $ingredient;
|
||||||
|
$item['nutrition'] = array(
|
||||||
|
'protein_g' => $this->round2(((float) $ingredient['protein_g_100']) * $factor),
|
||||||
|
'carbs_g' => $this->round2(((float) $ingredient['carbs_g_100']) * $factor),
|
||||||
|
'sugar_g' => $this->round2(((float) $ingredient['sugar_g_100']) * $factor),
|
||||||
|
'fat_g' => $this->round2(((float) $ingredient['fat_g_100']) * $factor),
|
||||||
|
'fiber_g' => $this->round2(((float) $ingredient['fiber_g_100']) * $factor),
|
||||||
|
'kcal' => $this->round2($kcal100 * $factor)
|
||||||
|
);
|
||||||
|
return $item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function calculateMealTotals(int $meal_id): array
|
||||||
|
{
|
||||||
|
$items = $this->buildMealItems($meal_id, true);
|
||||||
|
return $this->totalsFromCalculatedItems($items);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function totalsFromCalculatedItems(array $items): array
|
||||||
|
{
|
||||||
|
$totals = $this->emptyTotals();
|
||||||
|
foreach ($items as $item) {
|
||||||
|
if (!isset($item['nutrition']) || !is_array($item['nutrition'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$totals = $this->addTotals($totals, $item['nutrition']);
|
||||||
|
}
|
||||||
|
return $totals;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function emptyTotals(): array
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'protein_g' => 0.0,
|
||||||
|
'carbs_g' => 0.0,
|
||||||
|
'sugar_g' => 0.0,
|
||||||
|
'fat_g' => 0.0,
|
||||||
|
'fiber_g' => 0.0,
|
||||||
|
'kcal' => 0.0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addTotals(array $base, array $add): array
|
||||||
|
{
|
||||||
|
foreach ($base as $key => $value) {
|
||||||
|
$base[$key] = $this->round2((float) $value + (float) ($add[$key] ?? 0));
|
||||||
|
}
|
||||||
|
return $base;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDiaryDay(int $user_id, string $day_date): array|false
|
||||||
|
{
|
||||||
|
$rows = $this->diaryDays()->getList(array(
|
||||||
|
'user_id' => $user_id,
|
||||||
|
'day_date' => $day_date
|
||||||
|
));
|
||||||
|
if (!is_array($rows) || count($rows) <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return $rows[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function ensureDiaryDay(int $user_id, string $day_date): array
|
||||||
|
{
|
||||||
|
$day = $this->getDiaryDay($user_id, $day_date);
|
||||||
|
if (is_array($day)) {
|
||||||
|
return $this->mapDiaryDay($day);
|
||||||
|
}
|
||||||
|
$dayId = $this->diaryDays()->diaryDaySave(array(
|
||||||
|
'user_id' => $user_id,
|
||||||
|
'day_date' => $day_date,
|
||||||
|
'created_at' => '`NOW`'
|
||||||
|
));
|
||||||
|
if ($dayId === false) {
|
||||||
|
throw new \Exception('Failed to create diary day');
|
||||||
|
}
|
||||||
|
$created = $this->diaryDays()->diaryDay((int) $dayId);
|
||||||
|
if (!is_array($created)) {
|
||||||
|
throw new \Exception('Failed to load created diary day');
|
||||||
|
}
|
||||||
|
return $this->mapDiaryDay($created);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildDiaryDay(int $user_id, string $day_date, bool $with_totals): array
|
||||||
|
{
|
||||||
|
$day = $this->getDiaryDay($user_id, $day_date);
|
||||||
|
if (!is_array($day)) {
|
||||||
|
$ret = array(
|
||||||
|
'user_id' => $user_id,
|
||||||
|
'day_date' => $day_date,
|
||||||
|
'diary_day_id' => null,
|
||||||
|
'entries' => array()
|
||||||
|
);
|
||||||
|
if ($with_totals) {
|
||||||
|
$ret['totals'] = $this->emptyTotals();
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
$day = $this->mapDiaryDay($day);
|
||||||
|
$entriesRows = $this->diaryEntries()->search('diaryEntries')
|
||||||
|
->where(array('diary_day_id' => (int) $day['diary_day_id']))
|
||||||
|
->order(array('diary_entry_id' => 'ASC'))
|
||||||
|
->toArray();
|
||||||
|
$entriesRows = is_array($entriesRows) ? $entriesRows : array();
|
||||||
|
$entries = array();
|
||||||
|
$totals = $this->emptyTotals();
|
||||||
|
foreach ($entriesRows as $row) {
|
||||||
|
$entry = $this->mapDiaryEntry($row);
|
||||||
|
$meal = $this->mapMeal($this->getMealOwned($user_id, (int) $entry['meal_id']));
|
||||||
|
$entry['meal'] = $meal;
|
||||||
|
if ($with_totals) {
|
||||||
|
$mealTotals = $this->calculateMealTotals((int) $entry['meal_id']);
|
||||||
|
$entry['meal_totals'] = $mealTotals;
|
||||||
|
$totals = $this->addTotals($totals, $mealTotals);
|
||||||
|
}
|
||||||
|
$entries[] = $entry;
|
||||||
|
}
|
||||||
|
$ret = array(
|
||||||
|
'user_id' => $user_id,
|
||||||
|
'day_date' => $day_date,
|
||||||
|
'diary_day_id' => (int) $day['diary_day_id'],
|
||||||
|
'entries' => $entries
|
||||||
|
);
|
||||||
|
if ($with_totals) {
|
||||||
|
$ret['totals'] = $totals;
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mapIngredient(array $row): array
|
||||||
|
{
|
||||||
|
$row['ingredient_id'] = (int) $row['ingredient_id'];
|
||||||
|
$row['user_id'] = is_null($row['user_id']) ? null : (int) $row['user_id'];
|
||||||
|
$row['protein_g_100'] = $this->round2((float) $row['protein_g_100']);
|
||||||
|
$row['carbs_g_100'] = $this->round2((float) $row['carbs_g_100']);
|
||||||
|
$row['sugar_g_100'] = $this->round2((float) $row['sugar_g_100']);
|
||||||
|
$row['fat_g_100'] = $this->round2((float) $row['fat_g_100']);
|
||||||
|
$row['fiber_g_100'] = $this->round2((float) $row['fiber_g_100']);
|
||||||
|
$row['kcal_100'] = $this->round2((float) $row['kcal_100']);
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mapMeal(array $row): array
|
||||||
|
{
|
||||||
|
$row['meal_id'] = (int) $row['meal_id'];
|
||||||
|
$row['user_id'] = (int) $row['user_id'];
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mapMealItem(array $row): array
|
||||||
|
{
|
||||||
|
$row['meal_item_id'] = (int) $row['meal_item_id'];
|
||||||
|
$row['meal_id'] = (int) $row['meal_id'];
|
||||||
|
$row['ingredient_id'] = (int) $row['ingredient_id'];
|
||||||
|
$row['grams'] = $this->round2((float) $row['grams']);
|
||||||
|
$row['position'] = (int) $row['position'];
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mapDiaryDay(array $row): array
|
||||||
|
{
|
||||||
|
$row['diary_day_id'] = (int) $row['diary_day_id'];
|
||||||
|
$row['user_id'] = (int) $row['user_id'];
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function mapDiaryEntry(array $row): array
|
||||||
|
{
|
||||||
|
$row['diary_entry_id'] = (int) $row['diary_entry_id'];
|
||||||
|
$row['diary_day_id'] = (int) $row['diary_day_id'];
|
||||||
|
$row['meal_id'] = (int) $row['meal_id'];
|
||||||
|
return $row;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user