Überlegungen zum eigenen CMS - der Seitenbaum
In diesem Beitrag betrachten wir die Folgerungen, die wir aus den grundlegenden Gedanken für unseren Seitenbaum gemacht haben.
Neben der Wahl des richtigen Datenbank Algorithmus konstruiere ich in diesem Artikel auch die ersten Tabellen die für die Seitenverwaltung unseres CMS benötigt werden.
Der Seitenbaum
Hierarchische Darstellung in der Datenbank
Es gibt auf Datenbankebene zwei grundlegende Algorithmen um Daten hierarchisch zu speichern.
Das eine ist das Adjacency Modell, bei dem jeder Datensatz die ID des Elternelements mitschleppt.
Das Verfahren kann sicherlich ohne Weiteres nachvollzogen werden. Leider ergeben sich bei genauer Betrachtung aber erhebliche Probleme: Versucht man Funktionen wie Verschieben, Löschen, Pfad bilden, usw. abzubilden, werden die Queries zunehmend komplex und rekursiv, die Verarbeitung ist aufwendig und Ressourcen fressend.
Die gängigsten Lesevorgänge sollten mit einem Query abgebildet werden! - Nur so lässt sich ein einfaches und schnelles System am die Beine stellen. Im Adjacency Modell ist dies nicht möglich.
Ein CMS greift mehr lesend auf die Datenbank zu als schreibend, dies müssen wir unbedingt berücksichtigen!
Für unsere Anforderung eignet sich das NestedSets Modell am besten.
Jeder Datensatz einer Seite bekommt zwei Werte: Links und Rechts. Der Baum wird durch Inkrementierung dieser Werte von Links nach rechts durchlaufen bis man wieder rechts oben ankommt. Etwas Gehirnschmalz in dieses System zu stecken lohnt sich in jedem Fall, da diese Problemstellung quasi täglich auftritt. Wem dieses Datenmodell noch nicht geläufig ist, findet hier eine sehr gute Anleitung.
Die Tabelle Pages
Bezugnehmend auf das Nested Set Modell hat eine Seite schon mindestens vier Eigenschaften: ID, Name, lft, rgt.
Im Hinblick auf eine spätere Erweiterung sollten wir noch ein Feld: published hinzufügen. Eine Standarddatenerfassung für created und modified ist auch nicht verkehrt.
Damit lassen sich Seiten schon beliebig tief verschachteln und veröffentlichen.
Auch hier sollte weiter überlegen was eine Seite nun eigentlich sein kann: Zunächst ist sie der Container für Seiteninhalte, aber sie könnte auch einen externen Verweis oder einen Ordner für andere Datensätze repräsentieren.
Dafür hänge ich ein weiteres Feld mit dem Namen type an. Dieses Feld kann später mit einer Auswahl an Schlüsselwörtern gefüllt werden.
Die Indizes werden wie Folgt verteilt:
- Primär: ID
- INDEX: lft
- INDEX: rgt
Selbstredent wird die Tabelle im fertigen System wesentlich mehr Felder besitzen, allerdings reichen diese Felder hier bereits aus um die Grundfunkion der Inhaltsverwaltung abbilden zu können. Ich erachte es für sinnvoll nicht alle Informationen über eine Seite direkt in dieser Tabelle zu speichern, da die Typisierung hier zu viele Varianten einbringt. Mir genügt es hier wenn eine Seite weis, was sie ist und wo sich ihre Position im Seitenbaum befindet.
Alles weitere werden wir abhänig vom Seitentyp in einer separaten Tabelle unterbringen.
Wer sich jetzt schon Gedanken über die Implementierung macht, der sollte sich zunächst diesen Beitrag zum MVC Design zu Gemüte führen. Wir werden den Seitenbaum später vom Repositiory abholen und das Seitenmodel je nach Typ der Seite magisch instanzieren.
Eigenschaften von Seiten
Da eine Seite einen bestimmten Typ hat, werden sich ihre Eigenschaften grundlegend unterscheiden. Hierfür wählen wir die einfachste Lösung: Eine Tabelle die der Seiten-ID, Eigenschaften-Werte Paare zuweist. Dieser Ansatz ist simpel und Modular genug um alle anfallenden Anwendungfälle abzubilden.
Wir werden bei späteren Erweiterungen sicherlich zusätzliche Daten zu einer Seite anlegen. So könnte ein Ast des Seitenbaumes eine andere Domain repräsentieren, oder schlicht Meta-Informationen einer Seite an gehangen werden.
Der Rattenschwanz an Eigenschaften, die denkbar sind ist zwar gewaltig lang, allerdings ist nicht alles sinnvoll. Zugriffsrichtlinien gehören z.B. in die cms_pages Tabelle, da sich diese direkt auf die Seite auswirken, unabhängig vom Seitentyp. Ein Ordner braucht keinen Menü-Alias, eine Seite eventuell - diese Eigenschaften kommen später in diese Tabelle.
Der Einfachheit halber, kann dabei eine Seite viele Eigenschaften haben, eine Eigenschaft ist dabei aber immer nur einer Seite zugeteilt.
Dadurch ergibt sich die neue Tabelle: cms_pageproberties mit der Eigenschaft Proberty und dem zugehörigen Wert Value.
Den Zusammenhang herstellen
Die Verwaltung dieser Verknüpfung möchte ich großteils der Datenbank überlassen:
Hier kommen die Fähigkeiten der InnoDB Engine zum Tragen. Wir ersellen in der Tabelle cms_pageproberties den Index page_id und definieren eine Fremdschlüsselbeziehung auf cms_pages.id. Bei einem Update wird die Weiterleitung ON UPDATE auf No Action gesetzt, bei ON DELETE auf CASCADE gesetzt. Damit löscht die Datenbank alle Eigenschaften einer Seite wenn sie gelöscht wird.
Im MySQL Workbench sieht das Ergebnis dann so aus:

Der nächste Schritt
Mit diesem Artikel habe wir eine saubere Grundstruktur für unser Content Mangament System geschaffen. Unser Seitenbaum ist nun, zumindest auf Datenbankebene fertig. Im nächsten Beitrag werde ich einige Queries aufstellen die uns die wichtigsten Informationen in unsere Implementierung holen.
Aktualisiert (Sonntag, den 08. Januar 2012 um 18:50 Uhr)