routing
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
package catalog
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type ProductPageRequest struct {
|
||||
ID int64
|
||||
Slug string
|
||||
LanguageID int64
|
||||
ShopID int64
|
||||
}
|
||||
|
||||
type CategoryPageRequest struct {
|
||||
ID int64
|
||||
Slug string
|
||||
LanguageID int64
|
||||
ShopID int64
|
||||
}
|
||||
|
||||
type ProductPageData struct {
|
||||
ID int64
|
||||
Name string
|
||||
Slug string
|
||||
ShortDescription string
|
||||
Description string
|
||||
Price float64
|
||||
CoverImageID sql.NullInt64
|
||||
CategoryID int64
|
||||
CategorySlug string
|
||||
CategoryName string
|
||||
}
|
||||
|
||||
type CategoryPageData struct {
|
||||
ID int64
|
||||
Name string
|
||||
Slug string
|
||||
Description string
|
||||
Products []CategoryProductCard `gorm:"-"`
|
||||
}
|
||||
|
||||
type CategoryProductCard struct {
|
||||
ID int64
|
||||
Name string
|
||||
Slug string
|
||||
URL string `gorm:"-"`
|
||||
Price float64
|
||||
Description string
|
||||
EAN13 string
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
db *gorm.DB
|
||||
prefix string
|
||||
}
|
||||
|
||||
func NewService(db *gorm.DB, prefix string) *Service {
|
||||
return &Service{db: db, prefix: prefix}
|
||||
}
|
||||
|
||||
func (s *Service) GetProductPage(ctx context.Context, req ProductPageRequest) (*ProductPageData, error) {
|
||||
var product ProductPageData
|
||||
queryByID := fmt.Sprintf(`
|
||||
SELECT p.id_product AS id,
|
||||
pl.name AS name,
|
||||
pl.link_rewrite AS slug,
|
||||
pl.description_short AS short_description,
|
||||
pl.description AS description,
|
||||
ps.price AS price,
|
||||
i.id_image AS cover_image_id,
|
||||
p.id_category_default AS category_id,
|
||||
cl.link_rewrite AS category_slug,
|
||||
cl.name AS category_name
|
||||
FROM %sproduct p
|
||||
JOIN %sproduct_lang pl ON pl.id_product = p.id_product
|
||||
JOIN %sproduct_shop ps ON ps.id_product = p.id_product
|
||||
LEFT JOIN %scategory_lang cl ON cl.id_category = p.id_category_default AND cl.id_lang = pl.id_lang
|
||||
LEFT JOIN %simage i ON i.id_product = p.id_product AND i.cover = 1
|
||||
WHERE p.id_product = ?
|
||||
AND pl.id_lang = ?
|
||||
AND ps.id_shop = ?
|
||||
LIMIT 1
|
||||
`, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix)
|
||||
queryBySlug := fmt.Sprintf(`
|
||||
SELECT p.id_product AS id,
|
||||
pl.name AS name,
|
||||
pl.link_rewrite AS slug,
|
||||
pl.description_short AS short_description,
|
||||
pl.description AS description,
|
||||
ps.price AS price,
|
||||
i.id_image AS cover_image_id,
|
||||
p.id_category_default AS category_id,
|
||||
cl.link_rewrite AS category_slug,
|
||||
cl.name AS category_name
|
||||
FROM %sproduct p
|
||||
JOIN %sproduct_lang pl ON pl.id_product = p.id_product
|
||||
JOIN %sproduct_shop ps ON ps.id_product = p.id_product
|
||||
LEFT JOIN %scategory_lang cl ON cl.id_category = p.id_category_default AND cl.id_lang = pl.id_lang
|
||||
LEFT JOIN %simage i ON i.id_product = p.id_product AND i.cover = 1
|
||||
WHERE pl.link_rewrite = ?
|
||||
AND pl.id_lang = ?
|
||||
AND ps.id_shop = ?
|
||||
LIMIT 1
|
||||
`, s.prefix, s.prefix, s.prefix, s.prefix, s.prefix)
|
||||
|
||||
var result *gorm.DB
|
||||
if req.ID != 0 {
|
||||
result = s.db.WithContext(ctx).Raw(strings.TrimSpace(queryByID), req.ID, req.LanguageID, req.ShopID).Scan(&product)
|
||||
} else {
|
||||
result = s.db.WithContext(ctx).Raw(strings.TrimSpace(queryBySlug), req.Slug, req.LanguageID, req.ShopID).Scan(&product)
|
||||
}
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
return &product, nil
|
||||
}
|
||||
|
||||
func (s *Service) GetCategoryPage(ctx context.Context, req CategoryPageRequest) (*CategoryPageData, error) {
|
||||
var category CategoryPageData
|
||||
categoryQuery := fmt.Sprintf(`
|
||||
SELECT c.id_category AS id,
|
||||
cl.name AS name,
|
||||
cl.link_rewrite AS slug,
|
||||
cl.description AS description
|
||||
FROM %scategory c
|
||||
JOIN %scategory_lang cl ON cl.id_category = c.id_category
|
||||
WHERE c.id_category = ?
|
||||
AND cl.id_lang = ?
|
||||
LIMIT 1
|
||||
`, s.prefix, s.prefix)
|
||||
categoryFallbackQuery := fmt.Sprintf(`
|
||||
SELECT c.id_category AS id,
|
||||
cl.name AS name,
|
||||
cl.link_rewrite AS slug,
|
||||
cl.description AS description
|
||||
FROM %scategory c
|
||||
JOIN %scategory_lang cl ON cl.id_category = c.id_category
|
||||
WHERE c.id_category = ?
|
||||
ORDER BY cl.id_lang ASC
|
||||
LIMIT 1
|
||||
`, s.prefix, s.prefix)
|
||||
|
||||
lookupID := req.ID
|
||||
if lookupID == 0 {
|
||||
idQuery := fmt.Sprintf(`
|
||||
SELECT c.id_category
|
||||
FROM %scategory c
|
||||
JOIN %scategory_lang cl ON cl.id_category = c.id_category
|
||||
WHERE cl.link_rewrite = ?
|
||||
AND cl.id_lang = ?
|
||||
LIMIT 1
|
||||
`, s.prefix, s.prefix)
|
||||
var row struct {
|
||||
ID int64 `gorm:"column:id_category"`
|
||||
}
|
||||
result := s.db.WithContext(ctx).Raw(strings.TrimSpace(idQuery), req.Slug, req.LanguageID).Scan(&row)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
lookupID = row.ID
|
||||
}
|
||||
|
||||
result := s.db.WithContext(ctx).Raw(strings.TrimSpace(categoryQuery), lookupID, req.LanguageID).Scan(&category)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
result = s.db.WithContext(ctx).Raw(strings.TrimSpace(categoryFallbackQuery), lookupID).Scan(&category)
|
||||
if result.Error != nil {
|
||||
return nil, result.Error
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
}
|
||||
|
||||
productQuery := fmt.Sprintf(`
|
||||
SELECT p.id_product AS id,
|
||||
pl.name AS name,
|
||||
pl.link_rewrite AS slug,
|
||||
p.ean13 AS ean13,
|
||||
ps.price AS price,
|
||||
pl.description_short AS description
|
||||
FROM %scategory_product cp
|
||||
JOIN %sproduct p ON p.id_product = cp.id_product
|
||||
JOIN %sproduct_lang pl ON pl.id_product = p.id_product
|
||||
JOIN %sproduct_shop ps ON ps.id_product = p.id_product
|
||||
WHERE cp.id_category = ?
|
||||
AND pl.id_lang = ?
|
||||
AND ps.id_shop = ?
|
||||
ORDER BY cp.position ASC, p.id_product ASC
|
||||
LIMIT 48
|
||||
`, s.prefix, s.prefix, s.prefix, s.prefix)
|
||||
|
||||
if err := s.db.WithContext(ctx).Raw(strings.TrimSpace(productQuery), category.ID, req.LanguageID, req.ShopID).Scan(&category.Products).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &category, nil
|
||||
}
|
||||
|
||||
func (s *Service) ResolveLanguageID(ctx context.Context, req *http.Request, fallback int64) int64 {
|
||||
if req == nil || req.URL == nil {
|
||||
return fallback
|
||||
}
|
||||
path := strings.Trim(req.URL.Path, "/")
|
||||
if path == "" {
|
||||
return fallback
|
||||
}
|
||||
first := path
|
||||
if idx := strings.IndexByte(path, '/'); idx >= 0 {
|
||||
first = path[:idx]
|
||||
}
|
||||
first = strings.TrimSpace(first)
|
||||
if len(first) < 2 || len(first) > 5 {
|
||||
return fallback
|
||||
}
|
||||
|
||||
var row struct {
|
||||
ID int64 `gorm:"column:id_lang"`
|
||||
}
|
||||
query := fmt.Sprintf("SELECT id_lang FROM %slang WHERE iso_code = ? LIMIT 1", s.prefix)
|
||||
result := s.db.WithContext(ctx).Raw(query, strings.ToUpper(first)).Scan(&row)
|
||||
if result.Error != nil || result.RowsAffected == 0 || row.ID == 0 {
|
||||
return fallback
|
||||
}
|
||||
return row.ID
|
||||
}
|
||||
Reference in New Issue
Block a user