package store import ( "context" "fmt" "gitea.stevedudenhoeffer.com/steve/pansy/internal/domain" ) // plantColumns lists plants columns in the order scanPlant expects. const plantColumns = `id, owner_id, name, category, spacing_cm, color, icon, days_to_maturity, notes, version, created_at, updated_at` func scanPlant(s scanner) (*domain.Plant, error) { var p domain.Plant if err := s.Scan( &p.ID, &p.OwnerID, &p.Name, &p.Category, &p.SpacingCM, &p.Color, &p.Icon, &p.DaysToMaturity, &p.Notes, &p.Version, &p.CreatedAt, &p.UpdatedAt, ); err != nil { return nil, err } return &p, nil } // ListReferencedPlants returns the distinct plants used by a garden's active // plantings — the catalog subset the editor needs to render them. Always a // non-nil slice (empty until plantings exist, #14). Full plant CRUD/seeding is // #12; this is only the /full read side. func (d *DB) ListReferencedPlants(ctx context.Context, gardenID int64) ([]domain.Plant, error) { rows, err := d.sql.QueryContext(ctx, `SELECT DISTINCT `+qualifyColumns("p", plantColumns)+` FROM plants p JOIN plantings pl ON pl.plant_id = p.id JOIN garden_objects o ON o.id = pl.object_id WHERE o.garden_id = ? AND pl.removed_at IS NULL ORDER BY p.id`, gardenID, ) if err != nil { return nil, fmt.Errorf("store: list referenced plants: %w", err) } defer rows.Close() plants := []domain.Plant{} for rows.Next() { p, err := scanPlant(rows) if err != nil { return nil, fmt.Errorf("store: scan plant: %w", err) } plants = append(plants, *p) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("store: iterate plants: %w", err) } return plants, nil }