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:
- Based on page properties
- Based on SKU properties of a primitive type or string (typically, Boolean, byte, integer or string variables)
- Based on SKU properties from another database table (typically, public statuses or manufacturers)
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:
- Open your MVC application in Visual Studio.
- Add filtering logic to the controller that handles product listing:
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.
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); }
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; }
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); }
- 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:
Open your MVC application in Visual Studio.
Add filtering logic to the controller that handles product listing:
Ensure an instance of IShoppingService and ICatalogPriceCalculatorFactory is initialized for the controller.
We recommend using a dependency injection container to initialize service instances.
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); }
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; }
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.
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:
Open your MVC application in Visual Studio.
Add filtering logic to the controller that handles product listing:
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>(); }
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); }
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(); }
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); }
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; }
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.
Create a view and models based on your filter.
The controller now filters sets of products according to assigned manufacturers selected by the customers.