Base Page Reusable Field Schema

Werden in mehreren Items die gleichen Eigenschaften immer wieder benötigt wurde früher oft ein Basis Page Type angelegt von dem dann jede weitere Seite erbte. Dabei konnte nur ein einziger Page Type vererbt werden. Dies wurde in Xperience by Kentico ↗ mit Reusable Field Schemas stark verbessert.

Nun besteht mit den Reusable Field Schemas die Möglichkeit gleich mehrere unterschiedliche Eigenschaft-Sets zu vererben.

Wie man die Reusable Field Schemas einsetzen kann möchte ich in diesem Beitrag mit einem Beispiel aus Xperience by Kentico (Version 29.0.2) erläutern.

Was ist ein Reusable Field Schema

Viele Content Typen benötigen gemeinsame Eigenschaften. Zum Beispiel Web Content Items benötigen meist Eigenschaften um Teaserdaten aufzunehmen oder Einstellungen zur Navigation oder verschiedener Metadaten.

Dies könnten z.B. folgende Eigenschaften sein:

  • Teaser Image
  • Teaser Text
  • Meta Robots Einstellungen
  • Meta OG Eingenschaften
  • Ob die Seite in der Navigation auftauchen soll
  • Ob sie in der Sitemap eingetragen werden soll

Es sind allgemeine Eigenschaften die nicht an ein Template gebunden sind sondern ganz allgemein für die Seite gelten.

Diese Eigenschaften kann ich in Xperience by Kentico ↗ in Reusable Field Schemas kategorisiert unterbringen, z.B.:

  • Reusable Field Schema Teaser Daten
  • Reusable Field Schema Navigation
  • Reusable Field Schema Meta Einstellungen

In einem Content Type kann ich dann ganz einfach die passenden Reusable Field Schemas auswählen und diese werden automatisch zu dem Content Type hinzugefügt.

Damit ist es jetzt möglich mehrere Sets an unterschiedlichen Eigenschaften zu vererben.

Content Type mit Reusable Field Schema

Ein Reusable Field Schema in Kentico anlegen

In Kentico wird dazu einfach unter Configuration / Content Types im Reiter List of Reusable Field Schemas eine neue angelegt.

Die Felder werden exakt wie in den Content Types angelegt.

Im Detail kann man dies in der Dokumentation ↗ nachlesen.

Code generieren lassen

Sobald man sie in Kentico angelegt hat, kann man sich den dazu passenden Code von Kentico generieren lassen.

Siehe auch Code Generierung ↗ in der Dokumentation.

Die Generierung kann man nach Bedarf anpassen. In meinem Beispiel sieht dies so aus:

dotnet run --no-build -- --kxp-codegen --type "ReusableFieldSchemas" --skip-confirmation --location "../Xelerator.RCL/Business/DocumentEngine/Xel" --namespace "Xelerator.RCL.Business.DocumentEngine.Xel"

Reusable Field Schemas werden als Interface generiert, die Content Types, welche sie nutzen, implementieren diese.

=> Daher nicht vergessen den Code für die Content Types ebenfalls generieren zu lassen!

Habe ich meine Content Types und meine Reusable Field Schemas generieren lassen, sieht das Ergebnis so aus:

namespace Xelerator.RCL.Business.DocumentEngine.Xel
{
    /// <summary>
    /// Represents a page of type <see cref="ContentPage"/>.
    /// </summary>
    [RegisterContentTypeMapping(CONTENT_TYPE_NAME)]
    public partial class ContentPage : IWebPageFieldsSource, IBasepage
    {
        /// <summary>
        /// Code name of the content type.
        /// </summary>
        public const string CONTENT_TYPE_NAME = "Xel.ContentPage";


        /// <summary>
        /// Represents system properties for a web page item.
        /// </summary>
        [SystemField]
        public WebPageFields SystemFields { get; set; }

        [.....]

    /// <summary>
    /// Defines a contract for content types with the <see cref="IBasepage"/> reusable schema assigned.
    /// </summary>
    public interface IBasepage
    {
        /// <summary>
        /// Code name of the reusable field schema.
        /// </summary>
        public const string REUSABLE_FIELD_SCHEMA_NAME = "Basepage";


        /// <summary>
        /// BasePageRobots.
        /// </summary>
        public string BasePageRobots { get; set; }


        /// <summary>
        /// BasePageChildLayerNavigationName.
        /// </summary>
        public string BasePageChildLayerNavigationName { get; set; }

        [.....]

Zusätzlich zu dem IWebPageFieldsSource wird nun für jedes Reusable Field Schema welches mein Content Type nutzt ein weiteres Interface implementiert.

Reusable Field Schema in einer Datenbankabfrage

Im Query Code kann ich nun über den Codename des Reusable Field Schemas meine Query konkretisieren. In meinem Beispiel eine Methode, die mir alle Kinder eines Web Content Items zurück gibt, welche das Basepage Schema geerbt haben.

In Zeile 22 sieht man, dass hierfür ein neuer ContentTypeQueryParameter zur Verfügung steht:

public static async Task<List<IWebPageFieldsSource>> GetSubitemsBySchema(
    this IWebPageFieldsSource webPageFieldsSource,
    string[] reusableSchemaName)
{
    // needed services
    IContentQueryExecutor executor
        = Service.Resolve<IContentQueryExecutor>();
    IWebPageQueryResultMapper mapper
        = Service.Resolve<IWebPageQueryResultMapper>();
    IEventLogService eventLogService
        = Service.Resolve<IEventLogService>();

    // init return value
    List<IWebPageFieldsSource> webPages = [];

    // query definition
    ContentItemQueryBuilder builder = new ContentItemQueryBuilder();
    builder
        .ForContentTypes(item =>
        {
            item.ForWebsite(includeUrlPath: true);
            item.OfReusableSchema(reusableSchemaName);
            item.WithContentTypeFields();
        })
        .Parameters(p =>
        {
            p.Where(con =>
            {
                con.WhereEquals(
                    nameof(WebPageFields.WebPageItemParentID),
                    webPageFieldsSource.SystemFields.WebPageItemID);
            });
        });

    // map the results to an object
    IEnumerable<IWebPageFieldsSource> results
        = await executor
            .GetMappedWebPageResult<IWebPageFieldsSource>(builder: builder);

    // parse to List
    webPages = results.ToList();

    // exit
    return webPages;
}

Auf diese Weise kann ich in meiner Navigation Seiten ausblenden, die im Reusable Field Schema „Basepage“ eingestellt haben, dass sie nicht dort auftauchen wollen.

Dazu kann ich das IWebPageFieldSource Objekt einfach in das passende IBasepage Object umwandeln und die Eigenschaften direkt abrufen.

pages
    .AddRange(
        (await homepage.GetSubitemsBySchema([IBasepage.REUSABLE_FIELD_SCHEMA_NAME]))
        .Where(page => page.Cast<IBasepage>().BasePageHideInMainNav == false));

Fazit

Die Reusable Field Schemas sind eine starke Verbesserung gegenüber dem „Inherit Base Page“ von Kentico Xperience.

Die Möglichkeit gleich mehrere dieser Vererbungen einzurichten ist sehr flexibel und ermöglicht spezifischere Datenbankabfragen.

Dadurch können anspruchsvolle Vererbungen sehr gut und einfach umgesetzt werden.