Model

-Active Record
-Create-RUD
-C-Read-UD
-CR-Update-D
-CRU-Delete
-Valtidatoren
-Model-Callbacks
-Relationen zwischen Tabellen
-Reservierte Spaltennamen


Active Record

Auch in Zeiten der objektorientierten Programmierung (Java, Smaltalk, usw.) werden die Daten meist in relationalen Datenbanken (z.B. MySql, sqlite, …) gespeichert. Das Einbetten von diesen Datenbanken in ein durchgehend objektorientiertes Programm führt zu einem Stielbruch.
Dieses Manko wird durch das sogenannte „Object-Relation-Mapping (ORM)" verhindert. ORM definiert Regeln wie eine relationale Tabelle einer Datenbank in Objekte überführt wird, welche dann von dem OO-Progamm verwendet werden können:
- Die Tabelle wird zur Klasse
- Die Datensätze werden zu Objekten
- Die Spalten werden zu Objekt-Attributen

Active Record stellt in Rails genau diese Funktionalität bereit. In der Beispielsanwendung wird die Tabelle „newsmessages" durch die Klasse Newsmessage repräsentiert.
app\models\newsmessage.rb Diese Klasse erbt nur von ActivRecord und besitzt keine eigene Funktionalität, da alle benötigten Standartoperationen für die Arbeit auf Datenbanken in der erbenden Klasse bereits implementiert sind. Im Folgenden wird exemplarisch auf die Funktionen eingegangen.

Model-Konventionen
Tabellenname:	news_items
Klasse:		NewsItem
Pfad:		app/models/news_item.rb

[top]

Create-RUD

Das Anlegen eines neuen Datensatzes entspricht dem Erzeugen eines Objektes:
Anlegen einer News Alternativ nimmt die "new" Funktion einer von AR abgeleiteten Klasse auch einen Hash mit den Attributwerten entgegen. Wie zu erkennen ist, wird das "id"-Attribut nicht von uns gesetzt, kann jedoch nach dem Speichern ausgelesen werden. Rails behandelt die Tabellenspalte mit dem Namen "id", per Konvention.
[top]

C-Read-UD

.find
Die find-Methode dient dem Auslesen eines Datensatzes mit einer speziellen ID:
Eine Suche nach Datensätzen nur mittels derer ID ist nicht praktikabel, daher bietet die find-Methode noch weitere Suchmethoden an, indem man find mehrere Parameter übergibt:
:all / :first
Dieser Parameter gibt die Menge von Datensätzen an, die zurückgegeben werden soll. Steht einer der beiden alleine, so werden alle Elemente oder nur das Erste ausgegeben.

:conditions => condition
Mittels des Parameters :conditions wird die Datenbank anhand der übergebenen condition durchsucht. Sie entspricht dem „WHERE"-Teil einer SQL-Anfrage. So wird die condition auch formuliert:

:order => order
Dieser Parameter garantiert, dass die gefundenen Datenobjekte in der gewünschten Reihenfolge zurückgegeben werden. Hierbei orientiert sich die Formulierung der order auch an SQL:

:limit => #ofrows
Mittels des limit-Parameter wird die Anzahl der zurückgegebenen Datensätze begrenzt, so dass nur die ersten #ofrows geliefert werden. Hierbei ist es sinnvoll eine order auf den Datensätzen zu definieren, um auch die gewünschten Daten zu erhalten.
:offset => #ofcutoff
Dieser Parameter macht meist nur Sinn unter Verwendung des limit-Parameters. So lassen sich z.B. leicht Katalogseiten erstellen:

.find_
Alternativ zur find-Methode können auch die, der menschlichen Sprache nachempfundenen, dynamischen Finder verwendet werden:
- find_by_X (liefert das erste gefunden Element)
- find_all_by_X (liefert alle passenden Datensätze)
entspricht:
Zusätzlich lassen sich auch Anfragen über mehrere Spalten mittels "_and_” und „_or_" erstellen:

Eine weitere nützliche Funktion, die im Read-Kontext automatisch bereitgestellt wird, ist:
.count
Count liefert nach einer anfrage die Anzahl der erhaltenen Datensätze

[top]

CR-Update-D

Das ändern von Datensätzen kann auf zwei Weisen geschehen: Durch das verwenden von den find- und save-Methoden:

[top]

CRU-Delete

Löschen von Datensätzen wird durch die delete- oder destroy-Methoden realisiert. Der Unterschied zwischen ihnen ist, dass delete die Daten einfach nur löscht, während destroy die Konsistenz der Datenbank nach der Löschung garantiert.
.delete / .destroy
Um Datensätze zu löschen wird .delete eine ID oder ein Array von IDs übergeben.
.delete_all / .destroy_all
Die Methode delete_all erhält als Parameter eine condition und löscht alle passenden Datensätze
[top]

Valtidatoren

Active Record bietet zusätzlich noch die Möglichkeit der Kontrolle der Daten, die in die Datenbank geschrieben werden. Hierzu werden in dem jeweiligen Model spezielle Methoden definiert:
überprüft, dass beim Anlegen einer Newsheadline und newstext vorhanden sind, sowie das eine Headline nie doppelt in der Datenbank vorkommt: Valtidatoren werden automatisch von AR aufgerufen und stellen so die Konsistenz der Datenbank sicher. Insgesamt stehen hierfür 18 Validatoren mit default-Implementierung zur Verfügung (z.B. validate_on_update, validates_format_of, …).
[top]

Model-Callbacks

Activ Record garantiert nicht nur die Konsistenz der Daten, sondern auch den gesamten life-Cycle. Hierzu werden im Model Methoden definiert und als Callback für bestimmte Zustände des Models bei den drei Operationen (Create, Update und Destroy) eingehängt. AR ruft nun diese Methoden automatisch auf. Damit kann in der Beispielanwendung sichergestellt werden, dass mit einer Meldung auch alle dazugehörigen Kommentare gelöscht werden.
app\models\newsmessage.rb
[top]

Relationen zwischen Tabellen

Typischerweise kommen die Tabellen datenbankgestützter Programme nicht in der redundanten ersten Normalform vor, vielmehr existieren viele Abhängigkeiten zwischen den einzelnen Tabellen. Diese werden mittels der Foreign-Keys innerhalb der Datenbank angegeben. Active Record unterstützt hierbei die drei Basisfälle solcher Abhängigkeiten:
- 0-oder-1-zu-1
- 1-zu-n
- m-zu-n
Um die Funktionalität zu verdeutlichen wird ein Beispielprogramm verwendet. Es besitzt zwei in Relation stehende Tabellen:
Die Tabelle mit den Newsmeldungen:
Eine Tabelle mit Kommentaren zu den Meldungen
Und der Abhängigkeit, dass ein Kommentar zu einer Meldung gehört Erzeugen eines Model für die Comments
 > ruby script\generator model Comment
Also treten hier folgende Relationen auf:
- Ein Kommentar gehört zu einer Meldung
- Zu einer Meldung gehören n Kommentare

Diese Abhängigkeiten müssen Activ Record mitgeteilt werden:

app\models\newsmessage.rb Durch das Hinzufügen von "has_many" zum Newsmessage-Model wird, das Model um ein user_comments Attribut erweitert. Die restlichen Angaben definieren die Klasse der Child-Objekte, das Attribut, welches auf das Parent-Objekt zeigt, eine bestimmte Ordnung auf den Child-Objekten (siehe Klasse.find()) und die Anweisung beim Löschen des Parent-Objekts die Child-Objekte mit zu löschen (:dependent => true).
Im Attribut user_comments steht der Parent-Klasse ein Array mit allen verlinken Kommentaren zur Verfügung und stellt zusätzlich folgende Methoden bereit:
news.user_comments << <comment>
Hängt einen comment an die Liste der Kommentare des Parent-Objekts an. Altzernativ können die Funktionen .concat(<comment>, …) oder .push(<comment>, …) verwendet werden, sie erhalten als Parameter einen oder eine Liste von Kommentaren.
news.user_comments.delete(<comment>, …)
Setzt die Foreign-Keys der comments auf NULL und löst so die Abhängigkeit der Datensätze. self.user_comments.clear entspräche hierbei einem Aufruf von delete mit allen comments als Parameter.
app\models\comment.rb Das Hinzufügen von "belongs_to" zum Comment-Model gibt ihm eine Referenz auf die Klasse des Parent-Objekts. Diese wird hier nicht wie beim Newsmessage-Model durch die direkte Angabe, sondern durch Rails Sprachkonventionen erkannt. Die Foreign-Key-Spalte wird nach folgendem Schema benannt: ParentKlasseName + "_id". Durch diese Referenz ist der Zugriff auf das Parent-Objekt möglich, sowie auf folgende Methoden:
comment.newsmeassage = <news>
Setzt den Foreign-Key auf die ID der angegebenen Meldung.
comment.create_newsmeassage(parameter)
Erzeugt zu dem Kommentar eine News mittels der angegeben Parameter und verbindet diese über den Foreign-Key.

[top]

Reservierte Spaltennamen

createted/updated_at und createted/updated_on
In diese Spalten wird automatisch die Zeit der Erzeugung, bzw. des letzten Updates gespeichert.
childname_count
In dieser Spalte wird im Parent-Objekt mitgezählt wieviel Child-Objekte es besitzt. Hierfür muss in der Deklaration von belongs_to der Parameter „counter_cache" auf true gesetzt werden (siehe models\comment.rb).
id und parentname_id
Definieren, wie schon erwähnt, die ID, bzw. den Foreign-Key.
[top]