Developer forum

Forum » Dynamicweb 10 » Navigation with products

Navigation with products

Aki Ruuskanen
Aki Ruuskanen
Reply

Hi,

I am trying to create a navigation that also shows products. 

I have the "Include products" settings checked in the Ecommerce settings. In the navigation settings i have endlevel 10 and ExpandMode.All. 

I do not get products a the navigation nodes in the template, only product groups. 

How can I create a navigation including products?

Regards / Aki 

 


Replies

 
Nicolai Pedersen Dynamicweb Employee
Nicolai Pedersen
Reply
This post has been marked as an answer

There is the deprecated navigation system that is based on xml - known as Dynamicweb.Frontend.NavigationProviders.NavigationProvider - they support products in navigation.

The new navigation system that is based on razor templates and viewmodels - known as Dynamicweb.Frontend.Navigation.NavigationTreeNodeProvider - they do not support products in navigation.

This change is by design. As we saw navigations with 100.000+ products in the navigation leading to death and destruction...

If you need products in the navigation based on the razor model, you have to implement your self - either using the API in the razor template or by adding your own NavigationTreeNodeProvider that can also return products.

Performance can easily go wrong - so take that into consideration. Looping all groups and their products can be super expensive to do on all requests.

Our GroupNavigationTreeNodeProvider - you can copy and extend that to return products as well. 

using System;
using System.Collections.Generic;
using System.Linq;
using Dynamicweb.Configuration;
using Dynamicweb.Content;
using Dynamicweb.Ecommerce.Extensibility.Controls;
using Dynamicweb.Ecommerce.Products;
using Dynamicweb.Environment.Helpers;
using Dynamicweb.Extensibility.AddIns;
using Dynamicweb.Frontend.Navigation;

namespace Dynamicweb.Ecommerce.Frontend.Navigation
{
    [AddInOrder(2)]
    public class GroupNavigationTreeNodeProvider : NavigationTreeNodeProvider
    {

        /// <summary>
        /// Gets the group navigation nodes for a given parent based on the provided navigation context and navigation settings.
        /// </summary>
        /// <param name="context">The navigation context used to generate navigation tree nodes.</param>
        /// <param name="settings">The navigation settings used to generate navigation tree nodes.</param>
        /// <param name="parent">The parent navigation tree node.</param>
        /// <returns>A colllection of group navigation tree nodes.</returns>
        public override IEnumerable<NavigationTreeNode> GetNodes(NavigationContext context, NavigationSettings settings, NavigationTreeNode parent)
        {
            int rootPageId = parent.PageId;
            if (rootPageId <= 0)
            {
                return Enumerable.Empty<NavigationTreeNode>();
            }

            var page = Dynamicweb.Content.Services.Pages.GetPage(rootPageId);
            var groups = GetGroups(parent, page);
            IEnumerable<NavigationTreeNode> nodes;
            if (groups.Any())
            {
                int productPageId = LinkHelper.GetInternalPageId(page.NavigationSettings.ProductPage);
                nodes = groups.Select(group => CreateGroupNavigationTreeNode(page, group, (parent.Level == 0 ? page.Level : parent.Level) + 1, productPageId));
            }
            else
            {
                nodes = Enumerable.Empty<NavigationTreeNode>();
            }

            return nodes;
        }

        /// <summary>
        /// Initialize a navigation context.
        /// </summary>
        /// <param name="context">The navigation context used to generate navigation tree nodes.</param>
        public override void InitializeContext(NavigationContext context)
        {
            string groupId = context.QueryString["GroupID"];
            if (!string.IsNullOrEmpty(groupId))
            {
                var groupPath = GetPrimaryPath(groupId, Common.Context.LanguageID);
                foreach (var currentGroupId in groupPath)
                {
                    groupId = currentGroupId;
                    context.Path.Add($"Page_{context.PageId}::group_{groupId}");
                }
            }
        }

        private static List<string> GetPrimaryPath(string groupId, string languageId)
        {
            var result = new List<string>();
            while (!string.IsNullOrEmpty(groupId))
            {
                result.Add(groupId);
                var group = Services.ProductGroups.GetGroup(groupId, languageId);
                groupId = group?.GetPrimaryParentGroupId() ?? group?.GetParentGroups().FirstOrDefault()?.Id;
            }

            result.Reverse();
            return result;
        }

        /// <summary>
        /// Gets the groups for a given navigation context and navigation settings.
        /// </summary>
        /// <param name="parent">The parent navigation tree node.</param>
        /// <param name="page">The node page.</param>
        /// <returns>A coollection of groups.</returns>
        private static IEnumerable<Group> GetGroups(NavigationTreeNode parent, Page page)
        {
            IEnumerable<Group> groups;
            var ecomSettings = page.NavigationSettings;
            if (ecomSettings is null || !ecomSettings.UseEcomGroups)
            {
                return Enumerable.Empty<Group>();
            }

            if (parent is GroupNavigationTreeNode parentGroup)
            {
                if (ecomSettings.MaxLevels == 0 || parent.Level - page.Level < ecomSettings.MaxLevels)
                {
                    groups = GetSubgroups(parentGroup);
                }
                else
                {
                    groups = Enumerable.Empty<Group>();
                }
            }
            else
            {
                groups = GetGroupsBySettings(ecomSettings);
            }

            groups = FilterGroups(groups);
            return groups;
        }

        private static IEnumerable<Group> FilterGroups(IEnumerable<Group> groups)
        {
            bool useAssortments = SystemConfiguration.Instance.GetBoolean("/Globalsettings/Ecom/Assortments/UseAssortments");
            if (useAssortments)
            {
                var user = Security.UserManagement.UserContext.Current.User;
                var assortmentIds = user is not null ? Services.Assortments.GetAssortmentIdsByUser(user, true) : Services.Assortments.GetAllowAnonymousUsersAssortmentIds();
                var hasAccessToGroup = new Func<Group, bool>(group =>
                {
                    foreach (var assortmentId in assortmentIds)
                    {
                        var assortment = Services.Assortments.GetAssortmentById(assortmentId);
                        if (assortment is not null && assortment.GroupHierarchy.Contains(group.Id))
                            return true;
                    }

                    return false;
                });

                groups = groups.Where(g => g.NavigationShowInMenu && hasAccessToGroup(g));
            }
            else
            {
                groups = groups.Where(g => g.NavigationShowInMenu == true);
            }

            return groups;
        }

        private static IEnumerable<Group> GetSubgroups(GroupNavigationTreeNode parent)
        {
            return Services.ProductGroups.GetSubgroups(parent.Group);
        }

        private static IEnumerable<Group> GetGroupsBySettings(PageNavigationSettings ecomSettings)
        {
            IEnumerable<Group> topLevelGroups;
            string languageId = Common.Context.LanguageID;
            switch (ecomSettings.ParentType)
            {
                case EcommerceNavigationParentType.Groups:
                    {
                        var groupHandler = new ProductsAndGroupsHandler(ecomSettings.Groups);
                        if (groupHandler.Type == HandlerConfigurationType.All)
                        {
                            var shop = Services.Shops.GetShop(string.IsNullOrEmpty(groupHandler.ShopSelected) ? ecomSettings.ShopID : groupHandler.ShopSelected);
                            if (shop is null)
                            {
                                topLevelGroups = Enumerable.Empty<Group>();
                            }
                            else
                            {
                                topLevelGroups = shop.GetTopLevelGroups(languageId);
                            }
                        }
                        else
                        {
                            topLevelGroups = groupHandler.GroupsSelected;
                        }

                        break;
                    }

                case EcommerceNavigationParentType.Shop:
                    {
                        var shop = Services.Shops.GetShop(ecomSettings.ShopID);
                        if (shop is null)
                        {
                            topLevelGroups = Enumerable.Empty<Group>();
                        }
                        else
                        {
                            topLevelGroups = shop.GetTopLevelGroups(languageId);
                        }

                        break;
                    }

                default:
                    {
                        topLevelGroups = Enumerable.Empty<Group>();
                        break;
                    }
            }

            return topLevelGroups;
        }

        private static GroupNavigationTreeNode CreateGroupNavigationTreeNode(Page page, Group group, int level, int productPageId)
        {
            var node = new GroupNavigationTreeNode(page, group, level, productPageId);
            return node;
        }
    }
}

Votes for this answer: 1
 
Aki Ruuskanen
Aki Ruuskanen
Reply

Hi Nicolai,

Excellnt. My suspicion was that its not supported anymore but good to have it confirmed. Then I'll look into how we will solve it. I'll check your suggestion. It's not a lot of products so propably som API to get them. 

Regards / Aki 

 

You must be logged in to post in the forum