diff --git a/changelog/62_UNRELEASED_xxxx-xx-xx.md b/changelog/62_UNRELEASED_xxxx-xx-xx.md index bf01ffeb..7292b2bb 100644 --- a/changelog/62_UNRELEASED_xxxx-xx-xx.md +++ b/changelog/62_UNRELEASED_xxxx-xx-xx.md @@ -57,6 +57,7 @@ - Recipe printing improvements (thanks @Ape) - Calories are now always displayed per single serving (on the recipe and meal plan page) - The note of an ingredient will now also be added to the corresponding shopping list item when using "Put missing products on the shopping list" +- Fixed that the recipe page was slow when there were a lot meal plan recipe entries - Fixed that "Only check if any amount is in stock" (recipe ingredient option) didn't work for stock amounts < 1 - Fixed that when adding missing items to the shopping list, on the popup deselected items got also added - Fixed that the amount of self produced products with tare weight handling enabled was wrong ("Produces product" recipe option) @@ -64,6 +65,7 @@ - Fixed that the ingredient amount was wrong when using a to the product indirectly related quantity unit ### Meal plan fixes +- Improved the meal plan page loading time (drastically when having a big history of meal plan entries) - Fixed that stock fulfillment checking used the desired servings of the recipe (those selected on the recipes page, not them from the meal plan entry) ### Chores fixes diff --git a/controllers/RecipesController.php b/controllers/RecipesController.php index 6fd30e6a..5e6ce33a 100644 --- a/controllers/RecipesController.php +++ b/controllers/RecipesController.php @@ -8,11 +8,22 @@ class RecipesController extends BaseController { public function MealPlan(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) { + // Load +- 8 days to always include week boundaries + + if (isset($request->getQueryParams()['week']) && IsIsoDate($request->getQueryParams()['week'])) + { + $week = $request->getQueryParams()['week']; + $mealPlanWhereTimespan = "day BETWEEN DATE('$week', '-8 day') AND DATE('$week', '+8 day')"; + } + else + { + $mealPlanWhereTimespan = "day BETWEEN DATE(DATE('now', 'localtime'), '-8 day') AND DATE(DATE('now', 'localtime'), '+8 day')"; + } + $recipes = $this->getDatabase()->recipes()->where('type', RecipesService::RECIPE_TYPE_NORMAL)->fetchAll(); $events = []; - - foreach ($this->getDatabase()->meal_plan() as $mealPlanEntry) + foreach ($this->getDatabase()->meal_plan()->where($mealPlanWhereTimespan) as $mealPlanEntry) { $recipe = FindObjectInArrayByPropertyValue($recipes, 'id', $mealPlanEntry['recipe_id']); $title = ''; @@ -23,7 +34,6 @@ class RecipesController extends BaseController } $productDetails = null; - if ($mealPlanEntry['product_id'] !== null) { $productDetails = $this->getStockService()->GetProductDetails($mealPlanEntry['product_id']); @@ -44,8 +54,8 @@ class RecipesController extends BaseController return $this->renderPage($response, 'mealplan', [ 'fullcalendarEventSources' => $events, 'recipes' => $recipes, - 'internalRecipes' => $this->getDatabase()->recipes()->whereNot('type', RecipesService::RECIPE_TYPE_NORMAL)->fetchAll(), - 'recipesResolved' => $this->getRecipesService()->GetRecipesResolved(), + 'internalRecipes' => $this->getDatabase()->recipes()->where("id IN (SELECT recipe_id FROM meal_plan_internal_recipe_relation WHERE $mealPlanWhereTimespan)")->fetchAll(), + 'recipesResolved' => $this->getRecipesService()->GetRecipesResolved2("recipe_id IN (SELECT recipe_id FROM meal_plan_internal_recipe_relation WHERE $mealPlanWhereTimespan)"), 'products' => $this->getDatabase()->products()->orderBy('name', 'COLLATE NOCASE'), 'quantityUnits' => $this->getDatabase()->quantity_units()->orderBy('name', 'COLLATE NOCASE'), 'quantityUnitConversionsResolved' => $this->getDatabase()->quantity_unit_conversions_resolved() @@ -55,7 +65,7 @@ class RecipesController extends BaseController public function Overview(\Psr\Http\Message\ServerRequestInterface $request, \Psr\Http\Message\ResponseInterface $response, array $args) { $recipes = $this->getDatabase()->recipes()->where('type', RecipesService::RECIPE_TYPE_NORMAL)->orderBy('name', 'COLLATE NOCASE'); - $recipesResolved = $this->getRecipesService()->GetRecipesResolved(); + $recipesResolved = $this->getRecipesService()->GetRecipesResolved('recipe_id > 0'); $selectedRecipe = null; diff --git a/migrations/0141.sql b/migrations/0141.sql new file mode 100644 index 00000000..54bce7fa --- /dev/null +++ b/migrations/0141.sql @@ -0,0 +1,43 @@ +CREATE VIEW meal_plan_internal_recipe_relation +AS + +-- Relation between a meal plan (day) and the corresponding internal recipe(s) + +SELECT mp.day, r.id AS recipe_id +FROM meal_plan mp +JOIN recipes r + ON r.name = CAST(mp.day AS TEXT) + AND r.type = 'mealplan-day' + +UNION + +SELECT mp.day, r.id AS recipe_id +FROM meal_plan mp +JOIN recipes r + ON r.name = STRFTIME('%Y-%W', mp.day) + AND r.type = 'mealplan-week' + +UNION + +SELECT mp.day, r.id AS recipe_id +FROM meal_plan mp +JOIN recipes r + ON r.name = CAST(mp.day AS TEXT) || '#' || CAST(mp.id AS TEXT) + AND r.type = 'mealplan-shadow'; + +CREATE VIEW recipes_resolved2 +AS + +-- The same as recipes_resolved but without the column "missing_products_count" to improve performance when this is not needed + +SELECT + 1 AS id, -- Dummy, LessQL needs an id column + r.id AS recipe_id, + IFNULL(MIN(rpr.need_fulfilled), 1) AS need_fulfilled, + IFNULL(MIN(rpr.need_fulfilled_with_shopping_list), 1) AS need_fulfilled_with_shopping_list, + IFNULL(SUM(rpr.costs), 0) AS costs, + IFNULL(SUM(rpr.calories), 0) AS calories +FROM recipes r +LEFT JOIN recipes_pos_resolved rpr + ON r.id = rpr.recipe_id +GROUP BY r.id; diff --git a/public/viewjs/mealplan.js b/public/viewjs/mealplan.js index 6df0e044..19c484a1 100644 --- a/public/viewjs/mealplan.js +++ b/public/viewjs/mealplan.js @@ -25,17 +25,9 @@ var calendar = $("#calendar").fullCalendar({ "defaultView": ($(window).width() < 768) ? "basicDay" : "basicWeek", "firstDay": firstDay, "height": "auto", + "defaultDate": GetUriParam("week"), "viewRender": function(view) { - if (firstRender) - { - firstRender = false - } - else - { - UpdateUriParam("week", view.start.format("YYYY-MM-DD")); - } - $(".fc-day-header").prepend('\