package menuService import ( "slices" "sort" "git.ma-al.com/goc_daniel/b2b/app/model" "git.ma-al.com/goc_daniel/b2b/app/repos/categoriesRepo" routesRepo "git.ma-al.com/goc_daniel/b2b/app/repos/routesRepo" "git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors" ) type MenuService struct { categoriesRepo categoriesRepo.UICategoriesRepo routesRepo routesRepo.UIRoutesRepo } func New() *MenuService { return &MenuService{ categoriesRepo: categoriesRepo.New(), routesRepo: routesRepo.New(), } } func (s *MenuService) GetCategoryTree(root_category_id uint, id_lang uint) (*model.Category, error) { all_categories, err := s.categoriesRepo.GetAllCategories(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].CategoryID == root_category_id { 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++ { all_categories[i].Visited = false 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, success := s.createTree(root_index, &all_categories, &children_indices) if !success { return &tree, responseErrors.ErrCircularDependency } return &tree, nil } func (s *MenuService) createTree(index int, all_categories *([]model.ScannedCategory), children_indices *(map[int][]ChildWithPosition)) (model.Category, bool) { node := s.scannedToNormalCategory((*all_categories)[index]) if (*all_categories)[index].Visited { return node, false } (*all_categories)[index].Visited = true for i := 0; i < len((*children_indices)[index]); i++ { next_child, success := s.createTree((*children_indices)[index][i].Index, all_categories, children_indices) if !success { return node, false } node.Children = append(node.Children, next_child) } (*all_categories)[index].Visited = false // just in case we have a "diamond" diagram return node, true } func (s *MenuService) GetRoutes(id_lang uint) ([]model.Route, error) { return s.routesRepo.GetRoutes(id_lang) } func (s *MenuService) scannedToNormalCategory(scanned model.ScannedCategory) model.Category { var normal model.Category // normal.Active = scanned.Active normal.CategoryID = scanned.CategoryID normal.Label = scanned.Name // normal.Active = scanned.Active == 1 normal.Params = model.CategoryParams{CategoryID: normal.CategoryID, LinkRewrite: scanned.LinkRewrite, Locale: scanned.IsoCode} normal.Children = []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 } func (s *MenuService) GetBreadcrumb(root_category_id uint, start_category_id uint, id_lang uint) ([]model.CategoryInBreadcrumb, error) { all_categories, err := s.categoriesRepo.GetAllCategories(id_lang) if err != nil { return []model.CategoryInBreadcrumb{}, err } breadcrumb := []model.CategoryInBreadcrumb{} start_index := 0 start_found := false for i := 0; i < len(all_categories); i++ { if all_categories[i].CategoryID == start_category_id { start_index = i start_found = true break } } if !start_found { return []model.CategoryInBreadcrumb{}, responseErrors.ErrStartCategoryNotFound } // map category ids to indices id_to_index := make(map[uint]int) for i := 0; i < len(all_categories); i++ { all_categories[i].Visited = false id_to_index[all_categories[i].CategoryID] = i } // do a simple graph traversal, always jumping from node to its parent index := start_index success := true for { if all_categories[index].Visited { success = false break } all_categories[index].Visited = true var next_category model.CategoryInBreadcrumb next_category.CategoryID = all_categories[index].CategoryID next_category.Name = all_categories[index].Name breadcrumb = append(breadcrumb, next_category) if all_categories[index].CategoryID == root_category_id { break } next_index, ok := id_to_index[all_categories[index].ParentID] if !ok { success = false break } index = next_index } slices.Reverse(breadcrumb) if !success { return breadcrumb, responseErrors.ErrRootNeverReached } return breadcrumb, nil } func (s *MenuService) GetTopMenu(id uint) ([]*model.B2BTopMenu, error) { items, err := s.routesRepo.GetTopMenu(id) if err != nil { return nil, err } menuMap := make(map[int]*model.B2BTopMenu, len(items)) roots := make([]*model.B2BTopMenu, 0) for i := range items { menu := &items[i] menu.Children = make([]*model.B2BTopMenu, 0) menuMap[menu.MenuID] = menu } for i := range items { menu := &items[i] if menu.ParentID == nil { roots = append(roots, menu) continue } parent, ok := menuMap[*menu.ParentID] if !ok { // fallback for orphaned nodes roots = append(roots, menu) continue } parent.Children = append(parent.Children, menu) } return roots, nil }