Relationship names have been around in Kentico since I’ve been using it, and are a great way to create structure within your content. When Kentico 9 was released, it introduced the Pages data type, to build on the relationship names to prompt the editors to link up content and to allow related content to be sorted.
Advanced content modeling
Finding out how to use them can be a challenge, as searching for "Pages" in the documentation for a CMS is clearly going to produce more than a couple of pages of results. In fact, the main piece of documentation I refer to is actually called Advanced content modelling, which is not necessarily what people are going to be searching for.
I’ve seen and answered a few questions now on DevNet and Stack Overflow on the Pages data type related to how you can get the data out, so I thought I’d share my understanding of how this data type works.
Under the hood
The Pages data type is actually an extension of relationship names. Relationship names allow content editors to give a meaningful relationship between to content node by providing a human-friendly description. Out of the box, you get a relationship name called “is related to”. When you use this in practice, you end up with a construct of [page A]{is related to}[Page B].
To achieve this, there is a fairly simple underlying structure in the database:
In our example, the Node ID for [page A] is stored in LeftNodeID, and for [page A], it is stored in RightNodeID. You’ll notice in here that there is no way to sort relationships out of the box.
To implement our Pages data type, Kentico extended this structure a little further, and our database schema changed to the following:
Firstly, in the CMS_RelationshipName table, a new field marks a relationship name as ad-hoc. Whenever you create a new Pages data type field on a page type, a record is created here and RelationshipNameIsAdHoc is set to true.
When we actually assign some content on the form tab, records are created in the CMS_Relationship table. The relationship is marked as ad-hoc via the RelationshipIsAdHoc column (interestingly, the value is always true or NULL, I’ve yet to see False as a value in this column). Order is applied via the RelationshipOrder column and defaults to the order in which the relationships are added. The order of the pages change be changed by dragging them up and down. The change in order is immediate - you don’t need to save the page - so be careful on production sites.
Using the Pages in transformations
The common question on the Pages data type is how to use them.
Let’s set up a sample scenario to see how you can use these relationships. We want to list some pizzas on our site with a list of the ingredients that make them. We have two page types for this:
- Ingredient. This has fields for Name, and Description.
- Pizza. This has fields for Name, Description, and Ingredients. As you may guess, Ingredients is a Pages data type.
We’ll set up some content as follows:
Ingredients
Name | Description |
Pepperoni | A peppery, hot salami that is soft and slightly smoky. |
Mozzarella | Southern Italian cheese made from Italian buffalo's milk. |
Ham | Organic ham, locally sourced. |
Pineapple | The tabs vs. spaces topic for pizza. |
Tomato sauce | Freshly made every day. |
Pizzas
Name | Description | Ingredients |
Hawaiian | It’s basically a bit wrong, but still tasty. | Tomato sauce, Mozzarella, Ham, Pineapple |
Pepperoni | This has to be one of the all-time classic pizzas. | Tomato sauce, Mozzarella, Pepperoni |
What I want is a page that lists out the pizzas with their title, description, and list of ingredients in slightly smaller text. So it should look something like this:
Hawaiian
It’s basically a bit wrong, but still tasty.
- Tomato sauce
- Mozzarella
- Ham
- Pineapple
To set this out on the page, I’m going to create two Text/XML transformations; on transformation for the Pizza, and one for the Ingredient. These look as follows:
The main thing to note here is how we are applying the transformation to the ingredients. A common thing I see is people asking why nothign is showing on their page. Whereas in the above we're applying an inner-repeater, a lot of people by default will simply add {%PizzaDescription%} to their transformation; the column for this field in the underlying table is NULL in Kentico so nothing would happen.
In our code above, we're applying the ingredients transformation by doing 3 things:
- Documents[NodeAliasPath] retrieve the current document in the repeater
- .RelatedDocuments["pizza.pizza_1774d783-c905-42b5-ab3a-64780be38674"] uses the unique ad-hoc name of our pages relationship to get the individual ingredients.
- .ApplyTransformation("pizza.ingredient.IngredientList") applies the transformation that we want.
The name of the ad-hoc relationship is defined as [page type codename including namespace]_[field GUID]. So in our example above, pizza.pizza is the codename of my page type and 1774d783-c905-42b5-ab3a-64780be38674 is the GUID that Kentico created for the field. You can see the GUID in the fields list for the page type as shown below:
Now that we have a transformation, we can create a page and start putting things together.
This is as simple as adding a repeater to the page. In my example, my Pizzas page sits at the route of my site, so I've setup the repeater as follows:
- Path: /Pizzas/%
- Page types: pizza.pizza
- Maximum nesting level: 1
- ORDER BY expression: NodeOrder
- Columns: PizzaName,PizzaDescription, nodeGUID, NodeAliasPath
- Transformation: pizza.pizza.PizzaMenuList
Now when we look at out Pizzas page, we should see the list we're expecting!
Using the Pages in listing controls/viewers
So how do we use Pages via a standard listing listing control (such as a repeater)? Let's say for example we're designing a template for the individual pizza page and want to list out the ingredients. To do this, we need to look at the relationships settings in the Repeater. Our relationship names show up in here, including the ad-hoc one that was created for ingredients. So your repeater settings may look as follows:
As for the transformation, this will return the ingredients, so we need to ensure that we're selecting the right properties as follows:
- Path: /Pizzas/%
- Page types: pizza.ingredient
- Maximum nesting level: 1
- ORDER BY expression: NodeOrder
Columns: IngredientName,IngredientDescription, nodeGUID, NodeAliasPath - Transformation: pizza.ingredient.IngredientList
That's all we need, now we have a list of ingredients on our pizza page.
Any negatives?
One of the main things I like about relationship names is that I can look at the related pages tab in the pages application on any page and see where it is used. Sadly, these ad-hoc relationships do not show here, which feels like a bit of a loss to me. It’s probably pretty simple to add this to the Pages application give how simple it is to extend then Kentico UI.
Summary
The pages type is incredibly useful and powerful. Relationship names have always been great, but now being able to sort items was quite an issue. Fixing these issues gives strong platform for modelling content that works will within the Portal development model and is easy for content editors to understand. The same patterns that you employ to model your content here can also work well with the MVC development model within Kentico. Finding out how they work can be a challenge, but hopefully this post has helped with that!