Providing product filters

When you create a product listing in your MVC application that displays a list of offered products, you may want to allow customers to filter products based on their properties. If you are, for example, selling laptops, you may want to provide your customers the ability to filter them based on their manufacturers, screen resolutions, included accessories and other attributes or features.

You can filter based on different properties of products and their corresponding page type. A different approach is required when filtering:

Filtering based on page properties

Page properties are available in a page’sTreeNodeobject and its coupled data.

To filter products using properties of a product’s page type:

  1. Open your MVC application in Visual Studio.
  2. Add filtering logic to the controller that handles product listing:
    1. Ensure an instance of IShoppingService and ICatalogPriceCalculatorFactory is initialized for the controller.

      We recommend using a dependency injection container to initialize instances of used API services.

    2. Add a POST action that filters out products based on a property of the product’s page type. For example, the following code loads products that have a boolean property set to True (the LPTWithFeature propertyrepresents a page type’s field):

      
      
      
               /// <summary>
               /// Displays a product listing page of the class's product page type
               /// filtered based on the specified model.
               /// </summary>
               /// <param name="model">Model specifying all filtered products.</param>
               [HttpPost]
               public ActionResult FilterPageProperty(ProductFilterViewModel model)
               {                        
                   // Creates a new ProductFilterViewModel which holds a list of products
                   // that have the "LPTWithFeature" property checked.
                   ProductFilterViewModel filteredModel = new ProductFilterViewModel
                   {
                       LPTWithFeature = model.LPTWithFeature,
                       FilteredProducts = LoadProducts(GetWithFeatureWhereCondition(model))
                   };            
      
                   return View(filteredModel);
               }
      
      
      
       
    3. Add a where condition that filters the set of products. For example, to filter according to a boolean value:

      
      
      
               /// <summary>
               /// Returns a where condition to correctly retrieve which products are selected in the filter.
               /// </summary>
               /// <param name="model">Model specifying all filtered products.</param>
               /// <returns>Where condition specifying which products are selected.</returns>
               private WhereCondition GetWithFeatureWhereCondition(ProductFilterViewModel model)
               {   
      
                   // Initializes a new where condition
                   WhereCondition withFeatureWhere = new WhereCondition();
      
                   // If the feature is selected, sets the where condition
                   if (model.LPTWithFeature)
                   {
                       withFeatureWhere.WhereTrue("LPTWithFeature");
                   }
      
                   // Returns the where condition
                   return withFeatureWhere;
               }
      
      
      
       
    4. Add a method that loads products filtered by the where condition.

      
      
      
               /// <summary>
               /// Loads pages of the LearningProductType product page type based on the specified where condition.
               /// </summary>
               /// <param name="where">Where condition that restricts the returned products.</param>
               /// <returns>List of view models representing products, its prices and public status display name.</returns>
               private List<ProductListItemViewModel> LoadProducts(WhereCondition where)
               {            
                   // Gets products of the LearningProductType page type from the current site
                   List<LearningProductType> products = LearningProductTypeProvider.GetLearningProductTypes()
                      .LatestVersion(false)
                      .Published(true)
                      .OnSite(SiteContext.CurrentSiteName)
                      .Culture("en-US")
                      .CombineWithDefaultCulture()
                      .WhereTrue("SKUEnabled")
                      .Where(where)
                      .OrderByDescending("SKUInStoreFrom")
                      .ToList();
      
                   // Gets the current shopping cart (necessary to contextualize product price calculations)
                   ShoppingCartInfo cart = shoppingService.GetCurrentShoppingCart();
      
                   // Returns a list of products filtered by the where condition
                   return products.Select(
                       product => new ProductListItemViewModel(
                           product,
                           GetPrice(product.SKU, cart),
                           product.Product.PublicStatus?.PublicStatusDisplayName)
                       ).ToList();
               }
      
               // Retrieves a ProductCatalogPrices instance that contains calculated price information for the given product
               private ProductCatalogPrices GetPrice(SKUInfo product, ShoppingCartInfo cart)
               {
                   return calculatorFactory
                       .GetCalculator(cart.ShoppingCartSiteID)
                       .GetPrices(product, Enumerable.Empty<SKUInfo>(), cart);
               }
      
      
      
       
  3. Create a view and models based on your filter.

The controller now filters sets of products as specified by the customer.

Filtering based on SKU properties of a primitive type or string

SKU properties are available in the COM_SKU database table. In this subsection, you can learn about filtering based on SKU data that is stored directly within the COM_SKU table. For properties that are only foreign keys from a different table, see Filtering based on SKU properties from another database table.

To filter products based on the specified SKU properties:

  1. Open your MVC application in Visual Studio.

  2. Add filtering logic to the controller that handles product listing:

    1. Ensure an instance of IShoppingService and ICatalogPriceCalculatorFactory is initialized for the controller.

      We recommend using a dependency injection container to initialize service instances.

    2. Add a POST action that filters the products based on a primitive type or string. For example, the following code loads products based on a from–to price range (the product’s price is retrieved from the SKUPrice column of the COM_SKU database table):

      
      
      
               /// <summary>
               /// Displays a product listing page of the class's product page type
               /// filtered based on the specified model.
               /// </summary>
               /// <param name="model">Model specifying all filtered products.</param>
               [HttpPost]
               public ActionResult FilterSKUProperty(ProductFilterViewModel model)
               {            
                   // Creates a view model that consists of the entered price range
                   // and a list of products
                   ProductFilterViewModel filteredModel = new ProductFilterViewModel
                   {
                       PriceFrom = model.PriceFrom,
                       PriceTo = model.PriceTo,
                       FilteredProducts = LoadProducts(GetPriceWhereCondition(model))
                   };
      
                   return View(filteredModel);            
               }
      
      
      
       
    3. Add a where condition that filters the set of products. For example, for two text boxes representing a from–to range:

      
      
      
               /// <summary>
               /// Returns a where condition to correctly retrieve which products are selected in the filter.
               /// </summary>
               /// <param name="model">Model specifying all filtered products.</param>
               /// <returns>Where condition specifying which products are selected.</returns>
               private WhereCondition GetPriceWhereCondition(ProductFilterViewModel model)
               {
      
                   // Initializes a new where condition
                   WhereCondition priceWhere = new WhereCondition();
      
                   // Sets the price where condition based on the model's values and limited by the price from-to range
                   if (Constrain(model.PriceFrom, model.PriceTo))
                   {
                       priceWhere.WhereGreaterOrEquals("SKUPrice", model.PriceFrom)
                           .And().WhereLessOrEquals("SKUPrice", model.PriceTo);
                   }            
      
                   // Returns the where condition
                   return priceWhere;
               }
      
      
      
       
    4. Add a method that loads products filtered by the where condition. For instance, you can use a method similar to the one used in the Filtering based on page properties example.

  3. Create a view and models based on your filter.

The controller now filters sets of products as specified by the customer.

Filtering based on SKU properties from another database table

SKU properties are available in the COM_SKU database table. In this subsection, you can learn about filtering based on linked SKU data from another database table. To filter based on properties stored directly in the COM_SKU table, see Filtering based on SKU properties of a primitive type or string.

To filter products based on the specified linked properties:

  1. Open your MVC application in Visual Studio.

  2. Add filtering logic to the controller that handles product listing:

    1. Ensure an instance of IShoppingService and ICatalogPriceCalculatorFactory is initialized for the controller.

      We recommend using a dependency injection container to initialize service instances.

      
      
      
               /// <summary>
               /// Constructor for the ProductFilterController class.
               /// </summary>
               public ProductFilterController()
               {            
                   // Initializes instances of services required to facilitate product filtering
                   // For real-world projects, we recommend using a dependency injection container to initialize service instances
                   shoppingService = Service.Resolve<IShoppingService>();
                   calculatorFactory = Service.Resolve<ICatalogPriceCalculatorFactory>();
               }
      
      
      
       
    2. In the GET action that handles listing of unfiltered products, load all objects that you wish to use for filtering. For example, the following action loads all products of the LearningProductType page type, retrieves all manufacturers assigned to them, and encapsulates the retrieved objects in a view model:

      
      
      
               /// <summary>
               /// Displays a product listing page of the class's product page type with a possibility
               /// to filter products based on a foreign entity.
               /// </summary>
               public ActionResult FilterForeignProperty()
               {
      
                   // Creates a view model that consists of all foreign objects (manufacturers) related to the products
                   // and a list of products that will be filtered
                   ProductFilterViewModel model = new ProductFilterViewModel
                   {
                       Manufacturers = GetManufacturers(),
                       FilteredProducts = LoadProducts()
                   };
      
                   return View(model);            
               }
      
      
      
       
    3. Add a method that loads the foreign objects, for example with an object query. The following example loads all manufacturers assigned to products of the LearningProductType and enumerates them to a list of models that is sent to a view. This list of manufacturers then serves as a filter:

      
      
      
               /// <summary>
               /// Loads all available manufacturers assigned to products of the LearningProductType product page type.
               /// </summary>
               /// <returns>List of manufacturers' models and their unselected state.</returns>
               private List<ProductFilterCheckboxViewModel> GetManufacturers()
               {            
                   // Gets all manufacturers assigned to products of the LearningProductType
                   var manufacturers = LearningProductTypeProvider.GetLearningProductTypes()
                       .ToList()
                       .Where(skuPage => skuPage.Product.Manufacturer != null)
                       .Select(skuPage =>
                           new
                           {
                               skuPage.Product.Manufacturer?.ManufacturerID,
                               skuPage.Product.Manufacturer?.ManufacturerDisplayName
                           })
                       .Distinct();
      
                   // Returns a list of models that contain the manufacturers' display name, ID and false select state
                   return manufacturers.Select(manufacturer => new ProductFilterCheckboxViewModel
                   {
                       DisplayName = manufacturer.ManufacturerDisplayName,
                       Id = manufacturer.ManufacturerID.ToString(),
                       IsChecked = false
                   }).ToList();            
               }
      
      
      
       
    4. Add a POST action that filters products based on received parameters:

      
      
      
               /// <summary>
               /// Displays a product listing page of the class's product page type
               /// filtered based on the specified model.
               /// </summary>
               /// <param name="model">Model specifying all foreign objects and all filtered products.</param>
               [HttpPost]
               public ActionResult FilterForeignProperty(ProductFilterViewModel model)
               {            
                   // Creates a view model that consists of all foreign objects (manufacturers) related to the products
                   // and a list of products that will be filtered with their selected state
                   ProductFilterViewModel filteredModel = new ProductFilterViewModel
                   {
                       Manufacturers = model.Manufacturers,
                       FilteredProducts = LoadProducts(GetManufacturersWhereCondition(model))
                   };
      
                   return View(filteredModel);           
               }
      
      
      
       
    5. Add a where condition that filters the set of products.

      
      
      
               /// <summary>
               /// Returns a where condition to correctly retrieve which manufacturers are selected in the filter.
               /// </summary>
               /// <param name="model">Model specifying all foreign objects and all filtered products.</param>
               private WhereCondition GetManufacturersWhereCondition(ProductFilterViewModel model)
               {            
                   // Initializes a new where condition
                   WhereCondition manufacturersWhere = new WhereCondition();
      
                   // Gets a list of manufacturers that were selected on the live site
                   List<string> selectedManufacturersIds = model.Manufacturers
                                                               .Where(manufacturer => manufacturer.IsChecked)
                                                               .Select(manufacturer => manufacturer.Id)
                                                               .ToList();
      
                   // If any manufacturer is selected, sets the where condition
                   if (selectedManufacturersIds.Any())
                   {
                       manufacturersWhere.WhereIn("SKUManufacturerID", selectedManufacturersIds);
                   }            
      
                   // Returns the where condition
                   return manufacturersWhere;
               }
      
      
      
       
    6. Add a method that loads products filtered by the where condition. For instance, you can use a method similar to the one used in the Filtering based on page properties example.

  3. Create a view and models based on your filter.

The controller now filters sets of products according to assigned manufacturers selected by the customers.