When creating the detail view component for the genres and albums, we want the URL to be pretty and SEO friendly. So an id in the URL will not suffice.
Sitecore already has some base functionalities, like the creation of wildcard pages. The item name of these pages must be '*' and must be the first item of that level.
Once you open a page on that site level, it either searches for a specified item with that page name or otherwise opens the wildcard page.
First thing we need, is to create UrlProviders which will take care of finding the item which is mentioned in the URL.
Take for example the following GenreUrlProvider:
public class GenreUrlProvider : IGenreUrlProvider { private readonly IGenreService _genreService; public GenreUrlProvider(IGenreService genreService) { _genreService = genreService; } public Genre GetCurrentGenre() { var httpContext = HttpContext.Current; if (httpContext == null || httpContext.Request.Url == null) return null; if (!string.IsNullOrEmpty(httpContext.Request.Params["genreid"])) { return _genreService.GetGenre(httpContext.Request.Params["genreid"]); } var displayName = HttpUtility.UrlDecode(httpContext.Request.Url.Segments.Last().Split('?').First()); return _genreService.GetGenre(displayName); } }
The GetCurrentGenre method, first checks if the genre id is specified in the request parameters. If this isn't the case it takes the last segment of the URL and gets the genre by that display name.
Same thing should be done for the AlbumUrlProvider:
public class AlbumUrlProvider : IAlbumUrlProvider { private readonly IAlbumService _albumService; public AlbumUrlProvider(IAlbumService albumService) { _albumService = albumService; } public Album GetCurrentAlbum() { var httpContext = HttpContext.Current; if (httpContext == null || httpContext.Request.Url == null) return null; if (!string.IsNullOrEmpty(httpContext.Request.Params["albumid"])) { return _albumService.GetAlbum(httpContext.Request.Params["albumid"]); } var displayName = HttpUtility.UrlDecode(httpContext.Request.Url.Segments.Last().Split('?').First()); return _albumService.GetAlbum(displayName); } }
Next up is creating the components. These components are fairly simple. The request the current item from the UrlProvider and returns it to the view.
These components should be created in the StoreController. The Details method is used for the album details page, while the Browse method is used by the genre detail page.
public class StoreController : Controller { private readonly IAlbumService _albumService; private readonly IGenreService _genreService; private readonly IAlbumUrlProvider _albumUrlProvider; private readonly IGenreUrlProvider _genreUrlProvider; public StoreController(IAlbumService albumService, IGenreService genreService, IAlbumUrlProvider albumUrlProvider, IGenreUrlProvider genreUrlProvider) { _albumService = albumService; _genreService = genreService; _albumUrlProvider = albumUrlProvider; _genreUrlProvider = genreUrlProvider; } public ActionResult Index() { return View(_genreService.GetAll()); } public ActionResult Details(string id) { var album = _albumUrlProvider.GetCurrentAlbum() ?? _albumService.GetAlbum(id); return View(album); } public ActionResult Browse(string id) { Genre genre = _genreUrlProvider.GetCurrentGenre() ?? _genreService.GetGenre(id); genre.Albums = _albumService.GetAlbumsByGenre(genre.Id).ToList(); return View(genre); } }
As you can see, before the current genre is returned to the view it also fills the albums list with related albums from that genre using the AlbumService.
As for the views, they are also pretty simple and should look like this:
@inherits Glass.Mapper.Sc.Web.Mvc.GlassView<MusicStore.Web.Models.Album> <h2>@Editable(a => a.Title)</h2> <p> @RenderImage(a => a.AlbumArt, isEditable: true) </p> <div id="album-details"> <p> <em>Genre:</em> @Model.Genre.Name </p> <p> <em>Artist:</em> @Model.Artist.Name </p> <p> <em>Price:</em> @String.Format("{0:F}",Model.Price) </p> <p class="button"> @Html.ActionLink("Add to cart", "AddToCart", "ShoppingCart", new { id = Model.Id.ToString() }, "") </p> </div>
As you can see the Details view already contains an action link to a method and controller which we did not create yet. This will be added in the next part of this series and will eventually add the current album to the cart.
@inherits Glass.Mapper.Sc.Web.Mvc.GlassView<MusicStore.Web.Models.Genre> @{ ViewBag.Title = "Browse"; } <div class="genre"> <h3><em>@Model.Name</em> Albums</h3> <ul id="album-list"> @foreach (var album in Model.Albums) { <li> <a href="/Store/Details?id=@album.Id"> @RenderImage(a => album.AlbumArt) <span>@album.Title</span> </a> </li> } </ul> </div>
Next thing we should obviously do, is create the renderings in Sitecore.
And finally create the detail page for both the album details(under /Home/Store/Details/*) and the genre details(under /Home/Store/Browse/*) and add the newly created renderings in the 'main' placeholder.
Finally we should have two new pages which should look like this:
The Detail page.
The Browse page.