Daten verwalten

Quickstart

Im objektorientierten Ansatz von n2n verwalten wir Daten über Business Objects. In diesem Kapitel stellen wir dir die Arbeit mit Business Objects vor!

  1. Business Objects erstellen
  2. Eigenschaften und Beziehungen
  3. Annotationen von Beziehungen
  4. Entities registrieren

Business Objects erstellen

Entitäten sind Objekte, welche der Verwaltung von Daten dienen. Business Objects (in Deutsch Geschäftsobjekte) sind Entitäten, welche neben den Daten auch Logik zur Datenverarbeitung enthalten können. In unserem Blog Modul werden wir mit drei Business Objects arbeiten: BlogArticle, BlogCategory, BlogComment. Bitte beachte, dass es nicht darum geht, ein tolles Blog Modul zu erstellen, sondern dir mit wenig Code möglichst viel Funktionalität von n2n zu demonstrieren.

Die Erstellung von Business Objects ist sehr repetitiv. Diese Arbeiten kannst du später einfacher und schneller mit dem Hangar erledigen. Damit du n2n aber kennen lernst, zeigen wir dir hier die manuelle Erstellung von Business Objects!

Für unsere Business Objects legen wir in unserem Modul ein separates Verzeichnis an: /app/qs/bo

Business Objects bestehen aus ihren Eigenschaften und den entsprechenden Getter- und Setter Methoden. Das einzige, was etwas komplizierter zu verstehen ist, sind die Annotationen der Beziehungen zwischen den Business Objects.

business-objects.zip

Lade die Business Objects herunter und kopiere sie in das Verzeichnis der Business Objects

Eigenschaften und Beziehungen

Werfe zuerst einen genaueren Blick auf die importierten Business Objects.

BlogArticle

Ein BlogArticle kann mehrere Kommentare (OneToMany Beziehung) und mehrere Kategorien (ManyToMany) zugeteilt haben. Diese Beziehungen werden im Business Object annotiert:

    // Annotation der Beziehungen:
    private static function _annos(AnnoInit $ai) {
        $ai->p('comments', new AnnoOneToMany(BlogComment::getClass(), 'blogArticle', CascadeType::ALL));
        $ai->p('comments', new AnnoOrderBy(array('id' => 'DESC')));
        $ai->p('categories', new AnnoManyToMany(BlogCategory::getClass(), null,
                CascadeType::PERSIST|CascadeType::MERGE));
    }

Die verbundenen Kommentare und Kategorien stehen als Eigenschaften zur Verfügung ($comments, $categories). Bei Beziehungen werden die Eigenschaften (Property) annotiert, welche die verbundenen Objekte  enthalten.

Bei den Kommentaren (BlogComments) wird eine OneToMany Beziehung annotiert, weil auf einen BlogArticle mehrere Kommentare entfallen können. AnnoOneToMany werden folgende Parameter übergeben:

  • Die Klasse, des verknüpften Objektes
  • Die Eigenschaft des verknüpften Objektes, wo der Artikel gespeichert wird
  • Der CascadeType legt fest, welche Operationen (PERSIST, MERGE, REMOVE, DETACH) an die vernüpften Objekte weitergegeben werden. In unserem Falle werden sämtliche Operationen (CascadeType::ALL) weitergeleitet. Dies bedeutet, dass wenn der Blog-Artikel gelöscht wird (REMOVE), die Operation an die verbundenen Kommentare weitergegeben wird und diese ebenfalls gelöscht werden!

Die zweite Annotation von Comments legt fest, dass die Comments, wenn sie über getComments() aufgerufen werden, immer mit id DESC sortiert werden.

Die dritte Annotation legt die Beziehung zu BlogCategory fest. Wir definieren eine Many-To-Many Beziehung. Hier lassen wir den zweiten Parameter (mappedBy) leer und geben bei CascadeType nur PERSIST und MERGE weiter. Weil REMOVE fehlt, werden beim Löschen eines Artikels, die verbundenen Kategorien nicht gelöscht. Das macht Sinn, weil ja auch noch andere Blog-Artikel mit diesen Kategorien verbunden sein könnten!

BlogCategory

Auch bei BlogCategory sticht die Annotation der Beziehung zu BlogArticle ins Auge:

    private static function _annos(AnnoInit $ai) {
        $ai->p('articles', new AnnoManyToMany(BlogArticle::getClass(), 'categories',
                CascadeType::PERSIST|CascadeType::MERGE));
    }

Hier sehen wir jetzt das andere Ende der Many-To-Many Beziehung zwischen Artikel und Kategorien. Im Unterschied zur Annotation bei BlogArticle ist hier der zweite Parameter von AnnoManyToMany nicht NULL. Auf einer Seite einer Many-To-Many Beziehung müssen wir eine mappedBy Eigenschaft angeben.

BlogComment

Bei BlogComment sehen wir jetzt eine Many-To-One Beziehung. Es ist das andere Ende der One-To-Many Beziehung auf BlogArticle:

    private static function _annos(AnnoInit $ai) {
        $ai->p('blogArticle', new AnnoManyToOne(BlogArticle::getClass(), CascadeType::MERGE|CascadeType::PERSIST));
        $ai->p('image', new AnnoManagedFile());
    }

Die Annotation auf die Eigenschaft $image zeigt an, dass es sich bei der Eigenschaft um ein File handelt. AnnoManagedFile() bewirkt, dass sich ein FileManager um die Speicherung des hochgeladenen Files kümmert.

Dass die Eigenschaft $image etwas Spezielles ist, siehst du auch an der Setter Methode:

    public function setImage(File $image = null) {
        $this->image = $image;
    }

Hier wird bestimmt, dass der übergebene Wert ein File sein muss. Um auch NULL Werte zu erlauben ist das = null wichtig.

Eine weitere Besonderheit stellen wir im Konstruktor fest:

    public function __construct() {
        $this->setCreated(new \DateTime());
    }

Mit dieser Anweisung setzt du den Standardwert der Eigenschaft created das aktuelle Datum und die entsprechende Zeit. Jeder Kommentar der erstellt wird, kriegt so das aktuelle Datum als created Wert.

Der Konstruktor eines Business Objects kann dazu verwendet werden, Standard-Werte eines Business Objects zu definieren.

 Die Annotationen der Beziehungen wollen wir als nächstes genauer anschauen.

Annotationen von Beziehungen

n2n kennt folgende Klassen, um Beziehungen zwischen Business Objects zu annotieren:

  • AnnoOneToMany (1:n Beziehung)
  • AnnoManyToOne (n:1 Beziehung)
  • AnnoManyToMany (n:n Beziehung)
  • AnnoOneToOne (1:1 Beziehung)

Die ersten drei Klassen werden nachfolgend genauer erläutert.

1:n Beziehung

Diese Annotation befindet sich auf der Klasse BlogArticle. Ein BlogArticle kann mehrere BlogComments haben. Die Klasse AnnoOneToMany kennt folgende wichtige Parameter im Konstruktor:

Eigenschaft Erklärung Wert / Beispiel
$targetEntity Die Klasse des Objektes, zu welchem eine Beziehung besteht. BlogComment::getClass()
$mappedBy Die Eigenschaft auf der das andere Objekt die Beziehung speichert. 'blogArticle'
BlogComment besitzt die Eigenschaft $blogArticle, wo die Beziehung gespeichert wird.
$cascadeType Hier wird definiert, welche Operationen weitergegeben werden. CascadeType::ALL
Bei allen Datenbankmanipulationen werden diese auf die angehängten Objekte übertragen. Achtung: dies schliesst das Löschen mit ein!
$fetchType Hier wird bestimmt, wann die verbundenen Objekte geladen werden sollen. Unmittelbar beim Laden des Objektes oder erst beim Zugriff. FetchType::EAGER, lädt alle verbundenen Objekte sofort
FetchType::LAZY, lädt die Objekte erst, wenn sie gebraucht werden.
$orphanRemoval Bestimmt, ob nicht mehr verlinkte Objekte gelöscht werden sollen. true oder false

CascadeType

$cascadeType wollen wir uns genauer anschauen. Der CascadeType bestimmt, welche Operationen an die verbundenen Objekte weitergegeben werden. Es gibt folgende wichtige Typen:

  • CascadeType::PERSIST (erstellen)
  • CascadeType::MERGE
  • CascadeType::REMOVE (löschen)
  • CascadeType::ALL

Hier beachten wir PERSIST und REMOVE genauer. Wird ein Objekt persistiert, werden mit CascadeType::PERSIST auch die verbundenen Objekte persistiert. Mit REMOVE wird ein Objekt gelöscht. CascadeType::REMOVE bedeutet entsprechend, dass auch alle verbundenen Objekte mitgelöscht werden! Bei CascadeType::ALL werden alle Operationen weitergegeben.

In den meisten Fällen ist für CascadeType CascadeType::PERSIST|CascadeType:Merge oder CascadeType::ALL sinnvoll. Ausschlaggebend ist meistens, ob REMOVE weitergegeben werden darf oder nicht. Genauere Informationen findest du im Lifecycle Artikel.
Wird einem Business Object fälschlicherweise CascadeType::REMOVE oder CascadeType::ALL mitgegeben, werden verbundene Objekte mitgelöscht. Dies kann zu Datenverlust führen!

n:1 Bezieung

Diese Annotation befindet sich auf BlogComment. Ein BlogComment gehört immer zu einem BlogArticle. Die Klasse AnnoManyToOne besitzt folgende wichtige Konstruktor Parameter:

Eigenschaft Erklärung Wert / Beispiel
$targetEntity Die Klasse des Objektes, zu welchem eine Beziehung besteht. BlogArticle::getClass()
$cascadeType Bestimmt welche Operationen an die verbundenen Objekte weitergegeben werden. CascadeType::MERGE|CascadeType::PERSIST
$fetchType Hier wird bestimmt, wann die verbundenen Objekte geladen werden sollen.  

Im Gegensatz zur 1:n Beziehung gibt es hier kein $orphanRemoval.

n:n Beziehung

Die letzte Beziehung, welche wir genauer anschauen, ist die n:n Beziehung. Wir finden sie auf BlogArticle und BlogCategory. Ein BlogArticle kann mehreren BlogCategory zugeordnet sein und umgekehrt. Die Annotation AnnoManyToMany besitzt folgende wichtige Konstruktor Parameter:

Eigenschaft Erklärung Wert / Beispiel
$targetEntity Die Klasse des Objektes, zu welchem eine Beziehung besteht. BlogArticle::getClass()
$mappedBy Der MappedBy Wert muss nur auf einer Seite der Beziehung angegeben werden. Das System entscheidet so, welche Klasse Master und welche Slave ist. null oder die Eigenschaft auf dem anderen Objekt
$cascadeType Bestimmt welche Operationen an die verbundenen Objekte weitergegeben werden. CascadeType::MERGE|CascadeType::PERSIST
$fetchType Hier wird bestimmt, wann die verbundenen Objekte geladen werden sollen.  
$orphanRemoval Bestimmt, ob nicht mehr verlinkte Objekte gelöscht werden sollen.  

SQL-Dump

Damit sind die Business Objects, welche wir im Rahmen dieses Quickstarts brauchen, definiert und deren Beziehungen erklärt. Für die Speicherung der Business Objects braucht es noch die entsprechenden Tabellen in der Datenbank. n2n verwendet eine vordefinierte Naming-Strategy. Diese leitet aus den Namen von Klassen und Eigenschaften die korrespondierenden Tabellen- und Spaltennamen ab. Beispiel: BlogArticle Objekte werden in der Tabelle blog_article gespeichert.

Über Annotationen können Tabellen- (AnnoTable) und Spaltennamen (AnnoColumn) individuell gewählt werden. n2n erlaubt gar die Definition einer eigenen Naming-Strategy.

Datenbankbank Tabellen können auch über den Hangar erstellt werden. Im Quickstart erstellen wir die Tabellen aber mit dem folgenden SQL File.

quickstart.sql

Dieses SQL File enthält die benötigten CREATE TABLE und INSERT Statements für die oben gezeigten Business Objects.

Entities registrieren

Alle Entities müssen in n2n registriert werden. Ergänze dazu var/etc/qs/app.ini um folgende Zeilen:

[orm]
entities[] = "qs\bo\BlogArticle"
entities[] = "qs\bo\BlogCategory"
entities[] = "qs\bo\BlogComment"

Ab sofort kennt n2n unsere Business Objects. Jetzt sind wir bereit, um auf unsere Daten zuzugreifen!

« Views & Templates Auf Daten zugreifen »

comments_title

post_login_to_create

questions_title