mirror of
https://github.com/grocy/grocy.git
synced 2025-04-29 17:45:39 +00:00
Improved page loading time of /recipes and /mealplan when having a big meal plan (closes #695)
This commit is contained in:
parent
6660e1ff73
commit
7ee15946c7
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
43
migrations/0141.sql
Normal file
43
migrations/0141.sql
Normal file
@ -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;
|
@ -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('\
|
||||
<div class="btn-group mr-2 my-1"> \
|
||||
<button type="button" class="btn btn-outline-dark btn-xs add-recipe-button""><i class="fas fa-plus"></i></a></button> \
|
||||
@ -273,15 +265,21 @@ var calendar = $("#calendar").fullCalendar({
|
||||
},
|
||||
"eventAfterAllRender": function(view)
|
||||
{
|
||||
UpdateUriParam("week", view.start.format("YYYY-MM-DD"));
|
||||
|
||||
if (firstRender)
|
||||
{
|
||||
firstRender = false
|
||||
}
|
||||
else
|
||||
{
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
RefreshLocaleNumberDisplay();
|
||||
LoadImagesLazy();
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
|
||||
if (GetUriParam("week") !== undefined)
|
||||
{
|
||||
$("#calendar").fullCalendar("gotoDate", GetUriParam("week"));
|
||||
}
|
||||
|
||||
if (!Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK)
|
||||
{
|
||||
$(".recipe-order-missing-button").addClass("d-none");
|
||||
@ -498,7 +496,7 @@ $('#save-add-product-button').on('click', function(e)
|
||||
delete jsonData.display_amount;
|
||||
jsonData.product_amount = jsonData.amount;
|
||||
delete jsonData.amount;
|
||||
jsonData.product_qu_id = jsonData.qu_id;
|
||||
jsonData.product_qu_id = $("#qu_id").val();;
|
||||
delete jsonData.qu_id;
|
||||
|
||||
if (Grocy.IsMealPlanEntryEditAction)
|
||||
@ -803,10 +801,10 @@ Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
||||
function(productDetails)
|
||||
{
|
||||
Grocy.Components.ProductAmountPicker.Reload(productDetails.product.id, productDetails.quantity_unit_stock.id);
|
||||
Grocy.Components.ProductAmountPicker.SetQuantityUnit(productDetails.quantity_unit_stock.id);
|
||||
|
||||
$('#display_amount').val(1);
|
||||
RefreshLocaleNumberInput();
|
||||
$(".input-group-productamountpicker").trigger("change");
|
||||
$('#display_amount').focus();
|
||||
$('#display_amount').select();
|
||||
$(".input-group-productamountpicker").trigger("change");
|
||||
|
@ -87,10 +87,30 @@ class RecipesService extends BaseService
|
||||
return $this->getDataBaseService()->ExecuteDbQuery($sql)->fetchAll(\PDO::FETCH_OBJ);
|
||||
}
|
||||
|
||||
public function GetRecipesResolved(): Result
|
||||
public function GetRecipesResolved($customWhere = null): Result
|
||||
{
|
||||
if ($customWhere == null)
|
||||
{
|
||||
return $this->getDatabase()->recipes_resolved();
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->getDatabase()->recipes_resolved()->where($customWhere);
|
||||
}
|
||||
}
|
||||
|
||||
// The same as GetRecipesResolved but without the column "missing_products_count" to improve performance when this is not needed
|
||||
public function GetRecipesResolved2($customWhere = null): Result
|
||||
{
|
||||
if ($customWhere == null)
|
||||
{
|
||||
return $this->getDatabase()->recipes_resolved2();
|
||||
}
|
||||
else
|
||||
{
|
||||
return $this->getDatabase()->recipes_resolved2()->where($customWhere);
|
||||
}
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user