endpoint returning tree of categories
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@ bin/
|
||||
i18n/*.json
|
||||
*_templ.go
|
||||
tmp/main
|
||||
test.go
|
||||
@@ -1,6 +1,8 @@
|
||||
package restricted
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/menuService"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/i18n"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/nullable"
|
||||
@@ -29,7 +31,21 @@ func MenuHandlerRoutes(r fiber.Router) fiber.Router {
|
||||
}
|
||||
|
||||
func (h *MenuHandler) GetMenu(c fiber.Ctx) error {
|
||||
menu, err := h.menuService.GetMenu()
|
||||
|
||||
id_shop_attribute := c.Query("shopID")
|
||||
id_shop, err := strconv.Atoi(id_shop_attribute)
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||
}
|
||||
|
||||
id_lang, err := strconv.Atoi(c.Cookies("lang_id", "2"))
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(responseErrors.ErrBadAttribute)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, responseErrors.ErrBadAttribute)))
|
||||
}
|
||||
|
||||
menu, err := h.menuService.GetMenu(uint(id_shop), uint(id_lang))
|
||||
if err != nil {
|
||||
return c.Status(responseErrors.GetErrorStatus(err)).
|
||||
JSON(response.Make(nullable.GetNil(""), 0, responseErrors.GetErrorCode(c, err)))
|
||||
|
||||
@@ -81,4 +81,19 @@ type ProductFilters struct {
|
||||
InStock uint `query:"stock,omitempty"`
|
||||
}
|
||||
|
||||
type ScannedCategory struct {
|
||||
CategoryID uint `gorm:"column:ID;primaryKey"`
|
||||
Name string `gorm:"column:name"`
|
||||
Active uint `gorm:"column:active"`
|
||||
Position uint `gorm:"column:position"`
|
||||
ParentID uint `gorm:"column:id_parent"`
|
||||
IsRoot uint `gorm:"column:is_root_category"`
|
||||
}
|
||||
type Category struct {
|
||||
CategoryID uint `json:"category_id" form:"category_id"`
|
||||
Name string `json:"name" form:"name"`
|
||||
Active uint `json:"active" form:"active"`
|
||||
Subcategories []Category `json:"subcategories" form:"subcategories"`
|
||||
}
|
||||
|
||||
type FeatVal = map[uint][]uint
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
package menuService
|
||||
|
||||
import "git.ma-al.com/goc_daniel/b2b/repository/categoriesRepo"
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||
"git.ma-al.com/goc_daniel/b2b/repository/categoriesRepo"
|
||||
)
|
||||
|
||||
type MenuService struct {
|
||||
categoriesRepo categoriesRepo.UICategoriesRepo
|
||||
@@ -12,35 +18,73 @@ func New() *MenuService {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MenuService) GetMenu() (string, error) {
|
||||
// var menu_json string
|
||||
|
||||
// products, err := s.listProductsRepo.GetListing(id_shop, id_lang, p, filters)
|
||||
// if err != nil {
|
||||
// return products, err
|
||||
// }
|
||||
|
||||
// var loopErr error
|
||||
// parallel.ForEach(products.Items, func(t model.Product, i int) {
|
||||
// // products.Items[i].PriceTaxed *= currRate.Rate.InexactFloat64()
|
||||
// // products.Items[i].PriceTaxed = tiny_util.RoundUpMonetary(products.Items[i].PriceTaxed)
|
||||
|
||||
// if products.Items[i].Name.IsNull() {
|
||||
// translation, err := s.listProductsRepo.GetTranslation(ctx, products.Items[i].ID, defaults.DefaultLanguageID)
|
||||
// if err != nil {
|
||||
// loopErr = err
|
||||
// return
|
||||
// }
|
||||
// products.Items[i].Name = nullable.FromPrimitiveString(translation.Name)
|
||||
// products.Items[i].DescriptionShort = nullable.FromPrimitiveString(translation.DescriptionShort)
|
||||
// products.Items[i].LinkRewrite = nullable.FromPrimitiveString(translation.LinkRewrite)
|
||||
// }
|
||||
// })
|
||||
// if loopErr != nil {
|
||||
// return products, errs.Handled(span, loopErr, errs.InternalError, errs.ERR_TODO)
|
||||
// }
|
||||
|
||||
// return products, nil
|
||||
|
||||
return "", nil
|
||||
func (s *MenuService) GetMenu(id_shop uint, id_lang uint) (model.Category, error) {
|
||||
all_categories, err := s.categoriesRepo.GetAllCategories(id_shop, id_lang)
|
||||
if err != nil {
|
||||
return model.Category{}, err
|
||||
}
|
||||
|
||||
// find the root
|
||||
root_index := 0
|
||||
root_found := false
|
||||
for i := 0; i < len(all_categories); i++ {
|
||||
if all_categories[i].IsRoot == 1 {
|
||||
root_index = i
|
||||
root_found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !root_found {
|
||||
return model.Category{}, responseErrors.ErrNoRootFound
|
||||
}
|
||||
|
||||
// now create the children and reorder them according to position
|
||||
id_to_index := make(map[uint]int)
|
||||
for i := 0; i < len(all_categories); i++ {
|
||||
id_to_index[all_categories[i].CategoryID] = i
|
||||
}
|
||||
|
||||
children_indices := make(map[int][]ChildWithPosition)
|
||||
for i := 0; i < len(all_categories); i++ {
|
||||
parent_index := id_to_index[all_categories[i].ParentID]
|
||||
children_indices[parent_index] = append(children_indices[parent_index], ChildWithPosition{Index: i, Position: all_categories[i].Position})
|
||||
}
|
||||
|
||||
for key := range children_indices {
|
||||
sort.Sort(ByPosition(children_indices[key]))
|
||||
}
|
||||
|
||||
// finally, create the tree
|
||||
tree := s.createTree(root_index, &all_categories, &children_indices)
|
||||
|
||||
return tree, nil
|
||||
}
|
||||
|
||||
func (s *MenuService) createTree(index int, all_categories *([]model.ScannedCategory), children_indices *(map[int][]ChildWithPosition)) model.Category {
|
||||
node := s.scannedToNormalCategory((*all_categories)[index])
|
||||
|
||||
for i := 0; i < len((*children_indices)[index]); i++ {
|
||||
node.Subcategories = append(node.Subcategories, s.createTree((*children_indices)[index][i].Index, all_categories, children_indices))
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
func (s *MenuService) scannedToNormalCategory(scanned model.ScannedCategory) model.Category {
|
||||
var normal model.Category
|
||||
normal.Active = scanned.Active
|
||||
normal.CategoryID = scanned.CategoryID
|
||||
normal.Name = scanned.Name
|
||||
normal.Subcategories = []model.Category{}
|
||||
return normal
|
||||
}
|
||||
|
||||
type ChildWithPosition struct {
|
||||
Index int
|
||||
Position uint
|
||||
}
|
||||
type ByPosition []ChildWithPosition
|
||||
|
||||
func (a ByPosition) Len() int { return len(a) }
|
||||
func (a ByPosition) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a ByPosition) Less(i, j int) bool { return a[i].Position < a[j].Position }
|
||||
|
||||
@@ -48,6 +48,9 @@ var (
|
||||
|
||||
// Typed errors for product list handler
|
||||
ErrBadPaging = errors.New("bad or missing paging attribute value in header")
|
||||
|
||||
// Typed errors for menu handler
|
||||
ErrNoRootFound = errors.New("no root found in categories table")
|
||||
)
|
||||
|
||||
// Error represents an error with HTTP status code
|
||||
@@ -135,6 +138,9 @@ func GetErrorCode(c fiber.Ctx, err error) string {
|
||||
case errors.Is(err, ErrBadPaging):
|
||||
return i18n.T_(c, "error.err_bad_paging")
|
||||
|
||||
case errors.Is(err, ErrNoRootFound):
|
||||
return i18n.T_(c, "error.no_root_found")
|
||||
|
||||
default:
|
||||
return i18n.T_(c, "error.err_internal_server_error")
|
||||
}
|
||||
@@ -169,7 +175,8 @@ func GetErrorStatus(err error) int {
|
||||
errors.Is(err, ErrBadAttribute),
|
||||
errors.Is(err, ErrBadField),
|
||||
errors.Is(err, ErrInvalidXHTML),
|
||||
errors.Is(err, ErrBadPaging):
|
||||
errors.Is(err, ErrBadPaging),
|
||||
errors.Is(err, ErrNoRootFound):
|
||||
return fiber.StatusBadRequest
|
||||
case errors.Is(err, ErrEmailExists):
|
||||
return fiber.StatusConflict
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
package categoriesRepo
|
||||
|
||||
import (
|
||||
"git.ma-al.com/goc_daniel/b2b/app/db"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
)
|
||||
|
||||
type UICategoriesRepo interface {
|
||||
GetAllCategories(id_shop uint, id_lang uint) ([]model.ScannedCategory, error)
|
||||
}
|
||||
|
||||
type CategoriesRepo struct{}
|
||||
@@ -8,3 +14,28 @@ type CategoriesRepo struct{}
|
||||
func New() UICategoriesRepo {
|
||||
return &CategoriesRepo{}
|
||||
}
|
||||
|
||||
func (repo *CategoriesRepo) GetAllCategories(id_shop uint, id_lang uint) ([]model.ScannedCategory, error) {
|
||||
var allCategories []model.ScannedCategory
|
||||
|
||||
err := db.DB.Raw(`
|
||||
SELECT
|
||||
ps_category.id_category AS ID,
|
||||
ps_category_lang.name AS name,
|
||||
ps_category.active AS active,
|
||||
ps_category_shop.position AS position,
|
||||
ps_category.id_parent AS id_parent,
|
||||
ps_category.is_root_category AS is_root_category
|
||||
FROM ps_category
|
||||
LEFT JOIN ps_category_lang
|
||||
ON ps_category_lang.id_category = ps_category.id_category
|
||||
AND ps_category_lang.id_shop = ?
|
||||
AND ps_category_lang.id_lang = ?
|
||||
LEFT JOIN ps_category_shop
|
||||
ON ps_category_shop.id_category = ps_category.id_category
|
||||
AND ps_category_shop.id_shop = ?`,
|
||||
id_shop, id_lang, id_shop).
|
||||
Scan(&allCategories).Error
|
||||
|
||||
return allCategories, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user