Displaying product listings
When building an MVC e-commerce site, you may want to display product catalog and product details pages.
In its most basic implementation, a product catalog is often a listing of various products and services offered in a particular store. Customers can filter the catalog according to a product’s properties and specifications. For example by brand, price, size, etc. Individual product details pages then convey more comprehensive information about particular products.
When creating products, use a product page type labeled as content-only.
To build a product listing page, you need to ensure:
- Retrieval and listing of products
- (Optional) Suitable product image size
- (Optional) SEO friendly URLs
Displaying product listings
To display a listing of products:
Tip: To view the full code of a functional example, you can inspect and download the LearningKit project on GitHub. You can also run the LearningKit website by connecting the project to a Kentico database.
Generate code files for the product page types you intend to display.
- In the Page types application, edit the specific product page type.
- Switch to the Code tab.
- Click Save code.
Open your MVC project in Visual Studio.
Include the generated code files to your MVC project.
Add view models for each product you want to display. A view model for a particular product only needs to hold properties you plan to display or otherwise require in related views. If you want to display properties specific to a given product page type, you should create a separate view model for each product.
public class ProductListItemViewModel { public readonly PriceDetailViewModel PriceModel; public string Name; public string ImagePath; public string PublicStatusName; public bool Available; public Guid ProductPageGuid; public string ProductPageAlias; /// <summary> /// Constructor for the ProductListItemViewModel class. /// </summary> /// <param name="productPage">Product's page.</param> /// <param name="priceDetail">Price of the product.</param> /// <param name="publicStatusName">Display name of the product's public status.</param> public ProductListItemViewModel(SKUTreeNode productPage, ProductCatalogPrices priceDetail, string publicStatusName) { // Sets the page information Name = productPage.DocumentName; ProductPageGuid = productPage.NodeGUID; ProductPageAlias = productPage.NodeAlias; // Sets the SKU information ImagePath = productPage.SKU.SKUImagePath; Available = !productPage.SKU.SKUSellOnlyAvailable || productPage.SKU.SKUAvailableItems > 0; PublicStatusName = publicStatusName; // Sets the price format information PriceModel = new PriceDetailViewModel { Price = priceDetail.Price, ListPrice = priceDetail.ListPrice, CurrencyFormatString = priceDetail.Currency.CurrencyFormatString }; } }
The PriceDetailViewModel property is itself a model that contains select properties of the CMS.ECommerce.ProductCatalogPrices type. Objects of the ProductCatalogPrices type hold the final price values for a product after all price calculations have been applied.
public class PriceDetailViewModel { public decimal Price; public decimal ListPrice; public string CurrencyFormatString; }
Add controllers for each product page type you intend to display. The controller’s action retrieves products of the specific product page type and displays them. The following sample code uses products of the LearningProductType page type as an example.
We recommend using a dependency injection container to initialize instances of used API services (e.g. IShoppingService or ICatalogPriceCalculatorFactory).
public class LearningProductTypeController : Controller { private readonly string siteName = SiteContext.CurrentSiteName; private readonly IShoppingService shoppingService; private readonly ICatalogPriceCalculatorFactory calculatorFactory; /// <summary> /// Constructor for the LearningProductTypeController class. /// </summary> public LearningProductTypeController() { // Initializes instances of services required to manage product price calculation and the shopping cart // For real-world projects, we recommend using a dependency injection container to initialize service instances shoppingService = Service.Resolve<IShoppingService>(); calculatorFactory = Service.Resolve<ICatalogPriceCalculatorFactory>(); } /// <summary> /// Displays a product listing page of the class's product page type. /// </summary> public ActionResult Listing() { // Gets products of the product page type (via the generated page type code) List<LearningProductType> products = LearningProductTypeProvider.GetLearningProductTypes() .LatestVersion(false) .Published(true) .OnSite(siteName) .Culture("en-US") .CombineWithDefaultCulture() .WhereTrue("SKUEnabled") .OrderByDescending("SKUInStoreFrom") .ToList(); ShoppingCartInfo cart = shoppingService.GetCurrentShoppingCart(); // Prepares a collection of products of the LearningProductType page type to be sent to a view IEnumerable<ProductListItemViewModel> productListing = products.Select( product => new ProductListItemViewModel( product, GetPrice(product.SKU, cart), product.Product.PublicStatus?.PublicStatusDisplayName)); // Displays the action's view with an initialized view model return View(productListing); } // 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); } }
Using the product catalog price calculation API
The Kentico E-Commerce solution performs all product price calculations via ICatalogPriceCalculatorFactory. First call the GetCalculator(int siteId) method on an instance of ICatalogPriceCalculatorFactory to obtain a calculator of catalog prices for a site with the given site ID, then use the calculator’s GetPrices method to calculate the catalog price for a given product. GetPrices returns an object of the ProductCatalogPrices type that holds the product’s calculated catalog price values.
See the GetPrice method from the code sample above for a working example.
Tip: To avoid needless code duplicity, we recommend wrapping product catalog price calculations into a reusable method.
Add a view that sets the appearance of the product listing. For example:
@model IEnumerable<ProductListItemViewModel> <h2>Product listing of the LearningProductType</h2> <div> @* Iterates over all products. *@ @foreach (ProductListItemViewModel product in Model) { @* Generates a URL leading to the product's detail page. *@ <a href="@Url.RouteUrl("Product", new {guid = product.ProductPageGuid, productAlias = product.ProductPageAlias})"> <h3>@product.Name</h3> @* Displays information about the product's public status. *@ @if (!string.IsNullOrEmpty(product.PublicStatusName)) { <span>@product.PublicStatusName</span> } @* Displays the product's image. *@ @if (!string.IsNullOrEmpty(product.ImagePath)) { <img src="@Url.Kentico().ImageUrl(product.ImagePath, SizeConstraint.MaxWidthOrHeight(300))" alt="@product.Name"> } @* Displays the product's other properties. *@ <div> @if (!product.Available) { <span>Out of stock</span> } <span>@String.Format(product.PriceModel.CurrencyFormatString, product.PriceModel.Price)</span> @if (product.PriceModel.ListPrice > product.PriceModel.Price) { <s>@String.Format(product.PriceModel.CurrencyFormatString, product.PriceModel.ListPrice)</s> } </div> </a> } </div>
Customers can now browse through a listing of products of a particular product page type. Clicking on a product item from the listing sends them to the product’s details page.
Adjusting product image size
To change the size of product images, use the ImageUrl HTML helper method. The system then automatically resizes the image based on the entered size while keeping the image’s aspect ratio.
The method takes the following parameters:
- productImagePath – a string representing a path to the image that you want to display
- SizeConstrant – a SizeConstrant object consisting of:
- Width – you can enter the required width in pixels
- Height – you can enter the required height in pixels
—OR— - Size – you can enter both width and height in pixels – if used, this does not preserve the image’s aspect ratio
—OR— - MaxWidthOrHeight – larger images are scaled down, smaller images are not scaled up
@Url.Kentico().ImageUrl(productImagePath, SizeConstraint.Size(width, height))
For example:
<img src="@Url.Kentico().ImageUrl("~/image.jpg", SizeConstraint.Size(300, 300))" alt="Product image" />
Customizing product listing URLs
To provide SEO friendly identifiers in URLs, you can route your product listings to, for example, <your domain>/Store/<product page type>.
Add a new route to the RouteConfig class in the App_Start folder:
routes.MapRoute(
name: "Store",
url: "Store/{controller}",
defaults: new { action = "Listing" },
constraints: new { controller = "LearningProductType" }
);
The example assumes that each controller displays only products of one product page type and uses the Listingaction to list them (as described in Displaying product listings).