ActiveRecord
============

Man kann so gut wie alle Datenbankaufgaben mit Yii-DAO lösen. In 90% der Fälle
führt man aber immer wieder die gleichen Anweisungen  zum Lesen, Schreiben,
Aktualisieren und Löschen von Datensätzen (CRUD) aus. Außerdem erschwert
es die Codewartung, wenn zu viele SQL-Anweisungen darin vorkommen.
Als Alternative bieten sich daher oft ActiveRecords an.

ActiveRecords (oder kurz AR) sind eine verbreitete Technik zur sog. objektrelationalen
Abbildung (engl.: object-relational mapping,ORM). Jede AR-Klasse steht für
eine Tabelle (oder einen View) in der Datenbank. Die Spalten der Tabelle
werden zu AR-Klasseneigenschaften. Eine einzelne Tabellenzeile wird zu einem
AR-Objekt, das auch gleich Methoden für die üblichen CRUD-Operationen
bereitstellt. So kann man durch AR objektorientiert mit DB-Daten arbeiten.
Um zum Beispiel eine neue Zeile in die Tabelle `tbl_post` einzufügen, schreibt
man:

~~~
[php]
$post=new Post;
$post->title='Beispielbeitrag';
$post->content='Inhalt des Beitrags';
$post->save();
~~~

In diesem Kapitel geht es darum, wie man AR-Instanzen anlegt und für
CRUD-Operationen verwendet. Im nächsten Kapitel zeigen wir dann, wie man mit
Relationen arbeitet. Sämtliche Beispiele basieren auf der
folgenden Datenbanktabelle. Beachten Sie dabei bitte, dass Sie SQL AUTOINCREMENT
durch AUTO_INCREMENT ersetzen müssen, falls Sie MySQL verwenden.

~~~
[sql]
CREATE TABLE tbl_post (
	id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
	title VARCHAR(128) NOT NULL,
	content TEXT NOT NULL,
	create_time INTEGER NOT NULL
);
~~~

> Note|Hinweis: AR ist nicht unbedingt für alle Datenbankaufgaben geeignet.
AR ist zweckmäßig, um Datenbanktabellen in PHP abzubilden sowie für
Abfragen, die kein komplexes SQL erfordern. In komplizierten Fällen sollte
man stattdessen direkt Yii-DAO verwenden.


Aufbau einer DB-Verbindung
--------------------------

Um auf die Datenbank zugreifen zu können, benötigt AR eine Datenbankverbindung.
Standardmäßig erwartet AR diese in der Applikationskomponente `db`. Hier ein
Beispiel, wie diese Komponente konfiguriert werden kann:

~~~
[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 benötigt einige Metadaten über Tabellen, um
zum Beispiel die Spalteninformationen zu ermitteln. Das Lesen und
Analysieren dieser Daten braucht Zeit. Falls sich das Schema Ihrer Datenbank
kaum mehr ändert, sollten Sie daher Schema-Caching aktivieren, 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)

Falls Sie statt `db` eine andere Verbindungskomponente verwenden möchten oder
Sie mit AR auf mehreren Datenbanken arbeiten, können Sie
[CActiveRecord::getDbConnection()] in Ihrer Klasse überschreiben.
Die [CActiveRecord]-Klasse ist die Basisklasse für alle AR-Klassen.

> Tip|Tipp: Es gibt zwei Methoden, um mit AR auf mehren Datenbanken zu arbeiten.
Falls die Datenbankschemata sich unterscheiden, können Sie mehrere
AR-Basisklassen anlegen, die jeweils [getDbConnection()|CActiveRecord::getDbConnection]
überschreiben. Ist das Schema überall gleich, ist es günstiger, die statische Variable
[CActiveRecord::db] dynamisch zu ändern.

Definieren von AR-Klassen
-------------------------

Um auf eine Datenbanktabelle zuzugreifen, muss zuerst eine neue AR-Klasse
von [CActiveRecord] abgeleitet werden. Jede Klasse repräsentiert
eine einzelne Datenbanktabelle, eine AR-Instanz eine Zeile in dieser Tabelle.
Das folgende Beispiel zeigt den Minimalcode einer AR-Klasse. In diesem Fall
für die Tabelle `tbl_post`.

~~~
[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, importiert
> man am besten gleich das ganze Modelverzeichnis. Liegen alle AR-Klassen
> (wie meist üblich) im Verzeichnis `protected/models`, wird dies wie folgt
> konfiguriert:
> ~~~
> [php]
> return array(
> 	'import'=>array(
> 		'application.models.*',
> 	),
> );
> ~~~

Standardmäßig hat die AR-Klasse den gleichen Namen wie die Datenbanktabelle,
Sie können aber auch einen abweichenden Tabellennamen verwenden, indem Sie
die [tableName()|CActiveRecord::tableName]-Methode überschreiben.
Die [model()|CActiveRecord::model]-Methode muss in jeder AR-Klasse definiert
werden, worauf wir in Kürze noch näher eingehen werden.

> Info: Um ein [Tabellenpräfix](/doc/guide/database.dao#using-table-prefix) zu
> verwenden, kann man die  [tableName()|CActiveRecord::tableName]-Methode auch
> wie folgt überschreiben:
> ~~~
> [php]
> public function tableName()
> {
>     return '{{post}}';
> }
> ~~~
> Statt eines vollständigen Tabellennamens, gibt man den Namen der
> Tabelle ohne Präfix, aber dafür in doppelten geschweiften Klammern zurück.

Über die Objekteigenschaften kann man auf die Daten einer Tabellenzeile
zugreifen. Um zum Beispiel den Wert für die Spalte `title` zu setzen,
schreibt man:

~~~
[php]
$post=new Post;
$post->title='Ein Beispielbeitrag';
~~~

Erstaunlicherweise kann man auf die `title`-Eigenschaft zugreifen, obwohl
diese in der `Post`-Klasse gar nicht deklariert wurde. Das liegt daran,
das AR die magische PHP-Methode `__get()` (bzw. `__set()`) verwendet, um
Tabellenspalten wie Objekteigenschaften verfügbar zu machen. Versucht man
jedoch, ein nicht existentes Feld anzusprechen, wird eine Exception ausgelöst.

> Info|Info: In diesem Handbuch werden alle Tabellen- und Spaltennamen
> kleingeschrieben, da verschiedene DBMS mit Groß-/Kleinschreibung
> unterschiedlich umgehen. PostgreSQL zum Beispiel ignoriert die Schreibweise
> standardmäßig. Falls ein Spaltenname Groß- und Kleinbuchstaben enthält, muss
> er dort in Anführungszeichen gesetzt werden. Durch konsequente
> Kleinschreibung umgeht man dieses Problem.

AR erwartet, dass für alle Tabellen korrekte Primärschlüssel in der Datenbank
definiert wurden. Ist das für eine Tabelle nicht der Fall, muss die Methode
`primaryKey()` den Primärschlüssel wie folgt zurückliefern:

~~~
[php]
public function primaryKey()
{
	return 'id';
	// Für zusammengesetzte Primärschlüssel kann ein Array wie folgt
	// zurückgegeben werden:
	// return array('pk1', 'pk2');
}
~~~



Einfügen von Datensätzen
------------------------

Um eine neue Zeile in eine Datenbanktabelle einzufügen, erzeugt man eine
Objektinstanz der zugehörigen AR-Klasse, setzt die entsprechenden
Eigenschaften und ruft die [save()|CActiveRecord::save]-Methode auf.

~~~
[php]
$post=new Post;
$post->title='Beispielbeitrag';
$post->content='Inhalt des Beispielbeitrags';
$post->create_time=time();
$post->save();
~~~

Ist in der Datenbank AUTOINCREMENT für den Primärschlüssel aktiviert, enthält
die neue AR-Instanz nach dem Speichern automatisch den vergebenen
Schlüssel. Im Beispiel enhält also das `id`-Attribut den neuen Schlüssel, ohne
dass dieses explizit gesetzt wurde.

Wurden im Tabellenschema der DB statische Vorgabewerte für bestimmte Spalten
definiert (z.B. ein String oder eine Zahl), werden diese nach dem Speichern
ebenfalls automatisch bei den entsprechenden Eigenschaften gesetzt. Möchte
man diese Vorgabewerte ändern, kann man das in der AR-Klasse wie folgt
erreichen:

~~~
[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
~~~

Man kann 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, schreibt man:

~~~
[php]
$post=new Post;
$post->create_time=new CDbExpression('NOW()');
// $post->create_time='NOW()'; funktioniert nicht,
// da 'NOW()' wie ein String behandelt werden würde.
$post->save();
~~~

> Tip|Tipp: Obwohl man durch AR keine umständlichen SQL-Ausdrücke mehr
> schreiben muss, kann es zur Fehlersuche nützlich sein, die
> im Hintergrund verwendeten SQL-Befehle zu untersuchen. Das geht mit
> Hilfe des [Log-Features](/doc/guide/topics.logging) von Yii. Konfiguriert
> man zum Beispiel eine [CWebLogRoute], werden die ausgeführten SQL-Befehle
> am Ende der Seite angezeigt.  Setzt man [CDbConnection::enableParamLogging]
> auf `true`, werden dort auch die gebundenen Parameterwerte angezeigt.



Lesen von Datensätzen
---------------------

Um Daten aus der Datenbanktabelle zu lesen verwendet man eine der folgenden
`find`-Methoden:

~~~
[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 wird die `find`-Methode auf `Post::model()` aufgerufen. Wie Sie sich
erinnern, ist die statische `model()`-Methode bei jeder AR-Klasse nötig.
Sie gibt eine AR-Instanz zurück, mit der man auf Methoden der Klassenebene
zugreifen kann. Das ist ganz ähnlich zu statischen Klassenmethoden, allerdings
hier in einem Objektkontext.

Findet die `find`-Methode eine Zeile zu den gegebenen Abfragebedingungen,
liefert sie ein `Post`-Objekt mit den entsprechenden Daten zurück. Die
geladenen Werte können dann wie normale Objekteigenschaften z.B. mit
`$post->title` ausgelesen werden.

Wurde keine entsprechende Zeile gefunden, liefert `find` den Wert null zurück.

Die Abfragebedingungen werden über die Argumente `$condition` und `$params` an
`find` übergeben. `$condition` kann ein String sein, der die
`WHERE`-Klausel in einer SQL Abfrage darstellt, `$params` ein Array mit
Parametern, falls `$condition` Platzhalter enthält. Hier ein 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. Bei PostgreSQL, muss `$condition` zum
Beispiel `"postID"=:postID` lauten, da PostgreSQL
standardmäßig die Groß-/Kleinschreibung von Spaltennamen nicht berücksichtigt.

`$condition` kann auch viel komplexere Abfragebedingungen enthalten, indem man
statt eines Strings eine Instanz von [CDbCriteria] übergibt. Damit können dann
weitere Kriterien angegeben werden:

~~~
[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 das `$params`-Argument nicht mehr benötigt wird,
da die Parameter bereits in [CDbCriteria] angegeben werden können.

Alternativ zu einem [CDbCriteria]-Objekt, kann auch ein Array an die
`find`-Methode übergeben werden. Die Schlüssel und Werte des Array entsprechen
dann 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: Soll nach bestimmten Spaltenwerten in einer Tabelle gesucht
> werden, kann man dazu [findByAttributes()|CActiveRecord::findByAttributes]
> verwenden. `$attributes` ist ein Array mit Spaltennamen als Schlüsseln und
> den gesuchten Werten als Arraywerten.  In einigen Frameworks wird dies über
> Methoden wie z.B. `findByNameAndTitle` gelöst. Obwohl diese Herangehensweise
> ihre Reize besitzt, sorgt sie oft für Verwirrung und Konflikte bzw.
> Problemen im Zusammenhang mit der Groß-/Kleinschreibung von Spaltennamen.

Erfüllen mehrere Datenzeilen die Abfragebedingung, können diese alle auf
einmal mit den folgenden `findAll`-Methoden bezogen werden. Jede von Ihnen hat
ihr Pendant bei den eben beschriebenen `find`-Methoden:

~~~
[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);
~~~

Im Unterschied zu `find` liefern die `findAll`-Methoden ein leeres Array
zurück, falls keine entsprechende Zeile gefunden wurde.

AR bietet noch weitere Methoden, die viele gängige Aufgaben vereinfachen:

~~~
[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
-----------------------------

Wurde eine AR-Instanz mit den Werten einer Tabellenzeile befüllt, können
diese verändert und in die Datenbank zurückgespeichert werden.

~~~
[php]
$post=Post::model()->findByPk(10);
$post->title='Geänderter Titel eines Beitrags';
$post->save(); // Änderung in der Datenbank speichern
~~~

Wie Sie sehen, wird die [save()|CActiveRecord::save]-Methode auch zum
Aktualisieren von Einträgen verwendet. Wurde eine Instanz also mit `new`
erzeugt, fügt [save()|CActiveRecord::save] einen neuen Datensatz ein. Stammt
das Objekt von einer Abfrage, aktualisiert [save()|CActiveRecord::save] die
entsprechende Tabellenzeile. Mit [CActiveRecord::isNewRecord] kann man prüfen,
ob es sich um ein neues Objekt handelt oder nicht.

Man kann einzelne oder mehrere Zeilen in einer Tabelle auch aktualisieren,
ohne sie vorher zu laden.AR bietet dazu die folgenden nützlichen Methoden
auf Klassenebene an:

~~~
[php]
// Aktualisiere die Zeilen, die die angegebene Bedingung erfüllen
Post::model()->updateAll($attribute,$bedingung,$params);
// Aktualisiere die Zeilen mit dem/den angegebenen Primärschlüssel(n),
// die die angegebene Bedingung erfüllen
Post::model()->updateByPk($pk,$attribute,$bedingung,$params);
// Aktualisiere Zählerspalten in den Zeilen, die die angegebene Bedingung erfüllen
Post::model()->updateCounters($zaehler,$bedingung,$params);
~~~

Hier ist `$attribute` ein Array von Werten, die durch Feldnamen indiziert sind.
`$zaehler` ist ein Array ansteigender Werte, die durch Feldnamen indiziert
sind. `$bedingung` und `$params` wurden bereits in den vorangegangenen Abschnitten
beschrieben.

Löschen von Datensätzen
-----------------------

Man kann eine Datenzeile auch löschen, wenn die AR-Instanz dieser Zeile
entspricht:

~~~
[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, auch
wenn die entsprechende Zeile in der Datenbank bereits gelöscht wurde.

Mit diesen Methoden (wieder auf Klassenebene) kann man Zeilen auch 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 mit den angegebenen Primärschlüsseln,
// die die angegebene Bedingung erfüllen
Post::model()->deleteByPk($pk,$condition,$params);
~~~

Validieren der Daten
--------------------

Vor dem Einfügen oder Aktualisieren eines Datensatzes, muss oftmals geprüft
werden, ob die Werte auch bestimmten Regeln entsprechen. Diesen Vorgang nennt
man auch `Validierung`. Sie ist insbesondere wichtig, wenn die zu speichernden
Werte von Endanwendern stammen. Sämtlichen Daten von der Clientseite sollte man
grundsätzlich nie vertrauen.

AR führt die Validierung beim Aufruf von [save()|CActiveRecord::save]
automatisch durch. Die Prüfung erfolgt anhand der Regeln, die in der
[rules()|CModel::rules]-Methode der AR-Klasse angegeben wurden.
Weitere Einzelheiten zur Definition von Prüfregeln finden Sie im Abschnitt
[Angeben von Validierungsregeln](/doc/guide/form.model#declaring-validation-rules).
Beim Speichern kommt es in der Regel zu diesem typischen Ablauf:

~~~
[php]
if($post->save())
{
	// Die Daten sind gültig und wurden erfolgreich eingefügt/aktualisiert
}
else
{
	// Die Daten sind ungültig. Fehlermeldungen mit getErrors() abfragen
}
~~~

Stammen die einzufügenden oder zu aktualisierenden Daten von einem
HTML-Formular, müssen diese den entsprechenden AR-Eigenschaften zugewiesen
werden:

~~~
[php]
$post->title=$_POST['title'];
$post->content=$_POST['content'];
$post->save();
~~~

Hat eine Tabelle viele Spalten, müsste man also umständlich alle Felder
einzeln zuweisen. Über die [attributes|CActiveRecord::attributes]-Eigenschaft
kann man das vermeiden, wie im folgenden Beispiel zu sehen. Weitere Details
hierzu finden Sie im Abschnitt
[Sichere 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
-----------------------

Auch AR-Instanzen werden wie die entsprechenden Tabellenzeilen eindeutig durch
ihre Primärschlüsselwerte identifiziert. Man kann zwei Instanzen also einfach
über deren Primärschlüssel vergleichen, vorausgesetzt, sie gehören zur selben
Klasse. Einfacher geht dies jedoch mit [CActiveRecord::equals()].

> Info|Info: Im Unterschied zu anderen Frameworks unterstützen Yiis AR auch
> zusammengesetzte Primärschlüssel. Sie bestehen aus zwei oder mehr Feldern
> und werden daher als Array dargestellt. Die Eigenschaft
> [primaryKey|CActiveRecord::primaryKey] liefert den Wert des Primärschlüssels einer AR-Instanz.


Anpassung
---------

Zur Anpassung an bestimmte Abläufe, sind in [CActiveRecord] einige Methoden
als Platzhalter implementiert, die man in eigenen AR-Klassen einfach überschreiben kann.

   - [beforeValidate|CModel::beforeValidate] und [afterValidate|CModel::afterValidate]:
werden vor bzw. nach der Validierung aufgerufen

   - [beforeSave|CActiveRecord::beforeSave] und [afterSave|CActiveRecord::afterSave]:
werden vor bzw. nach dem Speichern aufgerufen

   - [beforeDelete|CActiveRecord::beforeDelete] und [afterDelete|CActiveRecord::afterDelete]:
werden vor bzw. nach dem Löschen einer AR-Instanz aufgerufen

   - [afterConstruct|CActiveRecord::afterConstruct]: wird nach dem Erzeugen
einer neuen Instanz aufgerufen

   - [beforeFind|CActiveRecord::beforeFind]: werden vor einer Suchabfrage
(z.B. `find()`, `findAll()`) ausgeführt.

   - [afterFind|CActiveRecord::afterFind]: wird aufgerufen, nachdem eine
Instanz aus einem Suchergebnis erzeugt wurde.


Transaktionen mit AR
--------------------

Über die [dbConnection|CActiveRecord::dbConnection]-Eigenschaft kann bei jeder
AR-Instanz auf die zugrundeliegende [CDbConnection] zugegriffen werden. Bei
Bedarf kann man so 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();
}
~~~


Scopes
------

> Note|Hinweis: Die Idee für Scopes stammt ursprünglich von Ruby on Rails.

Ein *Scope* (engl.: named scope, sinngem.: benannter Bereich) stellt ein
vordefiniertes Abfragekriterium dar, das man bei einem AR unter einem bestimmten Namen
abrufen und sogar mit anderen Scopes kombinieren kann.

Meist werden Scopes als Array von Name-Kriterium-Paaren in der Methode
[CActiveRecord::scopes()] definiert. Der folgende Code deklariert
zum Beispiel die beiden Scopes `veroeffentlicht` und `kuerzlich` in der
Modelklasse `Beitrag`:

~~~
[php]
class Beitrag extends CActiveRecord
{
	......
	public function scopes()
	{
		return array(
			'veroeffentlicht'=>array(
				'condition'=>'status=1',
			),
			'kuerzlich'=>array(
				'order'=>'erstellZeit DESC',
				'limit'=>5,
			),
		);
	}
}
~~~

Jeder Scope besteht aus einem Array, dessen Werte zum Initialisieren
einer [CDbCriteria]-Instanz verwendet werden können. Der Scope
`kuerzlich` bestimmt zum Beispiel, dass die `order`-Eigenschaft auf
`erstell_zeit DESC` und die `limit`-Eigenschaft auf 5 gesetzt werden soll. Dies
wird also in ein Abfragekriterium übersetzt, das die letzten 5 Beiträge
zurückliefern sollte.

Scopes werden meist als Modifikatoren beim Aufruf von `find`-Methoden
verwendet. Mehrere Scopes können miteinander verkettet werden
und resultieren so in einem immer weiter eingeschränkten Abfrageergebnis.
Mit dem folgenden Code kann man so zum Beispiel die letzten 5 veröffentlichten
Beiträge abfragen:

~~~
[php]
$beitraege=Beitrag::model()->veroeffentlicht()->kuerzlich()->findAll();
~~~

Scopes müssen grundsätzlich links vom Aufruf einer
`find`-Methode stehen. Jeder einzelne von ihnen liefert ein Abfragekriterium, das
mit weiteren 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.

> Note|Hinweis: Scopes können nur mit Methoden auf Klassenebene
verwendet werden. Das bedeutet, dass die Methoden über `KlassenName::model()`
aufgerufen werden muss.

###Parametrisierte Scopes

Scopes können auch parametrisiert werden. Man könnte zum Beispiel
die Anzahl der Beiträge des Scopes `kuerzlich` anpassen.
Statt den Scope in der [CActiveRecord::scopes]-Methode anzugeben, wird
dazu eine eigene Methode mit dem Namen des Scopes verwendet:

~~~
[php]
public function kuerzlich($limit=5)
{
	$this->getDbCriteria()->mergeWith(array(
		'order'=>'create_time DESC',
		'limit'=>$limit,
	));
	return $this;
}
~~~

Die letzten 3 veröffentlichten Beiträge kann man dann so abfragen:

~~~
[php]
$beitraege=Beitrag::model()->veroeffentlicht()->kuerzlich(3)->findAll();
~~~

Wird der Parameter 3 weggelassen, werden standardmäßig die letzten 5 Beiträge
angezeigt.

###Standardscope

Ein Scope kann auch als Standardscope 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 dazu bei den allen Abfragen immer die selben Sprachkriterien verwendet werden
müssen, kann man diese Aufgabe mit einem Standardscope lösen.
Dazu wird die Methode [CActiveRecord::defaultScope] wie folgt überschrieben:

~~~
[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();
~~~

> Note|Hinweis: Der Standardscope wird nur für `SELECT`-Abfragen verwendet. Bei `INSERT`-, `UPDATE`- und `DELETE`-Statements wird er ignoriert.
> Innerhalb einer Scopedeklaration können außerdem keine DB-Abfragen auf die
gleiche AR-Klasse durchgeführt werden.

<div class="revision">$Id: database.ar.txt 3318 2011-06-24 21:40:34Z qiang.xue $</div>
