Unicorn Configuration Predicate Presets

Introducing Unicorn Configuration Predicate Presets

Overview

Configuration Predicate Presets (or just Predicate Presets) is a new feature to Unicorn designed to help you get rid of a lot of configuration repetition on your projects.

If you’re following Helix guidelines for Unicorn (not saying you should but many of you are), there’s a good chance you have a flock of configuration files in your projects looking something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/src/Features/Carousel/Carousel.serialization.config
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<unicorn>
<configurations>
<configuration name="Feature.Carousel" dependencies="Foundation.*" patch:after="configuration[@name='Foundation.Serialization.Base']">
<predicate>
<include name="templates" database="master" path="/sitecore/templates/Feature/Carousel" />
<include name="branches" database="master" path="/sitecore/templates/branches/Feature/Carousel" />
<include name="renderings" database="master" path="/sitecore/layout/renderings/Feature/Carousel" />
<include name="thumbnails" database="master" path="/sitecore/media library/Feature/Carousel" />
<include name="rules" database="master" path="/sitecore/system/Settings/Rules/Insert Options/Rules/Carousel" />
</predicate>
</configuration>
</configurations>
</unicorn>
</sitecore>
</configuration>

And then you would have 20 or however many similar configuration files in each of your Feature projects.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/src/Features/Carousel/Flyout.serialization.config
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<unicorn>
<configurations>
<configuration name="Feature.Flyout" dependencies="Foundation.*" patch:after="configuration[@name='Foundation.Serialization.Base']">
<predicate>
<include name="templates" database="master" path="/sitecore/templates/Feature/Flyout" />
<include name="branches" database="master" path="/sitecore/templates/branches/Feature/Flyout" />
<include name="renderings" database="master" path="/sitecore/layout/renderings/Feature/Flyout" />
<include name="thumbnails" database="master" path="/sitecore/media library/Feature/Flyout" />
<include name="rules" database="master" path="/sitecore/system/Settings/Rules/Insert Options/Rules/Flhyout" />
</predicate>
</configuration>
</configurations>
</unicorn>
</sitecore>
</configuration>

And so on. Your <include>s might look a bit different but you get the idea.

With a Predicate Preset defined, you could remove some of the obvious redundancy here. Following this example you could add the following to the Foundation.Serialization.Base configuration.

1
2
3
4
5
6
7
8
9
<predicatePresets type="Unicorn.Configuration.PredicatePresetHandler, Unicorn" singleInstance="true">
<preset id="Component" database="master">
<include name="templates" database="$database" path="/sitecore/templates/Feature/$name" />
<include name="branches" database="$database" path="/sitecore/templates/branches/Feature/$name" />
<include name="renderings" database="$database" path="/sitecore/layouts/renderings/Feature/$name" />
<include name="thumbnails" database="$database" path="/sitecore/media library/Feature/$name" />
<include name="renderings" database="$database" path="/sitecore/system/settings/Rules/Insert Options/Rules/$name" />
</preset>
</predicatePresets>

And with this in place, your configurations could look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/src/Features/Carousel/Carousel.serialization.config
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<unicorn>
<configurations>
<configuration name="Feature.Carousel" dependencies="Foundation.*" patch:after="configuration[@name='Foundation.Serialization.Base']">
<predicate>
<preset id="Component" name="Carousel">
</predicate>
</configuration>
</configurations>
</unicorn>
</sitecore>
</configuration>

And

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/src/Features/Carousel/Flyhout.serialization.config
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<unicorn>
<configurations>
<configuration name="Feature.Carousel" dependencies="Foundation.*" patch:after="configuration[@name='Foundation.Serialization.Base']">
<predicate>
<preset id="Component" name="Flyout">
</predicate>
</configuration>
</configurations>
</unicorn>
</sitecore>
</configuration>

Better, yes?

But it doesn’t end there. How about this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/src/Foundation/Serialization/Features.Components.serialization.config
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/">
<sitecore>
<unicorn>
<predicatePresets type="Unicorn.Configuration.PredicatePresetHandler, Unicorn" singleInstance="true">
<preset id="Component" database="master">
<include name="$name.templates" database="$database" path="/sitecore/templates/Feature/$name" />
<include name="$name.branches" database="$database" path="/sitecore/templates/branches/Feature/$name" />
<include name="$name.renderings" database="$database" path="/sitecore/layouts/renderings/Feature/$name" />
<include name="$name.thumbnails" database="$database" path="/sitecore/media library/Feature/$name" />
<include name="$name.renderings" database="$database" path="/sitecore/system/settings/Rules/Insert Options/Rules/$name" />
</preset>
</predicatePresets>
<configurations>
<configuration name="Components" dependencies="Foundation.*">
<predicate>
<preset id="Component" name="Carousel">
<preset id="Component" name="Flyout">
<preset id="Component" name="CTA Banner">
<preset id="Component" name="Header">
<preset id="Component" name="Header Navigation">
<preset id="Component" name="Footer">
<preset id="Component" name="Sidebar">
</predicate>
</configuration>
</configurations>
</unicorn>
</sitecore>
</configuration>

Now that’s more like it. This is more in line with how I recommend using and implementing Unicorn. Minimise the project clutter, don’t mix serialisation configurations all over the place, and don’t mix your Unicorn serialised content with Visual Studio project assets. I know right?, gasp and so on. You are free to choose your own path obviously :-)

A closer look

So Predicate Presets works as an extended Configuration Parser. What that means is, that the preset handling happens when a configuration loads.

So if I have a Predicate Preset defined such as:

1
2
3
<preset id="Component" database="master">
<include name="templates.$name" database="$database" path="/sitecore/templates/Feature/$name" />
</preset>

And use it in a Configuration like:

1
2
3
4
5
<configuration name="Components" dependencies="Foundation.*">
<predicate>
<preset id="Component" name="Carousel">
</predicate>
</configuration>

When that configuration loads and is exposed to Unicorn, Unicorn will see it like this:

1
2
3
4
5
<configuration name="Components" dependencies="Foundation.*">
<predicate>
<include name="templates.Carousel" database="master" path="/sitecore/templates/Feature/Carousel" />
</predicate>
</configuration>

And from that point on, everything is as it has always been.

The important take away from this is, that you must include some sort of variant in the name attribute inside your Predicate Preset. If you don’t, Unicorn is going to get angry with you.

If I had done this:

1
2
3
<preset id="Component" database="master">
<include name="templates" database="$database" path="/sitecore/templates/Feature/$name" />
</preset>

And then this:

1
2
3
4
5
6
<configuration name="Components" dependencies="Foundation.*">
<predicate>
<preset id="Component" name="Carousel">
<preset id="Component" name="Flyout">
</predicate>
</configuration>

This is what would arrive at Unicorn:

1
2
3
4
5
6
<configuration name="Components" dependencies="Foundation.*">
<predicate>
<include name="templates" database="master" path="/sitecore/templates/Feature/Carousel" />
<include name="templates" database="master" path="/sitecore/templates/Feature/Flyout" />
</predicate>
</configuration>

And that would be invalid configuration. And we don’t want that.

I recommend doing your Predicate Presets something like this:

1
<include name="$id.templates.$name" database="$database" path="/sitecore/templates/Feature/$name" />

Token handling

As for the tokens, it’s actually as simple as it looks. The Predicate Preset Parser will - generally speaking - take attributes from the preset definition and use them as tokens when expanding the Predicate Preset. This is probably best explained with a few examples.

1
2
3
4
5
6
7
<preset id="Component" database="master">
<include name="templates.$name" database="$database" path="/sitecore/templates/Feature/$name" />
</preset>
<predicate>
<preset id="Component" name="Carousel">
</predicate>

From this, the Predicate Preset Parser will first try and resolve $database from <preset id="Component" name="Carousel"> but there is no database attribute to be found. It will then look to the Preset Predicate Definition <preset id="Component" database="master"> and find database="master". So $database becomes master and this is then replaced using simple string substitution on all attribute values in the preset.

So <include name="templates.$name" database="$database" path="/sitecore/templates/Feature/$name" /> becomes <include name="templates.$name" database="master" path="/sitecore/templates/Feature/$name" />

The process is then repeated for other tokens, in this case $name. The important take away here is, you are completely free to come up with as many attributes as you want or need.

An example could be:

1
2
3
<preset id="Component" database="master">
<include name="$id.templates.$name" database="$database" path="/sitecore/templates/Feature/$group/$name" />
</preset>

Which could then be used as:

1
2
3
4
5
6
<predicate>
<preset id="Component" name="Carousel" group="Banners">
<preset id="Component" name="Flyout" group="Navigation">
<preset id="Component" name="Content With Image" group="Content">
<preset id="Component" name="Content Without Image" group="Content">
</predicate>

The system is very flexible and I’m sure you can think of better uses of it than I can, I’m mostly focused on explaining all of this and writing this post ;-) I could easily see this being used in for instance SXA, with tokens like $tenant and $site.

In summary

Predicate Presets can help you avoid configuration duplication and keep consistency in your solution. It makes for much easier configuration setup and maintenance and provides you with a much better overview of what you have going on in your solution. Especially so, if you take my recommendation and turn the dial way back on the number of configuration files you have in your solution.

If you find Predicate Presets useful and you put them to good use in your projects, don’t be shy. Reach out to me and share what you’re doing. I would love for Unicorn to ship with some of the best and most widely used Predicate Presets out of the box. Especially if you come up with ones of general use for JSS and SXA projects for instance.

Enjoy :-)

Share