And with that magic word you can probably guess that this Sitecore instance is built with MVC. For a little more information about it, this instance of Sitecore is running version 7.5 and MVC 5.1 with Razor syntax.
The Problem
The problem is I want to keep an organized structure for my site not only with my Sitecore architecture, but also for my Controllers and Views. I am dealing with a Sitecore instance that will host multiple sites in the near future, so I have structured Sitecore to have different site-specific folders in my Content, Layouts, Media Library, and Templates sections. There are also Global folders in these sections where I will store items that are universal, but the specifics around my architecture can be saved for another post.Along with this structure in Sitecore, I would like to organize my Controllers and Views in a similar fashion to have site-specific subfolders within my site directory. However, due to the MVC approach to Controller and View mapping, I can’t just create subfolders within my Controllers and Views folders and expect the ViewEngine to magically find them.
The MVC Approach to Views
Anyone who has worked with MVC probably knows that a specific structure and naming convention is key for the mapping between Controllers and Views. By default, MVC stores Controllers in the Controllers folder, and Views in the Views folder. That’s pretty straight forward. Additionally, the default convention for views follows:- ~/Views/[Controller]/[Action]
- ~/Views/Test – matches the Controller name
- ~/Views/Shared – default shared layouts location
My Views Organization
Before arriving at a solution, I first defined my folder structure I wanted for this project. Since I am dealing with Sitecore, my Views relate to Layouts and Renderings in the system. I have therefore defined my Views into 3 groups for now:- Layouts – There is only a Main layout for now, but should we need a completely separate look, I have this.
- Containers – These relate to Header, Footer, Main content areas, or other structures that contain mostly Sitecore placeholders. The purpose of these is to define the page structure: rows, columns, etc…
- ContentItems – These are simple content items displaying Sitecore fields, or custom types like Navigation, Accordion, Sliders, etc....
To keep things simple and uniform, this same folder structure is how I defined my Layouts and Renderings in Sitecore.
My Solution
With my directory structure in mind, I wanted to simplify my work while accounting for new sites in the future. I also wanted to take advantage of ViewEngines and their ability to eliminate my need for specifying every View path in my Controllers or Views. To do this, I needed to create a custom ViewEngine.For a custom ViewEngine, we need to define the locations where our Views can reside. These are known as location formats, and the RazorViewEngine defines two sets: PartialViewLocationFormats and ViewLocationFormats, defined as string arrays. An example of a default Razor location format is:
- ~/Views/{1}/{0}.cshtml – {1} is the Controller name and {0} is the Action name
- ~/Views/[SITE FOLDER]/Containers/{0}.cshtml
- ~/Views/[SITE FOLDER]/ContentItems/{0}.cshtml
- ~/Views/[SITE FOLDER]/Layouts/{0}.cshtml
To make this more flexible, with more ease to adding and removing sites from the list, I created a key in the Web.config AppSettings with a comma delimited list of my site names as the value.
[pre class="brush: xml" title="Web.config"]
<appSettings>
<!-- comma delimited name of websites being used-->
<add key="Websites" value="Test1,Test2"/>
</appSettings>[/pre]
In my CustomViewEngine (very original name) I then split the value of the “Websites” key, and loop through each, adding the proper location format to my list. I build and return this array of location formats in a separate function called "getSiteLocationFormats". Additionally, my folder names are stored in a string array for me to loop through for ease of maintenance.
[pre class="brush: csharp" title="getSiteLocationFormats"]
private string[] getSiteLocationFormats() {
var sitesString = ConfigurationManager.AppSettings["Websites"];
string[] folders = { "Containers", "ContentItems", "Layouts" };
List<string> locations = new List<string>();
if (sitesString != null && !string.IsNullOrWhiteSpace(sitesString)) {
foreach (var site in sitesString.Split(',')) {
foreach (var folder in folders) {
locations.Add(string.Format("~/Views/{0}/{1}/{{0}}.cshtml", site, folder));
}
}
}
return locations.ToArray();
}
[/pre]
The location formats then have to be specified on the ViewEngine and this is done by setting the PartialViewLocationFormats and ViewLocationFormats. You can specify one or both depending on your need, but I wanted all views in these locations so I specified both. This step, plus the call to my "getSiteLocationFormats" function is done in the constructor:
[pre class="brush: csharp" title="CustomViewEngine constructor"]
public CustomViewEngine() {
var locationFormats = getSiteLocationFormats();
PartialViewLocationFormats = PartialViewLocationFormats.Union(locationFormats).ToArray();
ViewLocationFormats = ViewLocationFormats.Union(locationFormats).ToArray();
}
[/pre]
After that, you simply register the ViewEngine. This is done in the Application_Start method of the Global.asax:
[pre class="brush: csharp" title="Global.asax.cs"]
protected void Application_Start() {
ViewEngines.Engines.Add(new Custom.Configuration.CustomViewEngine());
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}[/pre]
Some people clear out the default ViewEngines before adding the new one, others leave it. I don't think it's a significant performance hit in my case, nor do I know, if Sitecore is doing anything that would be affected by me doing it, so I just left it and added my new one.
With this approach in place, whenever I need to add a new website, I can modify the comma delimited list in my Web.config, add my site folders to my Views directory, and start building my Controllers and Sitecore Renderings.
No comments:
Post a Comment
Share your thoughts or ask questions...