Files
yii/docs/guide/de/database.ar.txt
2009-12-13 16:48:49 +00:00

706 lines
26 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
ActiveRecord
============
Obwohl man mit Yii-DAO praktisch jede datenbankbezogene Aufgabe erledigen kann,
werden wir wahrscheinlich 90% unserer Zeit mit dem Schreiben von
SQL-Anweisungen für die üblichen CRUD-Operationen (create, read, update und delete)
verbringen. Auch die Codewartung wird erschwert, wenn dieser
mit SQL Anweisungen durchmischt ist. Zur Lösung dieser Probleme können wir
ActiveRecord nutzen.
ActiveRecord (AR) ist eine gängige objektrelationale Abbildungstechnik (engl.:
object-relational mapping,ORM). Jede AR-Klasse repräsentiert eine Tabelle der
Datenbank (oder einen View) deren Attribute durch AR-Klasseneigenschaften
repräsentiert werden. Eine AR-Instanz repräsentiert eine Zeile in der Tabelle.
Übliche CRUD-Operationen sind als AR-Methoden implementiert. Das hat zur Folge,
dass wir in eher objektorientierter Weise auf unsere Daten zugreifen
können. Zum Beispiel können wir den folgenden Code dazu benutzen, um eine neue Zeile
in die `Post`-Tabelle einzufügen:
~~~
[php]
$post=new Post;
$post->title='Beispielbeitrag';
$post->content='Inhalt des Beitrags';
$post->save();
~~~
Im Folgenden beschreiben wir, wie wir AR-Instanzen anlegen und für CRUD-Operationen
benutzen können. Im nächsten Kapitel werden wir dann darauf eingehen, wie wir AR
verwenden können, um mit Beziehungen in einer Datenbank (DB) umzugehen.
Der Einfachheit halber benutzen wir für die Beispiele in diesem Abschnitt
folgende Datenbanktabelle. Beachten Sie bitte, dass sie im folgenden SQL AUTOINCREMENT durch
AUTO_INCREMENT ersetzen müssen, falls Sie MySQL verwenden.
~~~
[sql]
CREATE TABLE Post (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
title VARCHAR(128) NOT NULL,
content TEXT NOT NULL,
createTime INTEGER NOT NULL
);
~~~
> Note|Hinweis: AR ist nicht dafür gedacht, alle datenbankbezogenen Aufgaben zu lösen.
AR ist zweckmäßig, um Datenbanktabellen in PHP abzubilden sowie für
Abfragen, die kein komplexes SQL erfordern. In komplexeren Fällen sollte Yii-DAO
benutzt werden.
Aufbau einer DB-Verbindung
--------------------------
AR setzt auf eine DB-Verbindung auf, um DB-bezogene Operationen durchzuführen.
Standardmäßig geht AR davon aus, dass die Applikationskomponente `db` die
notwendige [CDbConnection]-Instanz liefert, die als DB-Verbindung dient. Die
folgende Applikationskonfiguration zeigt ein Beispiel:
~~~
[php]
return array(
'components'=>array(
'db'=>array(
'class'=>'system.db.CDbConnection',
'connectionString'=>'sqlite:path/to/dbfile',
// Schema Caching einschalten, um die Performance zu verbessern
// 'schemaCachingDuration'=>3600,
),
),
);
~~~
> Tip|Tipp: ActiveRecord verwendet Metadaten von Tabellen, um
die Spalteninformationen zu ermitteln. Das Lesen und Analysieren dieser Daten
braucht Zeit. Falls das Schema Ihrer Datenbank
kaum mehr geändert wird, sollten Sie das Schema-Caching einschalten,
indem Sie die Eigenschaft [CDbConnection::schemaCachingDuration] auf
einen Wert größer 0 setzen.
Die AR-Unterstützung ist auf bestimmte Datenbankmanagementsysteme (DBMS) begrenzt.
Derzeit werden folgende DBMS unterstützt:
- [MySQL 4.1 oder später](http://www.mysql.com)
- [PostgreSQL 7.3 oder später](http://www.postgres.com)
- [SQLite 2 und 3](http://www.sqlite.org)
- [Microsoft SQL Server 2000 oder höher](http://www.microsoft.com/sqlserver/)
- [Oracle](http://www.oracle.com)
> Note|Hinweis: Microsoft SQL Server wird seit Version 1.0.4 unterstützt und
> Oracle seit Version 1.0.5.
Falls Sie eine andere Applikationskomponente als `db` benutzen möchten, oder
wenn Sie mit AR auf mehreren Datenbanken arbeiten, sollten Sie
[CActiveRecord::getDbConnection()] überschreiben. Die [CActiveRecord]-Klasse
ist die Basisklasse für alle AR-Klassen.
> Tip|Tipp: Es gibt zwei Wege, um mit AR auf mehren Datenbanken zu arbeiten.
Falls die Datenbankschemas sich unterscheiden, können Sie unterschiedliche
AR-Basisklassen mit verschiedenen Implementierungen von
[getDbConnection()|CActiveRecord::getDbConnection] erzeugen. Andernfalls
ist es günstiger, die statische Variable [CActiveRecord::db] dynamisch zu
ändern.
Definieren von AR-Klassen
-------------------------
Um auf eine Datenbanktabelle zuzugreifen, müssen wir zuerst eine AR-Klasse
durch Erweitern von [CActiveRecord] definieren. Jede Klasse repräsentiert
eine einzelne Datenbanktabelle, und eine AR-Instanz repräsentiert eine Zeile
der Tabelle. Das folgende Beispiel zeigt den nötigen Mindestcode einer AR-Klasse,
die die Tabelle `Post` repräsentiert.
~~~
[php]
class Post extends CActiveRecord
{
public static function model($className=__CLASS__)
{
return parent::model($className);
}
}
~~~
> Tip|Tipp: Da AR-Klassen oft an vielen Stellen verwendet werden, können
>wir das ganze Verzeichnis mit AR-Klassen importieren, anstatt diese einzeln
>einzubinden. Liegen beispielsweise alle unsere AR-Klassen im Verzeichnis
>`protected/models`, können wir die Applikation wie folgt konfigurieren:
> ~~~
> [php]
> return array(
> 'import'=>array(
> 'application.models.*',
> ),
> );
> ~~~
Standardmäßig hat die AR-Klasse den gleichen Namen wie die Datenbanktabelle.
Überschreiben Sie die [tableName()|CActiveRecord::tableName]-Methode, falls
sie sich unterscheiden sollen. Die [model()|CActiveRecord::model]-Methode wird in jedem
AR deklariert (worauf wir in Kürze eingehen werden).
> Info: Um ein [Tabellenpräfix](/doc/guide/database.dao#using-table-prefix) zu
> verwenden, wie es seit Version 1.1.0 verfügbar ist, kann man die
> [tableName()|CActiveRecord::tableName]-Methode wie folgt überschreiben:
> ~~~
> [php]
> public function tableName()
> {
> return '{{post}}';
> }
> ~~~
> Statt eines vollständigen Tabellennamens geben wir also den Namen der
> Tabelle ohne Präfix, aber dafür in doppelten geschweiften Klammern zurück.
Über die Eigenschaften der entsprechenden AR-Instanz, kann auf die Felder
einer Tabellenzeile zugegriffen werden. Der folgende Code setzt zum Beispiel das
`title`-Feld (Attribut).
~~~
[php]
$post=new Post;
$post->title='Ein Beispielbeitrag';
~~~
Obwohl die `title`-Eigenschaft in der `Post`-Klasse nicht explizit deklariert
wurde, können wir doch mit dem obigen Code auf sie zugreifen. Das liegt daran,
dass `title` ein Feld in der `Post`-Tabelle ist und CActiveRecord mittels der
magischen PHP Methode `__get()` erlaubt, darauf wie auf eine Eigenschaft
zuzugreifen. Falls wir auf diese Weise versuchen, ein nicht existierendes Feld
anzusprechen, wird eine Exception ausgelöst.
> Info|Info: In diesem Handbuch benennen wir Spalten in sog.
CamelCase-Schreibweise (z.B. `createTime`), da sie wie normale
Objekteigenschaften angesprochen werden, die ja ebenfalls
CamelCase verwenden. Während dies für mehr Konsistenz in unserem
PHP-Code sorgt, kann es auf der anderen Seite bei manchen Datenbanksystemen zu
Problemen führen. Bei PostgreSQL spielt die Groß-/Kleinschreibung zum Beispiel
standardmäßig keine Rolle und wir müssen eine Spalte in Anführungszeichen setzen,
falls sie gemischte Groß-/Kleinschreibung verwendet. Es kann daher ratsam sein,
Spalten- und Tabellennamen nur in Kleinbuchstaben zu benennen (z.B. `create_time`) um
möglichen Problemen mit der Schreibweise vorzubeugen.
Einfügen von Datensätzen
------------------------
Um eine neue Zeile in eine Datenbanktabelle einzufügen, erzeugen wir eine
neue Instanz der entsprechenden AR-Klasse, setzen die den Tabellenfeldern
entsprechenden Eigenschaften und rufen die [save()|CActiveRecord::save]-
Methode auf, um das Einfügen abzuschließen.
~~~
[php]
$post=new Post;
$post->title='Beispielbeitrag';
$post->content='Inhalt des Beispielbeitrags';
$post->createTime=time();
$post->save();
~~~
Wird der Primärschlüssel automatisch vergeben (auto-increment), enthält die AR-Instanz
nach dem Einfügen den aktuellen Primärschlüssel. Im obigen Beispiel spiegelt
die `id`-Eigenschaft den Wert des Primärschlüssels für den neu eingefügten
Beitrag wieder, obwohl wir diese Eigenschaft nicht explizit gesetzt haben.
Falls für eine Tabellenspalte im Schema ein statischer Vorgabewert definiert
wurde (z.B. ein String oder eine Zahl), enthält die AR-Instanz nach dem
Anlegen automatisch diesen Wert. Eine Möglichkeit, um diesen Vorgabewert zu
verändern, besteht darin, diese Eigenschaft explizit in der
AR-Klasse festzulegen:
~~~
[php]
class Post extends CActiveRecord
{
public $title='Bitte den Titel eingeben';
......
}
$post=new Post;
echo $post->title; // Dies führt zur Anzeige von: Bitte den Titel eingeben
~~~
Seit Version 1.0.2 kann man einem Attribut auch einen Wert vom Typ
[CDbExpression] zuweisen, bevor der Datensatz in der Datenbank gespeichert
wird. Um zum Beispiel einen Zeitstempel zu speichern, wie er von der
MySQL-Funktion `NOW()` geliefert wird, können wir folgenden Code verwenden:
~~~
[php]
$post=new Post;
$post->createTime=new CDbExpression('NOW()');
// $post->createTime='NOW()'; funktioniert nicht
// 'NOW()' würde wie ein String behandelt
$post->save();
~~~
> Tip|Tipp: Obwohl uns AR erlauben, Datenbankoperationen ohne umständliche
SQL-Ausdrücke durchzuführen, möchten wir oft wissen, welche SQL-Abfragen
tatsächlich von AR ausgeführt werden. Dies kann durch Aktivieren des
[Log-Features](/doc/guide/topics.logging) von Yii erreicht werden. Wir können
zum Beispiel [CWebLogRoute] in der Anwendungskonfiguration einschalten und
sehen dann am Ende jeder Webseite die ausgeführten SQL-Abfragen. Seit Version
1.0.5 können wir [CDbConnection::enableParamLogging] in der
Anwendungskonfiguration auf `true` setzen, um auch die Parameter, die an die
SQL-Abfragen gebunden werden, zu loggen.
Lesen von Datensätzen
---------------------
Um Daten aus der Datenbanktabelle zu lesen rufen wir eine der folgenden
`find`-Methoden auf:
~~~
[php]
// Finde die erste Zeile, die die angegebene Bedingung erfüllt
$post=Post::model()->find($condition,$params);
// Finde die Zeile mit dem angegebenen Primärschlüssel
$post=Post::model()->findByPk($postID,$condition,$params);
// Finde die Zeile mit den angegeben Attributwerten
$post=Post::model()->findByAttributes($attributes,$condition,$params);
// Finde die erste Zeile unter Anwenden der angegeben SQL-Anweisung
$post=Post::model()->findBySql($sql,$params);
~~~
Oben rufen wir die `find`-Methode mit `Post::model()` auf. Wie bereits
erwähnt, wird die statische Methode `model()` für jede AR-Klasse benötigt.
Die Methode gibt eine AR-Instanz zurück, die dazu benutzt wird, um in einem
Objektkontext auf Methoden auf Klassenebene zugreifen zu können (ähnlich zu
statischen Klassenmethoden).
Wenn die `find`-Methode eine Zeile findet, die die Abfragebedingung erfüllt,
gibt sie eine `Post`-Instanz zurück, deren Eigenschaften die entsprechenden
Werte der Felder der Zeile enthalten. Wir können die geladenen Werte dann
wie Eigenschaften von normalen Objekten auslesen, z.B. `echo $post->title;`.
Falls in der Datenbank zur gegebenen Abfragebedingung nichts gefunden wird,
gibt die `find`-Methode null zurück.
Beim Aufruf von `find` verwenden wir `$condition` und `$params` um
Abfragebedingungen anzugeben. Dabei kann `$condition` ein String sein, der die
`WHERE`-Klausel in einer SQL Abfrage darstellt und `$params` ein Array aus
Parametern, dessen Werte an die Platzhalter in `$condition` gebunden werden.
Zum Beispiel:
~~~
[php]
// Finde die Zeile mit postID=10
$post=Post::model()->find('postID=:postID', array(':postID'=>10));
~~~
> Note|Hinweis: Bei manchen Datenbanksystemen muss die `postID`-Spalte in
obigem Beispiel escaped werden. Verwenden wir zum Beispiel PostgreSQL, müssten
wir die Kondition als `"postID"=:postID` schreiben, da PostgreSQL
standardmäßig die Groß-/Kleinschreibung von Spaltennamen nicht berücksichtigt.
Wir können `$condition` auch für komplexere Abfragebedingungen verwenden.
Statt eines Strings kann `$condition` auch eine Instanz von [CDbCriteria]
sein, die es uns erlaubt, weitere Bedingungen statt nur der `WHERE`-Klausel
anzugeben, z.B.:
~~~
[php]
$criteria=new CDbCriteria;
$criteria->select='title'; // Nur die 'title' Spalte wird ausgewählt
$criteria->condition='postID=:postID';
$criteria->params=array(':postID'=>10);
$post=Post::model()->find($criteria); // $params ist nicht nötig
~~~
Beachten Sie, dass der `$params`-Parameter nicht mehr benötigt wird, wenn Sie
[CDbCriteria] als Abfragebedingung nutzen, da diese Parameter, wie oben gezeigt,
in [CDbCriteria] angegeben werden können.
Alternativ zur Verwendung von [CDbCriteria] kann auch ein Array an die
`find`-Methode übergeben werden. Die Schlüssel und Werte des Array entsprechen
den Namen und Werten der Eigenschaften von CDbCriteria. Das obige Beispiel
kann daher wie folgt umformuliert werden:
~~~
[php]
$post=Post::model()->find(array(
'select'=>'title',
'condition'=>'postID=:postID',
'params'=>array(':postID'=>10),
));
~~~
> Info|Info: Wenn bei einer Abfrage nach bestimmten Werten für Tabellenspalten gesucht
werden soll, können wir dazu [findByAttributes()|CActiveRecord::findByAttributes]
verwenden. Dabei ist der Parameter `$attributes` ein Array von Werten, die durch
Spaltennamen indiziert werden. In einigen Frameworks wird diese Aufgabe durch
Aufruf von Methoden wie `findByNameAndTitle` gelöst. Obwohl diese
Herangehensweise zunächst reizvoll aussieht, sorgt sie oft für Verwirrung,
Konflikte und Probleme im Zusammenhang mit der Groß-/Kleinschreibung von
Spaltennamen.
Wenn mehrere Datenzeilen die Abfragebedingung erfüllen, können wir diese
gesammelt mit den folgenen `findAll`-Methoden beziehen, von denen jede ihr
Pendant bei den schon beschriebnen `find`-Methoden hat.
~~~
[php]
// Finde alle Zeilen, die die angegebene Bedingung erfüllen
$posts=Post::model()->findAll($condition,$params);
// Finde alle Zeilen mit dem angegebenen Primärschlüsseln
$posts=Post::model()->findAllByPk($postIDs,$condition,$params);
// Finde alle Zeilen mit den angegeben Attributwerten
$posts=Post::model()->findAllByAttributes($attributes,$condition,$params);
// Finde alle Zeilen unter Anwendung der angegeben SQL-Anweisung
$posts=Post::model()->findAllBySql($sql,$params);
~~~
Falls die Abfragebedingung von keiner Zeile erfüllt wird, liefert `findAll`
ein leeres Array zurück. Dies unterscheidet sich von `find`, das null
zurückliefert, falls nichts gefunden wurde.
Zusätzlich zu den oben beschriebenen `find`- und `findAll`-Methoden, werden
für bequemen Zugriff noch die folgenden Methoden bereitgestellt:
~~~
[php]
// Liefert die Anzahl der Zeilen, die die angegebene Bedingung erfüllen
$n=Post::model()->count($condition,$params);
// Liefert die Anzahl der Zeilen durch Anwenden der angegeben SQL-Anweisung
$n=Post::model()->countBySql($sql,$params);
// Prüft, ob mindestens eine Zeile die angegebene Bedingung erfüllt
$exists=Post::model()->exists($condition,$params);
~~~
Aktualisieren von Datensätzen
-----------------------------
Nachdem eine AR-Instanz mit den Werten der Tabellenspalten befüllt wurde,
können wir diese verändern und in die Datenbanktabelle zurück speichern.
~~~
[php]
$post=Post::model()->findByPk(10);
$post->title='Titel eines neuen Beitrags';
$post->save(); // Änderung in der Datenbank speichern
~~~
Wie wir sehen, verwenden wir für Einfüge- und Aktualisierungsoperationen die gleiche
[save()|CActiveRecord::save]-Methode. Wenn eine AR-Instanz durch den
`new`-Operator erzeugt wurde, fügt der Aufruf von [save()|CActiveRecord::save]
eine neue Zeile in die Datenbanktabelle ein. Falls die AR-Instanz das Ergebnis
eines `find`- oder `findAll`-Aufrufs war, aktualisiert der Aufruf von
[save()|CActiveRecord::save] die bestehende Zeile in der Tabelle. Wir können
[CActiveRecord::isNewRecord] abfragen, um herauszufinden, ob wir eine neue
AR-Instanz vorliegen haben oder nicht.
Es ist auch möglich, eine oder mehrere Zeilen in der Datenbanktabelle zu aktualisieren
ohne sie vorher zu laden. AR bietet dazu auf Klassenebene folgende nützliche
Methoden:
~~~
[php]
// Aktualisiere die Zeilen, die die angegebene Bedingung erfüllen
Post::model()->updateAll($attribute,$bedingung,$params);
// Aktualisiere die Zeilen, die die angegebene Bedingung und den(die) Primärschlüssel erfüllen
Post::model()->updateByPk($pk,$attribute,$bedingung,$params);
// Aktualisiere Zähler-Felder in den Zeilen, die die angegebene Bedingung erfüllen
Post::model()->updateCounters($zaehler,$bedingung,$params);
~~~
Hier ist `$attribute` ein Array von Feldwerten, die durch Feldnamen indiziert sind.
`$zaehler` ist ein Array inkrementeller Werte, die durch Feldnamen indiziert
sind. `$bedingung` und `$params` wurden bereits in vorangegangenen Abschnitten
beschrieben.
Löschen von Datensätzen
-----------------------
Wir können eine Datenzeile auch löschen, wenn eine AR-Instanz mit dieser Zeile
befüllt wurde.
~~~
[php]
$post=Post::model()->findByPk(10); // Unter der Annahme, es gibt einen `Post` mit ID 10
$post->delete(); // Lösche diese Zeile in der Datenbanktabelle
~~~
Beachten Sie, dass die AR-Instanz nach dem Löschen unverändert bleibt, obwohl
die entsprechende Zeile in der Datenbank bereits gelöscht wurde.
Die folgenden Methoden auf Klassenebene stehen bereit, um Zeilen zu löschen,
ohne sie vorher laden zu müssen:
~~~
[php]
// Lösche die Zeilen, die die angegebene Bedingung erfüllen
Post::model()->deleteAll($condition,$params);
// Lösche die Zeilen, die die angegebene Bedingung und den(die) Primärschlüssel erfüllen
Post::model()->deleteByPk($pk,$condition,$params);
~~~
Gültigkeitsprüfung der Daten
----------------------------
Wenn eine Datenzeile eingefügt oder aktualisiert wird, müssen wir oft überprüfen,
ob die Werte der Felder bestimmten Regeln entsprechen. Dies ist besonders wichtig,
wenn die Werte von Endbenutzern stammen. Generell sollten wir Daten, die von
Clientseite stammen, niemals vertrauen.
AR führt die Gültigkeitsprüfung beim Aufruf von [save()|CActiveRecord::save]
automatisch durch. Die Prüfung wird anhand der Regeln durchgeführt,
die in der [rules()|CModel::rules]-Methode der AR-Klasse angegeben wurden.
Weitere Einzelheiten zur Definition von Prüfregeln finden Sie im Abschnitt
[Angeben der Regeln zur Gültigkeitsprüfung](/doc/guide/form.model#declaring-validation-rules).
Nachfolgend der typische Ablauf, der zum Speichern eines Datensatzes notwendig ist:
~~~
[php]
if($post->save())
{
// Die Daten sind gültig und wurden erfolgreich eingefügt/aktualisiert
}
else
{
// Die Daten sind ungültig. Rufen Sie getErrors() auf um die Fehlermeldungen abzufragen
}
~~~
Wenn die einzufügenden oder zu aktualisierenden Daten von einem Endbenutzer
per HTML-Formular abgeschickt wurden, müssen wir diese den entsprechenden AR-Eigenschaften
zuweisen. Wir können das wie folgt tun:
~~~
[php]
$post->title=$_POST['title'];
$post->content=$_POST['content'];
$post->save();
~~~
Bei vielen Feldern würden wir eine lange Liste solcher Zuweisungen benötigen.
Linderung verspricht hier der Einsatz der
[attributes|CActiveRecord::attributes]-Eigenschaft, wie unten gezeigt.
Weitere Details finden Sie im Abschnitt
[Absichern von Attributzuweisungen](/doc/guide/form.model#securing-attribute-assignments)
sowie im Kapitel [Erstellen der Action](/doc/guide/form.action).
~~~
[php]
// Angenommen, $_POST['Post'] ist ein Array von Werten,
// das durch Feldnamen indiziert wurde
$post->attributes=$_POST['Post'];
$post->save();
~~~
Vergleichen von Records
-----------------------
Wie Tabellenzeilen werden AR-Instanzen eindeutig durch Primärschlüsselwerte
identifiziert. Um zwei AR-Instanzen zu vergleichen, müssen wir daher lediglich
die Werte ihrer Primärschlüssel vergleichen, vorausgesetzt, sie gehören beide
zur selben AR-Klasse. Einfacher geht dies jedoch über einen Aufruf von
[CActiveRecord::equals()].
> Info|Info: Im Unterschied zu anderen Frameworks, unterstützen Yiis AR
zusammengesetzte Primärschlüssel. Ein zusammengesetzter Primärschlüssel besteht aus zwei
oder mehr Feldern. Entsprechend wird der Primärschlüsselwert in Yii als Array
dargestellt. Die Eigenschaft [primaryKey|CActiveRecord::primaryKey] liefert den Wert
des Primärschlüssels einer AR-Instanz.
Anpassung
---------
[CActiveRecord] stellt einige Platzhalter-Methoden bereit, die in abgeleiteten
Klassen zur Anpassung bestimmter Abläufe überschrieben werden können.
- [beforeValidate|CModel::beforeValidate] und
[afterValidate|CModel::afterValidate]: diese werden aufgerufen, bevor bzw. nachdem
die Gültigkeitsprüfung durchgeführt wird/wurde.
- [beforeSave|CActiveRecord::beforeSave] und
[afterSave|CActiveRecord::afterSave]: diese werden aufgerufen, bevor bzw. nachdem
die AR-Instanz gespeichert wird/wurde.
- [beforeDelete|CActiveRecord::beforeDelete] und
[afterDelete|CActiveRecord::afterDelete]: diese werden aufgerufen, bevor bzw. nachdem
eine AR-Instanz gelöscht wird/wurde.
- [afterConstruct|CActiveRecord::afterConstruct]: diese wird aufgerufen, nachdem
eine AR-Instanz mit dem `new` Operator erstellt wurde.
- [beforeFind|CActiveRecord::beforeFind]: diese wird aufgerufen, bevor eine
Abfrage (z.B. `find()`, `findAll()`) mit einem AR-Finder durchgeführt wird.
Diese Methode steht seit Version 1.0.9 zur Verfügung.
- [afterFind|CActiveRecord::afterFind]: diese wird aufgerufen, nachdem
eine AR-Instanz als Ergebnis einer Abfrage erstellt wurde.
Transaktionen mit AR
--------------------
Jede AR-Instanz hat eine Eigenschaft mit dem Namen
[dbConnection|CActiveRecord::dbConnection], welche eine Instanz von [CDbConnection]
darstellt. Bei Bedarf können wir deshalb beim Arbeiten mit AR das Transaktions-Feature
von Yii-DAO verwenden:
~~~
[php]
$model=Post::model();
$transaction=$model->dbConnection->beginTransaction();
try
{
// Finden und Speichern sind zwei Schritte, zwischen denen eine andere
// Anfrage vorkommen könnte. Wir verwenden daher eine Transaktion, um
// Konsistenz und Integrität zu gewährleisten.
$post=$model->findByPk(10);
$post->title='Titel eines neuen Beitrags';
$post->save();
$transaction->commit();
}
catch(Exception $e)
{
$transaction->rollBack();
}
~~~
Benannte Bereiche
-----------------
> Note|Hinweis: Benannte Bereiche werden seit Version 1.0.5 unterstützt.
> Die usprüngliche Idee für benannte Bereiche stammt von Ruby on Rails.
Ein *benannter Bereich* (engl.: named scope) stellt ein *benanntes*
Abfragekriterium dar, das mit anderen benannten Bereichen kombiniert
und auf eine ActiveRecord-Abfrage angewendet werden kann.
Benannte Bereiche werden in der Methode [CActiveRecord::scopes()]
hauptsächlich als Name-Kriterium-Paare definiert. Der folgende Code deklariert
die beiden benannten Bereiche `veroeffentlicht` und `kuerzlich` in der
Model-Klasse `Beitrag`:
~~~
[php]
class Beitrag extends CActiveRecord
{
......
public function scopes()
{
return array(
'veroeffentlicht'=>array(
'condition'=>'status=1',
),
'kuerzlich'=>array(
'order'=>'erstellZeit DESC',
'limit'=>5,
),
);
}
}
~~~
Jeder benannte Bereich wird als Array deklariert, das zum Initialisieren
einer [CDbCriteria]-Instanz verwendet werden kann. Der benannte Bereich
`kuerzlich` bestimmt zum Beispiel, dass die `order`-Eigenschaft auf
`erstellZeit DESC` und die `limit`-Eigenschaft auf 5 gesetzt werden soll. Dies
wird in ein Abfragekriterium übersetzt, das die letzten 5 Beiträge
zurückliefern sollte.
Benannte Bereiche werden meist als Modifikatoren beim Aufruf von `find`-Methoden
verwendet. Mehrere benannte Bereiche können miteinander verkettet werden
und resultieren so in einem immer weiter eingeschränkten Abfrageergebnis.
Um zum Beispiel die letzten veröffentlichten Beiträge zu finden, können wir
diesen Code verwenden:
~~~
[php]
$beitraege=Beitrag::model()->veroeffentlicht()->kuerzlich()->findAll();
~~~
Benannte Bereiche müssen grundsätzlich links vom Aufruf einer
`find`-Methode stehen. Jeder einzelne von ihnen stellt ein Abfragekriterium bereit, das
mit anderen Kriterien kombiniert wird, inklusive demjenigen, das als
Parameter and die `find`-Methode übergeben wurde. Letztendlich fügt man
einer Abfrage also eine Liste von Filtern hinzu.
Seit Version 1.0.6 können benannte Bereiche auch mit `update`- und
`delete`-Methoden verwendet werden. Der folgende Code würde zum Beispiel alle
kürzlich veröffentlichten Beiträge löschen:
~~~
[php]
Beitrag::model()->veroeffentlicht()->kuerzlich()->delete();
~~~
> Note|Hinweis: Benannte Bereiche können nur mit Methoden auf Klassenebene
verwendet werden. Das bedeutet, dass die Methode über `KlassenName::model()`
aufgerufen werden muss.
###Parametriesierte benannte Bereiche
Benannte Bereiche können parametrisiert werden. Wir könnten zum Beispiel
die Anzahl der Beiträge des benannten Bereichs `kuerzlich` anpassen wollen.
Statt den Bereich in der [CActiveRecord::scopes]-Methode anzugeben, müssen wir
dazu eine neue Methode mit dem Namen des Bereichs definieren:
~~~
[php]
public function kuerzlich($limit=5)
{
$this->getDbCriteria()->mergeWith(array(
'order'=>'createTime DESC',
'limit'=>$limit,
));
return $this;
}
~~~
Wir können dann den folgenden Befehl verwenden, um die letzten 3
veröffentlichten Beiträge zu erhalten:
~~~
[php]
$beitraege=Beitrag::model()->veroeffentlicht()->kuerzlich(3)->findAll();
~~~
Wenn wir den Parameter 3 hier nicht angeben, würden wir standardmäßig die letzten 5
veröffentlichten Beiträge erhalten.
###Standardmäßiger benannter Bereich
Ein benannter Bereich kann auch als Standardbereich für eine Modelklasse
festgelegt werden, so dass er bei allen Abfragen (inkl. relationalen)
verwendet wird. So könnte zum Beispiel eine mehrsprachige Website ihre
Inhalte immer nur in der Sprache des aktuellen Besuchers anzeigen wollen.
Da bei den Abfragen für diese Inhalten immer die selben Sprachkriterien
verwendet werden sollen, können wir einen benannten Bereich als Standard
festlegen, um dieses Problem zu lösen. Dazu überschreiben wir die Methode
[CActiveRecord::defaultScope] wie folgt:
~~~
[php]
class Content extends CActiveRecord
{
public function defaultScope()
{
return array(
'condition'=>"sprache='".Yii::app()->language."'",
);
}
}
~~~
Dadurch verwendet der folgene Aufruf automatisch das eben festgelegte
Abfragekriterium.
~~~
[php]
$contents=Content::model()->findAll();
~~~
Beachten Sie, dass der Standardbereich nur für
`SELECT`-Abfragen verwendet wird. Bei `INSERT`-, `UPDATE`- und
`DELETE`-Statements wird er ignoriert.
<div class="revision">$Id: database.ar.txt 1479 2009-10-23 13:24:20Z qiang.xue $</div>