From c6087446961b8acdc0689962eaeb5081c5acc06e Mon Sep 17 00:00:00 2001 From: Hayden Hargreaves Date: Sun, 27 Jul 2025 12:00:28 -0700 Subject: [PATCH] (FIX): Working on the nil derefs, removed tx's from select queries. Lots of the derefs were from the transactions failing, which meant that removing most of them, helped prevent them! --- internal/app/handlers/page_handler.go | 12 +- .../database/repository/recipe_repository.go | 101 +---- internal/templates/components/dropdowns.templ | 80 +++- .../templates/components/dropdowns_templ.go | 428 ++++++++++++++---- .../templates/components/search_bar.templ | 6 +- .../templates/components/search_bar_templ.go | 38 +- internal/templates/pages/favorites.templ | 2 +- internal/templates/pages/favorites_templ.go | 2 +- internal/templates/pages/home.templ | 16 +- internal/templates/pages/home_templ.go | 2 +- internal/templates/pages/search.templ | 24 +- internal/templates/pages/search_templ.go | 12 +- 12 files changed, 466 insertions(+), 257 deletions(-) mode change 100644 => 100755 internal/app/handlers/page_handler.go diff --git a/internal/app/handlers/page_handler.go b/internal/app/handlers/page_handler.go old mode 100644 new mode 100755 index 0f3629e..95716d1 --- a/internal/app/handlers/page_handler.go +++ b/internal/app/handlers/page_handler.go @@ -91,14 +91,14 @@ func FavoritesPage(ctx *gin.Context) { // Get filters from cookies if bytes, err := ctx.Cookie("search-filters"); err != nil { fmt.Printf("ERROR: Failed to get search-filter cookie. %s\n", err.Error()) - page = pages.FavoritesPage(domainRecipe.SearchFilters{}) + page = pages.FavoritesPage(nil) } else { var filters domainRecipe.SearchFilters if err := json.Unmarshal([]byte(bytes), &filters); err != nil { fmt.Printf("ERROR: Failed to unmarshal search-filter cookie. %s\n", err.Error()) - page = pages.FavoritesPage(domainRecipe.SearchFilters{}) + page = pages.FavoritesPage(nil) } else { - page = pages.FavoritesPage(filters) + page = pages.FavoritesPage(&filters) } } @@ -218,14 +218,14 @@ func SearchPage(ctx *gin.Context) { // Get filters from cookies if bytes, err := ctx.Cookie("search-filters"); err != nil { fmt.Printf("ERROR: Failed to get search-filter cookie. %s\n", err.Error()) - page = pages.SearchPage(domainRecipe.SearchFilters{}, false) + page = pages.SearchPage(nil, false) } else { var filters domainRecipe.SearchFilters if err := json.Unmarshal([]byte(bytes), &filters); err != nil { fmt.Printf("ERROR: Failed to unmarshal search-filter cookie. %s\n", err.Error()) - page = pages.SearchPage(domainRecipe.SearchFilters{}, false) + page = pages.SearchPage(nil, false) } else { - page = pages.SearchPage(filters, true) + page = pages.SearchPage(&filters, true) } } diff --git a/internal/infrastructure/database/repository/recipe_repository.go b/internal/infrastructure/database/repository/recipe_repository.go index 9a758c9..5a4d437 100644 --- a/internal/infrastructure/database/repository/recipe_repository.go +++ b/internal/infrastructure/database/repository/recipe_repository.go @@ -94,12 +94,6 @@ func (r *RecipeRepository) CreateRecipe(recipe *domain.Recipe) error { // for added safety. The repository will not check for a nil result, instead the service will. Callers // are responsible for protecting against double nil results. Any errors will be bubbled to the caller. func (r *RecipeRepository) GetRecipe(id int, userId *int) (*domain.Recipe, error) { - tx, err := r.db.Begin() - if err != nil { - tx.Rollback() - return nil, err - } - query := ` SELECT id, title, description, instructions, serves, difficulty, duration, category, ingredients, @@ -112,7 +106,7 @@ func (r *RecipeRepository) GetRecipe(id int, userId *int) (*domain.Recipe, error var ingredientBytes []byte var recipe domain.Recipe - if err := tx.QueryRow(query, id).Scan( + if err := r.db.QueryRow(query, id).Scan( &recipe.Id, &recipe.Title, &recipe.Description, @@ -167,11 +161,6 @@ func (r *RecipeRepository) GetRecipe(id int, userId *int) (*domain.Recipe, error recipe.Favorite = false } - if err := tx.Commit(); err != nil { - tx.Rollback() - return nil, err - } - return &recipe, nil } @@ -180,12 +169,6 @@ func (r *RecipeRepository) GetRecipe(id int, userId *int) (*domain.Recipe, error // will. Callers are responsible for protecting against double nil results. Any errors will be bubbled // to the caller. func (r *RecipeRepository) GetRecipes(ids []int, userId *int) ([]domain.Recipe, error) { - tx, err := r.db.Begin() - if err != nil { - tx.Rollback() - return nil, err - } - query := ` SELECT id, title, description, instructions, serves, difficulty, duration, category, ingredients, userid, modified, created @@ -196,9 +179,8 @@ func (r *RecipeRepository) GetRecipes(ids []int, userId *int) ([]domain.Recipe, var recipes []domain.Recipe - rows, err := tx.Query(query, pq.Array(ids)) + rows, err := r.db.Query(query, pq.Array(ids)) if err != nil { - tx.Rollback() return nil, fmt.Errorf("Failed to get recipes. %s", err.Error()) } defer rows.Close() @@ -266,11 +248,6 @@ func (r *RecipeRepository) GetRecipes(ids []int, userId *int) ([]domain.Recipe, recipes = append(recipes, recipe) } - if err := tx.Commit(); err != nil { - tx.Rollback() - return nil, err - } - return recipes, nil } @@ -288,12 +265,6 @@ func isBitActive(bits, pos int) bool { // // TODO: Pagination is required, to provide infinite scroll. func (r *RecipeRepository) SearchRecipes(filters domain.SearchFilters, userId *int, favorites bool) ([]domain.Recipe, error) { - tx, err := r.db.Begin() - if err != nil { - tx.Rollback() - return nil, err - } - // Compute meals type filters (there are 7 bits) var mealConditions []string for i := range 7 { @@ -442,7 +413,7 @@ func (r *RecipeRepository) SearchRecipes(filters domain.SearchFilters, userId *i query += ";" // Execute the query - rows, err := tx.Query(query) + rows, err := r.db.Query(query) if err != nil { return nil, fmt.Errorf("failed to query recipes: %w", err) } @@ -513,11 +484,6 @@ func (r *RecipeRepository) SearchRecipes(filters domain.SearchFilters, userId *i recipes = append(recipes, recipe) } - if err := tx.Commit(); err != nil { - tx.Rollback() - return nil, err - } - return recipes, nil } @@ -584,12 +550,6 @@ func (r *RecipeRepository) CreateRecipeTags(recipe domain.Recipe, tags []string) // authenticated or exists. If nothing is found, a blank slice will be returned. The resulting list // is sorted by the created dates, newest first. Any errors will be bubbled to the caller. func (r *RecipeRepository) GetUserRecipes(id int) ([]domain.Recipe, error) { - tx, err := r.db.Begin() - if err != nil { - tx.Rollback() - return nil, err - } - query := ` SELECT id, title, description, instructions, serves, difficulty, duration, category, ingredients, userid, modified, created @@ -598,7 +558,7 @@ func (r *RecipeRepository) GetUserRecipes(id int) ([]domain.Recipe, error) { ORDER BY created DESC; ` - rows, err := tx.Query(query, id) + rows, err := r.db.Query(query, id) if err != nil { return nil, fmt.Errorf("Failed to query DB for user recipes. %s\n", err.Error()) } @@ -669,11 +629,6 @@ func (r *RecipeRepository) GetUserRecipes(id int) ([]domain.Recipe, error) { recipes = append(recipes, recipe) } - if err := tx.Commit(); err != nil { - tx.Rollback() - return nil, err - } - return recipes, nil } @@ -681,12 +636,6 @@ func (r *RecipeRepository) GetUserRecipes(id int) ([]domain.Recipe, error) { // authenticated or exists. If nothing is found, a blank slice will be returned. The resulting list // is sorted by the created dates, newest first. Any errors will be bubbled to the caller. func (r *RecipeRepository) GetUserFavoriteRecipes(id int) ([]domain.Recipe, error) { - tx, err := r.db.Begin() - if err != nil { - tx.Rollback() - return nil, err - } - query := ` SELECT r.id, r.title, r.description, r.instructions, r.serves, r.difficulty, r.duration, r.category, r.ingredients, r. userid, r.modified, r.created @@ -695,7 +644,7 @@ func (r *RecipeRepository) GetUserFavoriteRecipes(id int) ([]domain.Recipe, erro WHERE f.userid = $1 ORDER BY f.created DESC; ` - rows, err := tx.Query(query, id) + rows, err := r.db.Query(query, id) if err != nil { return nil, fmt.Errorf("Failed to query DB for user recipes. %s\n", err.Error()) } @@ -760,11 +709,6 @@ func (r *RecipeRepository) GetUserFavoriteRecipes(id int) ([]domain.Recipe, erro recipes = append(recipes, recipe) } - if err := tx.Commit(); err != nil { - tx.Rollback() - return nil, err - } - return recipes, nil } @@ -777,12 +721,6 @@ func (r *RecipeRepository) GetRecipeTags(recipe *domain.Recipe) error { return nil } - tx, err := r.db.Begin() - if err != nil { - tx.Rollback() - return err - } - recipe.Tags = []domain.Tag{} query := ` @@ -790,7 +728,7 @@ func (r *RecipeRepository) GetRecipeTags(recipe *domain.Recipe) error { JOIN recipetags rt ON rt.tagid = t.id WHERE rt.recipeid = $1; ` - rows, err := tx.Query(query, recipe.Id) + rows, err := r.db.Query(query, recipe.Id) if err != nil { return fmt.Errorf("Failed to get tags for recipe. %s\n", err.Error()) } @@ -807,11 +745,6 @@ func (r *RecipeRepository) GetRecipeTags(recipe *domain.Recipe) error { recipe.Tags = append(recipe.Tags, tag) } - if err := tx.Commit(); err != nil { - tx.Rollback() - return err - } - return nil } @@ -824,12 +757,6 @@ func (r *RecipeRepository) GetRecipeFavorite(recipe *domain.Recipe, userId int) return nil } - tx, err := r.db.Begin() - if err != nil { - tx.Rollback() - return err - } - query := ` SELECT COUNT(*) FROM favorites @@ -837,8 +764,7 @@ func (r *RecipeRepository) GetRecipeFavorite(recipe *domain.Recipe, userId int) ` var count int - if err := tx.QueryRow(query, recipe.Id, userId).Scan(&count); err != nil { - tx.Rollback() + if err := r.db.QueryRow(query, recipe.Id, userId).Scan(&count); err != nil { return fmt.Errorf("Failed to get recipe favorite. %s", err.Error()) } @@ -852,12 +778,6 @@ func (r *RecipeRepository) GetRecipeFavorite(recipe *domain.Recipe, userId int) // table and return it. If there is no entry, nil will be returned Any errors will be bubbled to // the caller. func (r *RecipeRepository) GetRecipeOfTheWeek(userId *int) (*domain.Recipe, error) { - tx, err := r.db.Begin() - if err != nil { - tx.Rollback() - return nil, err - } - query := ` SELECT r.id, r.title, r.description, r.instructions, r.serves, r.difficulty, r.duration, r.category, @@ -872,7 +792,7 @@ func (r *RecipeRepository) GetRecipeOfTheWeek(userId *int) (*domain.Recipe, erro var ingredientBytes []byte var recipe domain.Recipe - if err := tx.QueryRow(query).Scan( + if err := r.db.QueryRow(query).Scan( &recipe.Id, &recipe.Title, &recipe.Description, @@ -930,10 +850,5 @@ func (r *RecipeRepository) GetRecipeOfTheWeek(userId *int) (*domain.Recipe, erro recipe.Favorite = false } - if err := tx.Commit(); err != nil { - tx.Rollback() - return nil, err - } - return &recipe, nil } diff --git a/internal/templates/components/dropdowns.templ b/internal/templates/components/dropdowns.templ index 2940e38..2ca33b6 100644 --- a/internal/templates/components/dropdowns.templ +++ b/internal/templates/components/dropdowns.templ @@ -20,7 +20,7 @@ templ dropdownButton(content, name, value string, selected bool) { } -templ FilterDropdown(filters domainRecipe.SearchFilters) { +templ FilterDropdown(filters *domainRecipe.SearchFilters) {