From 6dfadb75406727fe0126ccddc667223afa4da07c Mon Sep 17 00:00:00 2001 From: sotor186 Date: Thu, 5 Oct 2023 02:12:54 +1300 Subject: [PATCH 1/2] Updated recipe cards to include ingredients and instructions --- assets/app.html | 1 + assets/css/styles.css | 49 ++++++++++++---------------- assets/scripts/script.js | 70 ++++++++++++++++++++++++++++++++-------- src/MyApp.cpp | 62 ++++++++++++++++++++++++----------- src/recipeDatabase.cpp | 8 +++-- 5 files changed, 129 insertions(+), 61 deletions(-) diff --git a/assets/app.html b/assets/app.html index f0ce033..933b923 100644 --- a/assets/app.html +++ b/assets/app.html @@ -25,5 +25,6 @@

Recipes

+ \ No newline at end of file diff --git a/assets/css/styles.css b/assets/css/styles.css index eb51d31..814d0c3 100644 --- a/assets/css/styles.css +++ b/assets/css/styles.css @@ -180,67 +180,60 @@ button:active, } -/* recipe CARD STYLING */ +/* RECIPE CARD STYLING */ #search-results { - display: flex; - flex-wrap: wrap; - /* display: grid; - grid-template-columns: auto 1fr; */ + /*width : 100%;*/ + display: grid; + grid-template-columns: repeat(auto-fill, minmax(auto, 1fr)); gap: 10px; + align-items: start; } .recipe-card { display: flex; - align-items: center; + flex-direction: column; border-radius: 20px; padding: 10px; margin: 10px; background-color: white; box-shadow: 0 0 10px lightgrey; - max-width: 400px; - /* max-width: 500px; */ -} - -.recipe-name { - font-weight: bold; - font-size: large; - margin-bottom: 10px; + width: calc(33.333% - 20px); + box-sizing: border-box; } .recipe-card button { - margin-right: 10px; + margin-top: 10px; } -.recipe-image img{ - width: 150px; - height: 150px; +.recipe-image img { + width: 100%; + height: 100%; + object-fit: cover; + object-position: center; + display: block; } .recipe-image { - width: 150px; - height: 150px; - + width: 100%; + height: 200px; + border-top-left-radius: 20px; + border-top-right-radius: 20px; overflow: hidden; - border-radius: 10px; - object-fit: cover; - object-position: center; - } .recipe-info { flex-grow: 1; margin-left: 20px; max-width: 300px; + padding: 10px 0; } .recipe-info .recipe-category { font-style: italic; } - - .recipe-card h3 { - margin: 0; + margin: 10px 0; } diff --git a/assets/scripts/script.js b/assets/scripts/script.js index 4a18799..0f210d5 100644 --- a/assets/scripts/script.js +++ b/assets/scripts/script.js @@ -83,15 +83,21 @@ function createStars(rating) { return starsWrapper; } +var msnry; +// Wait for the content to load +document.addEventListener('DOMContentLoaded', function() { + // Initialize masonry + var grid = document.querySelector('#search-results'); + msnry = new Masonry( grid, { + // options + itemSelector: '.recipe-card', + columnWidth: '.recipe-card', + percentPosition: true + }); +}); + function displayCard(recipe, location) { const searchResults = document.getElementById(location); - - let recipeName = recipe.recipeName; - if(recipeName.length>18){ - recipeName = recipeName.substring(0, 17); - recipeName+= "..."; - } - // Create a div for the card const card = document.createElement('div'); card.className = 'recipe-card'; @@ -101,9 +107,9 @@ function displayCard(recipe, location) { recipeInfo.className = 'recipe-info'; // Add name to the card - const name = document.createElement('div'); + const name = document.createElement('h2'); name.className = 'recipe-name'; - name.textContent = recipeName; + name.textContent = recipe.recipeName; recipeInfo.appendChild(name); // Add image if it exists to the card @@ -137,6 +143,40 @@ function displayCard(recipe, location) { rating.appendChild(starsWrapper); recipeInfo.appendChild(rating); + // Add ingredients to the card + const ingredientsDiv = document.createElement('div'); + ingredientsDiv.className = 'recipe-ingredients'; + const ingredientsHeader = document.createElement('h3'); + ingredientsHeader.textContent = 'Ingredients'; + ingredientsDiv.appendChild(ingredientsHeader); + const ingredientsList = document.createElement('ul'); + const ingredientsArray = JSON.parse(GetIngredientsByRecipe(recipe.recipeId)); + for (const ingredient of ingredientsArray) { + const ingredientItem = document.createElement('li'); + ingredientItem.textContent = ingredient.Name; + ingredientsList.appendChild(ingredientItem); + } + ingredientsDiv.appendChild(ingredientsList); + recipeInfo.appendChild(ingredientsDiv); + + // Add instructions to the card + if (recipe.instructions && recipe.instructions.trim() !== '') { + const instructionsDiv = document.createElement('div'); + instructionsDiv.className = 'recipe-instructions'; + const instructionsHeader = document.createElement('h3'); + instructionsHeader.textContent = 'Instructions'; + instructionsDiv.appendChild(instructionsHeader); + const instructionList = document.createElement('ol'); + const instructionArray = recipe.instructions.split('|'); + for (const instruction of instructionArray) { + const instructionItem = document.createElement('li'); + instructionItem.textContent = instruction.trim(); + instructionList.appendChild(instructionItem); + } + instructionsDiv.appendChild(instructionList); + recipeInfo.appendChild(instructionsDiv); + } + // Add favorite button to the card const favourite = document.createElement("button"); favourite.className = ("favourite-icon"); @@ -212,16 +252,18 @@ function displayCard(recipe, location) { popupDiv.appendChild(buttonAddMeal); popupContainer.appendChild(popupDiv); - + recipeInfo.appendChild(favourite); recipeInfo.appendChild(addSymbol); recipeInfo.appendChild(popupContainer); - - card.appendChild(recipeInfo); - searchResults.appendChild(card); + + if (msnry) { + msnry.appended(card); + msnry.layout(); + } } @@ -383,3 +425,5 @@ function closePopup() { const popup = document.getElementById("popup"); popup.style.display = 'none'; } + + diff --git a/src/MyApp.cpp b/src/MyApp.cpp index bb206f0..9f31313 100644 --- a/src/MyApp.cpp +++ b/src/MyApp.cpp @@ -120,6 +120,24 @@ std::string MyApp::removeQuotes(const std::string &input) return result; } +std::string escapeJsonString(const std::string& input) { + std::ostringstream ss; + for (auto c : input) { + switch (c) { + case '"': ss << "\\\""; break; + case '\\': ss << "\\\\"; break; + case '/': ss << "\\/"; break; + case '\b': ss << "\\b"; break; + case '\f': ss << "\\f"; break; + case '\n': ss << "\\n"; break; + case '\r': continue; // Just skip the carriage return + case '\t': ss << "\\t"; break; + default: ss << c; break; + } + } + return ss.str(); +} + std::string MyApp::convertRecipesToJson(const std::vector &recipes) { std::cout << "convertRecipesToJson called" << std::endl; @@ -175,6 +193,7 @@ std::string MyApp::convertRecipesToJson(const std::vector &recipes) ss << "\"recipeName\": \"" << (recipe.getName()) << "\","; ss << "\"recipeCalories\": \"" << (recipe.getCalories()) << "\","; ss << "\"firstRating\": \"" << (review.getRating()) << "\","; + ss << "\"instructions\": \"" << escapeJsonString(recipe.getInstructions()) << "\","; ss << "\"recipeImageURL\": \"" << removeQuotes(image.getImageURL()) + "\""; ss << " }"; } @@ -211,31 +230,38 @@ JSValue MyApp::SearchRecipes(const JSObject &thisObject, const JSArgs &args) } JSValue MyApp::GetIngredientsByRecipe(const JSObject &thisObject, const JSArgs &args) - { - // std::cout << "GetIngredientsByRecipe called" << std::endl; + std::cout << "GetIngredientsByRecipe called" << std::endl; + + int recipeId = args[0].ToInteger(); + RecipeDatabase recipeDB; + std::vector ingredients = recipeDB.getIngredientsByRecipe(recipeId); - int recipeId = args[0].ToInteger(); + std::ostringstream jsonIngredients; + + jsonIngredients << "["; - RecipeDatabase recipeDB; - std::vector ingredients = recipeDB.getIngredientsByRecipe(recipeId); + for (const Ingredient &ingredient : ingredients) + { + jsonIngredients << "{"; + jsonIngredients << "\"IngredientId\": " << ingredient.getIngredientId() << ", "; + jsonIngredients << "\"Name\": \"" << escapeJsonString(ingredient.getIngredientName()) << "\""; + jsonIngredients << "},"; + } - std::string jsonIngredients = "["; + if (!ingredients.empty()){ + std::string temp = jsonIngredients.str(); + temp.pop_back(); + jsonIngredients.str(""); + jsonIngredients.clear(); + jsonIngredients << temp; + } - for (const Ingredient &ingredient : ingredients) - { - jsonIngredients += "{ "; - jsonIngredients += "\"ingredientId\": " + removeQuotes(std::to_string(ingredient.getIngredientId())) + ","; - jsonIngredients += "\"ingredientName\": \"" + removeQuotes(ingredient.getIngredientName()) + "\""; - jsonIngredients += " },"; - } - if (jsonIngredients.back() == ',') - jsonIngredients.pop_back(); - jsonIngredients += "]"; + jsonIngredients << "]"; - // std::cout << "jsonIngredients: " << jsonIngredients.c_str() << std::endl; + //std::cout << "jsonIngredients: " << jsonIngredients.str() << std::endl; - return JSValue(jsonIngredients.c_str()); + return JSValue(jsonIngredients.str().c_str()); } JSValue MyApp::GetReviewsByRecipe(const JSObject& thisObject, const JSArgs& args) { diff --git a/src/recipeDatabase.cpp b/src/recipeDatabase.cpp index 3f8ba01..7e97114 100644 --- a/src/recipeDatabase.cpp +++ b/src/recipeDatabase.cpp @@ -224,11 +224,14 @@ std::vector RecipeDatabase::getAllReviewsForRecipes(const std::vector RecipeDatabase::getIngredientsByRecipe(int recipeId){ std::vector ingredients; - std::string query = "SELECT * FROM ingredients WHERE recipeId = " + std::to_string(recipeId) + ";"; + std::string query = + "SELECT ingredients.name, ingredients.ingredientId " + "FROM recipe_ingredients JOIN ingredients " + "ON recipe_ingredients.ingredientId = ingredients.ingredientId " + "WHERE recipeId = " + std::to_string(recipeId) + ";"; try { auto con = dbConn.getConnection(); @@ -236,6 +239,7 @@ std::vector RecipeDatabase::getIngredientsByRecipe(int recipeId){ std::unique_ptr res(stmt->executeQuery(query)); while (res->next()) { + //std::cout << "DB Ingredient ID: " << res->getInt("ingredientId") << " Name: " << res->getString("name") << std::endl; ingredients.push_back( Ingredient( res->getInt("ingredientId"), From d089f6dee53f3d279dc4a512a0744069c41f7f5e Mon Sep 17 00:00:00 2001 From: sotor186 Date: Fri, 6 Oct 2023 04:39:41 +1300 Subject: [PATCH 2/2] Fixed slow ingredient search --- assets/css/styles.css | 15 ++++---- assets/scripts/script.js | 82 +++++++++++++++++++++++++++------------- src/MyApp.cpp | 57 ++++++++++++++++------------ src/MyApp.h | 2 +- src/recipeDatabase.cpp | 38 ++++++++++++------- src/recipeDatabase.h | 31 +++++++-------- 6 files changed, 135 insertions(+), 90 deletions(-) diff --git a/assets/css/styles.css b/assets/css/styles.css index 814d0c3..f9aae53 100644 --- a/assets/css/styles.css +++ b/assets/css/styles.css @@ -179,26 +179,25 @@ button:active, background-color: #f2f2f2; } - /* RECIPE CARD STYLING */ -#search-results { +#search-results, #saved-recipe-container, #andrews-recipe-container, #featured-recipe-container { /*width : 100%;*/ display: grid; - grid-template-columns: repeat(auto-fill, minmax(auto, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + grid-template-rows: masonry; gap: 10px; - align-items: start; + /*align-items: start;*/ } .recipe-card { - display: flex; - flex-direction: column; + /*display: flex; + flex-direction: column; */ border-radius: 20px; padding: 10px; margin: 10px; background-color: white; box-shadow: 0 0 10px lightgrey; - width: calc(33.333% - 20px); - box-sizing: border-box; + max-width: 400px; } .recipe-card button { diff --git a/assets/scripts/script.js b/assets/scripts/script.js index ef0fe72..db3ce17 100644 --- a/assets/scripts/script.js +++ b/assets/scripts/script.js @@ -55,12 +55,19 @@ function searchRecipes() { const jsonRecipes = SearchRecipes(ingredients); const recipes = JSON.parse(jsonRecipes); + + const recipeIds = recipes.map(r => r.recipeId); + // Fetch ingredients for all these recipes + const jsonIngredients = GetIngredientsForRecipes(recipeIds); + const allIngredients = JSON.parse(jsonIngredients); + console.log("Recipes:", recipes); document.getElementById('search-results').innerHTML = ""; document.getElementById('search-results').innerHTML = ""; for (let recipe of recipes) { - displayCard(recipe, "search-results"); + const recipeIngredients = allIngredients[recipe.recipeId]; + displayCard(recipe, "search-results", recipeIngredients); } } catch (error) { console.error("Error fetching recipes:", error); @@ -83,20 +90,22 @@ function createStars(rating) { return starsWrapper; } +/* var msnry; // Wait for the content to load -document.addEventListener('DOMContentLoaded', function() { +document.addEventListener('DOMContentLoaded', function () { // Initialize masonry var grid = document.querySelector('#search-results'); - msnry = new Masonry( grid, { - // options - itemSelector: '.recipe-card', - columnWidth: '.recipe-card', - percentPosition: true + msnry = new Masonry(grid, { + // options + itemSelector: '.recipe-card', + columnWidth: '.recipe-card', + percentPosition: true }); }); +*/ -function displayCard(recipe, location) { +function displayCard(recipe, location, ingredientsArray) { const searchResults = document.getElementById(location); // Create a div for the card const card = document.createElement('div'); @@ -150,7 +159,6 @@ function displayCard(recipe, location) { ingredientsHeader.textContent = 'Ingredients'; ingredientsDiv.appendChild(ingredientsHeader); const ingredientsList = document.createElement('ul'); - const ingredientsArray = JSON.parse(GetIngredientsByRecipe(recipe.recipeId)); for (const ingredient of ingredientsArray) { const ingredientItem = document.createElement('li'); ingredientItem.textContent = ingredient.Name; @@ -180,7 +188,7 @@ function displayCard(recipe, location) { // Add favorite button to the card const favourite = document.createElement("button"); favourite.className = ("favourite-icon"); - favourite.innerHTML = "♥"; + favourite.innerHTML = "♥"; favourite.setAttribute("aria-label", "Add to favourites"); favourite.onclick = function () { addToSaved(recipe.recipeId); @@ -252,7 +260,7 @@ function displayCard(recipe, location) { popupDiv.appendChild(buttonAddMeal); popupContainer.appendChild(popupDiv); - + recipeInfo.appendChild(favourite); recipeInfo.appendChild(addSymbol); recipeInfo.appendChild(popupContainer); @@ -260,10 +268,11 @@ function displayCard(recipe, location) { card.appendChild(recipeInfo); searchResults.appendChild(card); - if (msnry) { - msnry.appended(card); - msnry.layout(); - } + //if (msnry) { + // msnry.appended(card); + // msnry.layout(); + //} + } @@ -276,29 +285,36 @@ function addToSaved(recipeId) { let isSaved = false; for (let i = 0; i < savedRecipes.length; i++) { - if(savedRecipes[i] == recipeId){ - isSaved=true; + if (savedRecipes[i] == recipeId) { + isSaved = true; } } - if(!isSaved){ + if (!isSaved) { savedRecipes.push(recipeId); console.log("saved recipe(s):\n") SaveRecipe(recipeId); - }else{ + } else { console.log("already saved\n") } } //function which retrieves saved recipes from c -function getSaved(){ +function getSaved() { try { const jsonRecipes = GetSaved(); const recipes = JSON.parse(jsonRecipes); + + const recipeIds = recipes.map(r => r.recipeId); + // Fetch ingredients for all these recipes + const jsonIngredients = GetIngredientsForRecipes(recipeIds); + const allIngredients = JSON.parse(jsonIngredients); + console.log("Recipes:", recipes); document.getElementById('saved-recipe-container').innerHTML = ""; for (let recipe of recipes) { - displayCard(recipe, 'saved-recipe-container'); + const recipeIngredients = allIngredients[recipe.recipeId]; + displayCard(recipe, 'saved-recipe-container', recipeIngredients); } } catch (error) { console.error("Error fetching recipes:", error); @@ -306,15 +322,22 @@ function getSaved(){ } } -function getAndrews(){ +function getAndrews() { try { const jsonRecipes = GetAndrews(); const recipes = JSON.parse(jsonRecipes); + + const recipeIds = recipes.map(r => r.recipeId); + // Fetch ingredients for all these recipes + const jsonIngredients = GetIngredientsForRecipes(recipeIds); + const allIngredients = JSON.parse(jsonIngredients); + console.log("Recipes:", recipes); document.getElementById('andrews-recipe-container').innerHTML = ""; for (let recipe of recipes) { - displayCard(recipe, 'andrews-recipe-container'); + const recipeIngredients = allIngredients[recipe.recipeId]; + displayCard(recipe, 'andrews-recipe-container', recipeIngredients); } } catch (error) { console.error("Error fetching recipes:", error); @@ -323,15 +346,22 @@ function getAndrews(){ } -function getFeatured(){ +function getFeatured() { try { const jsonRecipes = ShowFeatured(); const recipes = JSON.parse(jsonRecipes); + + const recipeIds = recipes.map(r => r.recipeId); + // Fetch ingredients for all these recipes + const jsonIngredients = GetIngredientsForRecipes(recipeIds); + const allIngredients = JSON.parse(jsonIngredients); + console.log("Recipes:", recipes); document.getElementById('featured-recipe-container').innerHTML = ""; for (let recipe of recipes) { - displayCard(recipe, 'featured-recipe-container'); + const recipeIngredients = allIngredients[recipe.recipeId]; + displayCard(recipe, 'featured-recipe-container', recipeIngredients); } } catch (error) { console.error("Error fetching recipes:", error); @@ -359,7 +389,7 @@ function addToPlanner(recipeName, recipeId) { const selectedMeal = mealOptions.value; let mealOption = []; addToJSON(selectedDay, selectedMeal, recipeName, currRecipeId); - mealOption.push(recipeName, currRecipeId, selectedDay, selectedMeal ); + mealOption.push(recipeName, currRecipeId, selectedDay, selectedMeal); mealPlanner.push(mealOption); closePopup(); } diff --git a/src/MyApp.cpp b/src/MyApp.cpp index 05e06fe..d7047bc 100644 --- a/src/MyApp.cpp +++ b/src/MyApp.cpp @@ -7,7 +7,7 @@ #include #include "timer.h" -#define WINDOW_WIDTH 1000 +#define WINDOW_WIDTH 1200 #define WINDOW_HEIGHT 800 std::vector savedRecipes; @@ -233,37 +233,44 @@ JSValue MyApp::SearchRecipes(const JSObject &thisObject, const JSArgs &args) return JSValue(jsonRecipes.c_str()); } -JSValue MyApp::GetIngredientsByRecipe(const JSObject &thisObject, const JSArgs &args) -{ - std::cout << "GetIngredientsByRecipe called" << std::endl; +JSValue MyApp::GetIngredientsForRecipes(const JSObject &thisObject, const JSArgs &args) { + std::cout << "GetIngredientsForRecipes called" << std::endl; + + std::vector recipeIds; + if (args[0].IsArray()) { + JSArray recipeIdArray = args[0].ToArray(); + for (size_t i = 0; i < recipeIdArray.length(); i++) { + recipeIds.push_back(recipeIdArray[i].ToInteger()); + } + } - int recipeId = args[0].ToInteger(); RecipeDatabase recipeDB; - std::vector ingredients = recipeDB.getIngredientsByRecipe(recipeId); + std::map> ingredientsMap = recipeDB.getIngredientsForRecipes(recipeIds); std::ostringstream jsonIngredients; - - jsonIngredients << "["; - - for (const Ingredient &ingredient : ingredients) - { - jsonIngredients << "{"; - jsonIngredients << "\"IngredientId\": " << ingredient.getIngredientId() << ", "; - jsonIngredients << "\"Name\": \"" << escapeJsonString(ingredient.getIngredientName()) << "\""; - jsonIngredients << "},"; + jsonIngredients << "{"; + + for (const auto& pair : ingredientsMap) { + int recipeId = pair.first; + const std::vector& ingredients = pair.second; + jsonIngredients << "\"" << recipeId << "\": ["; + for (const Ingredient &ingredient : ingredients) { + jsonIngredients << "{"; + jsonIngredients << "\"IngredientId\": " << ingredient.getIngredientId() << ", "; + jsonIngredients << "\"Name\": \"" << escapeJsonString(ingredient.getIngredientName()) << "\""; + jsonIngredients << "},"; + } + if (!ingredients.empty()) { + jsonIngredients.seekp(-1, jsonIngredients.cur); // Remove the trailing comma + } + jsonIngredients << "],"; } - if (!ingredients.empty()){ - std::string temp = jsonIngredients.str(); - temp.pop_back(); - jsonIngredients.str(""); - jsonIngredients.clear(); - jsonIngredients << temp; + if (!ingredientsMap.empty()) { + jsonIngredients.seekp(-1, jsonIngredients.cur); // Remove the trailing comma } - jsonIngredients << "]"; - - //std::cout << "jsonIngredients: " << jsonIngredients.str() << std::endl; + jsonIngredients << "}"; return JSValue(jsonIngredients.str().c_str()); } @@ -479,7 +486,7 @@ void MyApp::OnDOMReady(ultralight::View *caller, global["GetSaved"] = BindJSCallbackWithRetval(&MyApp::GetSaved); global["GetAndrews"] = BindJSCallbackWithRetval(&MyApp::GetAndrews); global["ShowFeatured"] = BindJSCallbackWithRetval(&MyApp::ShowFeatured); - global["GetIngredientsByRecipe"] = BindJSCallbackWithRetval(&MyApp::GetIngredientsByRecipe); + global["GetIngredientsForRecipes"] = BindJSCallbackWithRetval(&MyApp::GetIngredientsForRecipes); global["GetReviewsByRecipe"] = BindJSCallbackWithRetval(&MyApp::GetReviewsByRecipe); } diff --git a/src/MyApp.h b/src/MyApp.h index 2a25c17..8ad4766 100644 --- a/src/MyApp.h +++ b/src/MyApp.h @@ -55,7 +55,7 @@ class MyApp : public AppListener, void SaveOnExit(); - JSValue GetIngredientsByRecipe(const JSObject& thisObject, const JSArgs& args); +JSValue GetIngredientsForRecipes(const JSObject &thisObject, const JSArgs &args); JSValue GetReviewsByRecipe(const JSObject& thisObject, const JSArgs& args); diff --git a/src/recipeDatabase.cpp b/src/recipeDatabase.cpp index 7e97114..584f74d 100644 --- a/src/recipeDatabase.cpp +++ b/src/recipeDatabase.cpp @@ -224,28 +224,40 @@ std::vector RecipeDatabase::getAllReviewsForRecipes(const std::vector RecipeDatabase::getIngredientsByRecipe(int recipeId){ - std::vector ingredients; +std::map> RecipeDatabase::getIngredientsForRecipes(const std::vector& recipeIds) { + std::map> ingredientsMap; - std::string query = - "SELECT ingredients.name, ingredients.ingredientId " + if (recipeIds.empty()) { + return ingredientsMap; + } + + std::stringstream query; + query << + "SELECT ingredients.name, ingredients.ingredientId, recipe_ingredients.recipeId " "FROM recipe_ingredients JOIN ingredients " "ON recipe_ingredients.ingredientId = ingredients.ingredientId " - "WHERE recipeId = " + std::to_string(recipeId) + ";"; + "WHERE recipe_ingredients.recipeId IN ("; + + for (size_t i = 0; i < recipeIds.size(); ++i) { + query << recipeIds[i]; + if (i < recipeIds.size() - 1) { + query << ","; + } + } + query << ");"; try { auto con = dbConn.getConnection(); std::unique_ptr stmt(con->createStatement()); - std::unique_ptr res(stmt->executeQuery(query)); + std::unique_ptr res(stmt->executeQuery(query.str())); while (res->next()) { - //std::cout << "DB Ingredient ID: " << res->getInt("ingredientId") << " Name: " << res->getString("name") << std::endl; - ingredients.push_back( - Ingredient( - res->getInt("ingredientId"), - res->getString("name") - ) + int recipeId = res->getInt("recipeId"); + Ingredient ingredient( + res->getInt("ingredientId"), + res->getString("name") ); + ingredientsMap[recipeId].push_back(ingredient); } } catch (sql::SQLException &e) { std::cout << "# ERR: SQLException in " << __FILE__ << " on line " << __LINE__ << std::endl; @@ -254,7 +266,7 @@ std::vector RecipeDatabase::getIngredientsByRecipe(int recipeId){ std::cout << ", SQLState: " << e.getSQLState() << " )" << std::endl; } - return ingredients; + return ingredientsMap; } std::vector RecipeDatabase::getAllRecipeImagesForRecipes(const std::vector& recipes) diff --git a/src/recipeDatabase.h b/src/recipeDatabase.h index 2cc8173..626a8df 100644 --- a/src/recipeDatabase.h +++ b/src/recipeDatabase.h @@ -9,30 +9,28 @@ #include #include "ingredient.h" - /** * @class recipeDatabase * @brief Manages interactions between the application and the database for recipes. - * + * * The recipeDatabase class provides methods to perform operations on recipes - * stored in a database. It uses the DatabaseConnection class to establish and + * stored in a database. It uses the DatabaseConnection class to establish and * manage the database connection. */ -class RecipeDatabase { +class RecipeDatabase +{ private: - /** @brief Connection to the database. */ DatabaseConnection dbConn; public: - /** * @brief Fetches a recipe from the database based on its unique identifier. - * + * * Queries the database for a recipe with the given ID. If found, constructs a recipe object * with the relevant data and returns it. In the case of a SQL exception, an error message is printed * to the console. If no recipe matches the ID or there's an exception, a default recipe object is returned. - * + * * @param id The unique identifier of the recipe to fetch. * @return The recipe object corresponding to the given ID or a default recipe object if not found. */ @@ -40,7 +38,7 @@ class RecipeDatabase { /** * @brief Get the Recipe Image By Id object - * + * * @param id The unique identifier of the recipe to fetch the image from. * @param imageNumber The image number of the recipe image to fetch from a given recipe. * @return The RecipeImage object corresponding to the given ID or a default RecipeImage object if not found. @@ -50,15 +48,15 @@ class RecipeDatabase { /** * @brief Construct a new Recipe Database::get Recipes By Search object - * - * @param search + * + * @param search */ std::vector getRecipesBySearch(const std::vector &ingredients); /** * @brief Get the Reviews By Recipe Id object - * + * * @param recipeId The unique identifier of the recipe to fetch the reviews from. * @return A vector of Review objects corresponding to the given recipe ID or an empty vector if not found. */ @@ -67,18 +65,17 @@ class RecipeDatabase { /** * @brief Get a list of Ingredients By Recipe Id - * + * * @param recipeId The unique identifier of the recipe to fetch the ingredients from. * @return A vector of Ingredient objects corresponding to the given recipe ID or an empty vector if not found. */ - std::vector getIngredientsByRecipe(int recipeId); + std::map> getIngredientsForRecipes(const std::vector &recipeIds); std::vector getAllRecipeImagesForRecipes(const std::vector &recipes); - std::vector getAllReviewsForRecipes(const std::vector& recipes); + std::vector getAllReviewsForRecipes(const std::vector &recipes); std::vector getFeaturedRecipes(); - }; -#endif // recipe_DATABASE_H +#endif // recipe_DATABASE_H