mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-05 07:44:05 +01:00
523 lines
22 KiB
Plaintext
523 lines
22 KiB
Plaintext
Relationale ActiveRecords
|
|
=========================
|
|
|
|
Wir haben schon gesehen, wie wir ActiveRecord (AR) nutzen können, um Daten
|
|
einer einzelnen Tabelle auszulesen. In diesem Abschnitt beschreiben wir,
|
|
wie wir mit AR mehrere relationale Datentabellen zusammenführen,
|
|
um verbundene Datensätze zurückzuerhalten.
|
|
|
|
Um mit relationalen AR zu arbeiten, müssen zwischen den zu
|
|
verbindenden Tabellen eindeutige Fremdschlüsselbeziehungen definiert worden sein.
|
|
AR stützt sich auf die Metadaten dieser Beziehungen, um zu ermitteln,
|
|
wie die Tabellen verbunden werden sollen.
|
|
|
|
> Note|Hinweis: Ab Version 1.0.1 können Sie relationale AR auch verwenden,
|
|
>wenn Sie keine Fremdschlüssel-Constraints in ihrer Datenbank definiert haben.
|
|
|
|
Der Einfachheit halber, verwenden wir zur Veranschaulichung der Beispiel in
|
|
diesem Abschnitt das folgende ER-Diagramm (engl.: entity relationship,
|
|
Gegenstands-Beziehungs-Modell).
|
|
|
|

|
|
|
|
> Info|Info: DBMS unterscheiden sich in ihrer jeweiligen Unterstützung von
|
|
> Fremdschlüssel-Constraints.
|
|
>
|
|
> SQLite unterstützt keine Fremschlüssel-Constraints, aber Sie können beim Erstellen
|
|
> von Tabellen trotzdem Constraints festlegen. AR kann sich diese Angaben zunutze
|
|
> machen, um relationale Abfragen in richtiger Weise zu unterstützen.
|
|
>
|
|
> MySQL unterstützt Fremdschlüssel-Constraints mit der InnoDB-Engine, aber nicht mit
|
|
> MyISAM. Es wird deshalb empfohlen, dass Sie InnoDB für MySQL-Datenbanken benutzen.
|
|
> Falls Sie MyISAM verwenden, können Sie relationale Abfragen mit AR mit folgendem Trick
|
|
> durchführen:
|
|
> ~~~
|
|
> [sql]
|
|
> CREATE TABLE Foo
|
|
> (
|
|
> id INTEGER NOT NULL PRIMARY KEY
|
|
> );
|
|
> CREATE TABLE bar
|
|
> (
|
|
> id INTEGER NOT NULL PRIMARY KEY,
|
|
> fooID INTEGER
|
|
> COMMENT 'CONSTRAINT FOREIGN KEY (fooID) REFERENCES Foo(id)'
|
|
> );
|
|
> ~~~
|
|
> Hier verwenden wir das Schlüsselwort `COMMENT`, um die Fremdschlüssel-Constraints
|
|
> zu beschreiben. AR kann diese Informationen auslesen, um die Beziehung zu erkennen.
|
|
|
|
Festlegen der Beziehungen
|
|
-------------------------
|
|
|
|
Bevor wir relationale Abfragen mit AR durchführen können, müssen wir AR bekannt geben,
|
|
wie eine AR-Klasse mit einer anderen in Beziehung steht.
|
|
|
|
Die Beziehung zwischen zwei AR-Klassen steht in direktem Zusammenhang mit der
|
|
Beziehung zwischen den Datenbanktabellen, die die AR-Klassen repräsentieren.
|
|
Aus Sicht der Datenbank gibt es drei Beziehungstypen zwischen zwei
|
|
Tabellen A und B: eins-zu-viele (engl.: one-to-many, 1:n, z.B. zwischen `User` und `Post`),
|
|
eins-zu-eins (engl.: one-to-one, 1:1, z.B. zwischen `User` und `Profile`) und
|
|
viele-zu-viele (engl.: many-to-many, n:m, z.B. zwischen `Category` und `Post`):
|
|
|
|
- `BELONGS_TO` (gehört zu): Wenn die Beziehung zwischen den Tabellen A und B
|
|
eins-zu-viele ist, dann gehört B zu A (z.B. `Post` gehört zu `User`).
|
|
|
|
- `HAS_MANY` (hat viele): Wenn die Beziehung zwischen der Tabelle A und B
|
|
eins-zu-viele ist, dann hat A viele B (z.B. `User` hat viele `Post`).
|
|
|
|
- `HAS_ONE` (hat ein): Dies ist ein Spezialfall von `HAS_MANY`, wobei A höchstens ein
|
|
B hat (z.B. `User` hat höchstens ein `Profile`).
|
|
|
|
- `MANY_MANY` (viele viele): Dies entspricht der viele-zu-viele-Beziehung (n:m-Beziehung) bei
|
|
Datenbanken. Eine Verbindungstabelle wird benötigt, um die viele-zu-viele
|
|
Beziehung auf eins-zu-viele-Beziehungen herunterzubrechen, da die meisten DBMS
|
|
viele-zu-viele-Beziehungen nicht direkt unterstützen. In unserem Schema dient
|
|
`PostCategory` diesem Zweck. In AR-Terminologie können wir `MANY_MANY` als
|
|
eine Kombination von `BELONGS_TO` und `HAS_MANY` erklären. Beispielsweise gehört
|
|
`Post` zu vielen `Category` und `Category` hat viele `Post`.
|
|
|
|
Das Festlegen der Beziehungen geschieht in AR durch Überschreiben der
|
|
[relations()|CActiveRecord::relations]-Methode von [CActiveRecord]. Die Methode
|
|
gibt ein Array der Beziehungsstruktur zurück. Jedes Arrayelement repräsentiert
|
|
eine einzelne Beziehung im folgenden Format:
|
|
|
|
~~~
|
|
[php]
|
|
'VarName'=>array('RelationsTyp', 'KlassenName', 'FremdSchlüssel', ...Zusätzliche Optionen)
|
|
~~~
|
|
|
|
wobei `VarName` der Name der Beziehung ist. `RelationsTyp` spezifiziert den Typ der Beziehung
|
|
und kann eine dieser vier Konstanten sein: `self::BELONGS_TO`, `self::HAS_ONE`,
|
|
`self::HAS_MANY` und `self::MANY_MANY`. `KlassenName` ist der Name der AR-Klasse, die zu
|
|
dieser in Beziehung steht. Und `FremdSchlüssel` gibt den/die an der Beziehung beteiligten
|
|
Fremdschlüssel an. Am Ende können zusätzliche Optionen für jede Beziehung angegeben werden
|
|
(was später beschrieben wird).
|
|
|
|
Der folgende Code zeigt, wie wir die Beziehung für die `User`- und `Post`-Klasse angeben.
|
|
|
|
~~~
|
|
[php]
|
|
class Post extends CActiveRecord
|
|
{
|
|
public function relations()
|
|
{
|
|
return array(
|
|
'author'=>array(self::BELONGS_TO, 'User', 'authorID'),
|
|
'categories'=>array(self::MANY_MANY, 'Category', 'PostCategory(postID, categoryID)'),
|
|
);
|
|
}
|
|
}
|
|
|
|
class User extends CActiveRecord
|
|
{
|
|
public function relations()
|
|
{
|
|
return array(
|
|
'posts'=>array(self::HAS_MANY, 'Post', 'authorID'),
|
|
'profile'=>array(self::HAS_ONE, 'Profile', 'ownerID'),
|
|
);
|
|
}
|
|
}
|
|
~~~
|
|
|
|
> Info|Info: Ein Fremdschlüssel kann auch ein kombinierter Schlüssel sein,
|
|
also aus zwei oder mehr Attributen bestehen. In diesem Fall sollten wir die Namen der
|
|
Fremdschlüssel verketten und durch ein Komma oder Leerzeichen trennen. Beim
|
|
`MANY_MANY` Beziehungstyp muss der Name der Verbindungstabelle beim
|
|
`FremdSchlüssel` ebenfalls angegeben werden. Zum Beispiel ist der Fremdschlüssel für die
|
|
`categories`-Beziehung in `Post` mit `PostCategory(postID, categoryID)` angegeben.
|
|
|
|
Das Definieren von Beziehungen in einer AR-Klasse fügt dieser implizit eine Eigenschaft
|
|
für jede Beziehung hinzu. Nachdem eine relationale Abfrage ausgeführt wurde, ist die
|
|
entsprechende Eigenschaft mit der/den verbundenen AR Instanz(en) befüllt.
|
|
Steht `$author` bespielsweise für eine AR-Instanz `User`, so können wir mit
|
|
`$author->posts` auf die verbundenen `Post`-Instanzen zugreifen.
|
|
|
|
Ausführen von relationalen Abfragen
|
|
-----------------------------------
|
|
|
|
Die einfachste Art, relationale Abfragen auszuführen, ist, eine Verbundeigenschaft
|
|
einer AR-Instanz zu lesen. Falls auf die Eigenschaft noch nicht zugegriffen wurde,
|
|
wird eine relationale Abfrage ausgeführt, die die beiden betroffenen Tabellen kombiniert
|
|
und nach dem Primärschlüssel der aktuellen AR-Instanz filtert.
|
|
Das Abfrageergebnis wird in der Eigenschaft als Instanz(en) der verbundenen
|
|
AR-Klasse gespeichert. Dies ist unter dem Namen *lazy loading*-Methode
|
|
(träges Nachladen) bekannt, d.h. die relationale Abfrage wird erst beim ersten Zugriff
|
|
auf die Verbundobjekte durchgeführt. Das folgende Beispiel zeigt, wie man dieses Konzept
|
|
einsetzen kann:
|
|
|
|
~~~
|
|
[php]
|
|
// Frage den Beitrag mit der ID 10 ab
|
|
$post=Post::model()->findByPk(10);
|
|
// Frage den Autor des Beitrags ab: Hier wird eine relationale Abfrage durchgeführt
|
|
$author=$post->author;
|
|
~~~
|
|
|
|
> Info|Info: Wenn es keine verbundene Instanz für die Beziehung gibt, kann die
|
|
entsprechende Eigenschaft null oder ein leeres Array sein. Für die `BELONGS_TO`
|
|
und `HAS_ONE` Beziehungen ist das Ergebnis null, für `HAS_MANY` und `MANY_MANY`
|
|
ein leerer Array.
|
|
|
|
Die `lazy loading`-Methode ist sehr bequem einzusetzen, aber in einigen Szenarien
|
|
nicht sehr effizient. Wenn wir beispielsweise mit der `lazy loading`-Methode auf
|
|
die `author`-Information von `N` Posts zugreifen wollen, müssen `N` relationale
|
|
Abfragen durchgeführt werden. Unter diesen Umständen sollten wir auf die so genannte
|
|
*eager loading*-Methode (begieriges Laden) zurückgreifen.
|
|
|
|
Das `eager loading`-Konzept fragt die verbundenen AR Instanzen zusammen mit der
|
|
AR Hauptinstanz ab. Das wird in AR mit der [with()|CActiveRecord::with]-Methode zusammen
|
|
mit der [find|CActiveRecord::find]- oder [findAll|CActiveRecord::findAll]-Methode
|
|
durchgeführt. Zum Beispiel:
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->with('author')->findAll();
|
|
~~~
|
|
|
|
Der obige Code liefert ein Array von `Post`-Instanzen. Anders als beim
|
|
`lazy loading`-Ansatz ist die `author`-Eigenschaft in jeder `Post`-Instanz
|
|
schon mit der verbundenen `User`-Instanz befüllt, bevor wir auf die Eigenschaft zugreifen.
|
|
Anstatt für jeden Post eine relationale Abfrage durchzuführen, liefert der
|
|
`eager loading`-Ansatz mit einer einzigen JOIN-Abfrage alle Beiträge zusammen
|
|
mit ihren Autoren.
|
|
|
|
Wir können mehrere Namen von Beziehungen in der [with()|CActiveRecord::with]-Methode
|
|
angeben und mit der `eager loading`-Methode alle in einem Zug zurückerhalten.
|
|
Beispielsweise liefert der folgende Code Beiträge zusammen mit ihren Autoren und
|
|
Kategorien zurück:
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->with('author','categories')->findAll();
|
|
~~~
|
|
|
|
Wir können auch verschachteltes `eager loading` ausführen. Anstatt einer Liste
|
|
von Beziehungsnamen, können wir der [with()|CActiveRecord::with]-Methode
|
|
eine hierarchische Darstellung wie folgt mitgeben:
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->with(
|
|
'author.profile',
|
|
'author.posts',
|
|
'categories')->findAll();
|
|
~~~
|
|
|
|
Das obige Beispiel liefert alle Beiträge zusammen mit ihrem Autor und den
|
|
Kategorien zurück. Zusätzlich wird auch das Profil sowie alle Beiträge des
|
|
jeweiligen Autors zurückgegeben.
|
|
|
|
> Note|Hinweis: Die Verwendung der [with()|CActiveRecord::with]-Methode hat sich ab
|
|
> Version 1.0.2 geändert. Bitte lesen Sie die API-Dokumentation sorgfältig durch.
|
|
|
|
Die AR-Implementierung von Yii ist sehr effektiv. Beim `eager loading` einer
|
|
Hierarchie von `N` `HAS_MANY`- oder `MANY_MANY`-Beziehungen sind `N+1`
|
|
SQL Abfragen erforderlich, um das Ergebnis zu erhalten. Das bedeutet, dass
|
|
im letzten Beispiel 3 SQL Abragen für die `posts`- und `categories`-Eigenschaften
|
|
ausgeführt werden müssen. Andere Frameworks verfolgen eine fundamentalere
|
|
Herangehensweise, indem sie nur eine SQL Anweisung benutzen. Auf den ersten Blick
|
|
ist dieses fundamentalere Verfahren effizienter, da weniger Anfragen vom DBMS
|
|
analysiert und ausgeführt werden müssen. In Wahrheit ist es aber aus zwei Gründen
|
|
tatsächlich unpraktisch. Erstens enthält das Ergebnis viele, sich wiederholende
|
|
Datenspalten, für deren Übermittlung und Bearbeitung zusätzliche Zeit benötigt wird.
|
|
Zweitens wächst mit der Anzahl der beteiligten Tabellen die Anzahl der Spalten
|
|
exponentiell und das macht es unübersichtlicher, wenn mehrere Beziehungen daran beteiligt sind.
|
|
|
|
Seit Version 1.0.2 können sie auch erzwingen, dass die relationale Abfrage mit genau einer
|
|
SQL Abfrage durchgeführt wird. Fügen Sie einfach einen
|
|
[together()|CActiveFinder::together]-Aufruf nach [with()|CActiveRecord::with] an, z.B.
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->with(
|
|
'author.profile',
|
|
'author.posts',
|
|
'categories')->together()->findAll();
|
|
~~~
|
|
|
|
Die obige Abfrage wird über eine einzelne SQL-Abfrage ausgeführt. Ohne
|
|
den Aufruf von [together|CActiveFinder::together] würden drei SQL Abfragen
|
|
benötigt werden: eine für den Verbund der Tabellen `Post`, `User` und
|
|
`Profile`, eine, die die Tabellen `User` und `Post` verbindet und eine
|
|
weitere die `Post`, `PostCategory` und `Category` zusammenführt.
|
|
|
|
Optionen für relationale Abfragen
|
|
---------------------------------
|
|
|
|
Wir haben bereits erwähnt, dass bei Beziehungsdeklarationen zusätzliche Optionen
|
|
angegeben werden können. Diese Optionen, durch Name-Wert-Paare festgelegt, werden
|
|
verwendet um die relationale Abfrage individuell anzupassen. Sie sind nachfolgend zusammengestellt.
|
|
|
|
- `select`: Eine Liste der abzufragenden Spalten der betreffenden AR Klasse.
|
|
Der Standardwert ist '*', d.h. alle Spalten. Die Spaltennamen sollten durch
|
|
`aliasToken` eindeutig gekennzeichnet sein, wenn sie in einem Ausdruck auftreten
|
|
(z.B. `COUNT(??.name) AS nameCount`).
|
|
|
|
- `condition`: Die `WHERE` Klausel, standardmäßig leer. Beachten Sie,
|
|
dass Spaltenreferenzen mit `aliasToken` eindeutig gekennzeichnet werden müssen (z.B. `??.id=10`).
|
|
|
|
- `params`: Die Parameter, die an die erzeugte SQL Abfrage gebunden werden sollen.
|
|
Diese sollten als ein Array von Namen-Werte Paaren angegeben werden. Diese Option ist
|
|
seit Version 1.0.3 verfügbar.
|
|
|
|
- `on`: Die `ON` Klausel. Die hier angegebene Bedingung wird an die JOIN Abfrage
|
|
mit einem `AND` Operator angehängt. Die Spaltennamen sollten durch
|
|
`aliasToken` eindeutig gekennzeichnet sein (z.B. `??.id=10`). Bei
|
|
`MANY_MANY`-Beziehungen wird diese Option nicht berücksichtigt. Diese Option ist seit Version 1.0.2 verfügbar.
|
|
|
|
- `order`: Die `ORDER BY` Klausel, standardmäßig leer. Beachten Sie,
|
|
dass Spaltenreferenzen mit `aliasToken` eindeutig gekennzeichnet werden müssen (z.B. `??.age
|
|
DESC`).
|
|
|
|
- `with`: Eine Liste kind-bezogener Objekte, die zusammen mit diesem Objekt
|
|
geladen werden sollen. Beachten Sie, dass bei falscher Verwendung dieser
|
|
Option eine endlose Beziehungsschleife entstehen kann.
|
|
|
|
- `joinType`: Jointyp für diesen Beziehungstyp. Der Standardwert ist `LEFT
|
|
OUTER JOIN`.
|
|
|
|
- `aliasToken`: Der Platzhalter für die Spaltenpräfix. Er wird durch den
|
|
entsprechenden Alias der Tabelle ersetzt, um die Spaltenreferenzen zu vereindeutigen.
|
|
Der Standardwert ist `'??.'`
|
|
|
|
- `alias`: Der Alias für die in dieser Beziehung verbundene Tabelle.
|
|
Diese Option ist seit Version 1.0.1 verfügbar. Der Standardwert ist null,
|
|
das bedeutet, der Tabellenalias wird automatisch erzeugt. Das ist etwas
|
|
anderes als `aliasToken`. Im letzterem Falle handelt es sich nur um einen Platzhalter, der
|
|
vom tatsächlichen Tabellenalias ersetzt wird.
|
|
|
|
- `group`: Die `GROUP BY` Klausel, standardmäßig leer. Beachten Sie,
|
|
dass Spaltenreferenzen mit `aliasToken` eindeutig gekennzeichnet werden müssen (z.B. `??.age`).
|
|
|
|
- `having`: Die `HAVING` Klausel, standarmäßig leer. Beachten Sie,
|
|
dass Spaltenreferenzen mit `aliasToken` eindeutig gekennzeichnet werden müssen (z.B. `??.age`).
|
|
Diese Option ist seit Version 1.0.1. verfügbar.
|
|
|
|
- `index`: Der Name der Spalte, deren Wert als Schlüssel für den Array mit
|
|
relationalen Objekten verwendet werden soll. Wird diese Option nicht gesetzt,
|
|
wird ein 0-basierter ganzzahliger Index verwendet.
|
|
Diese Option kann nur für `HAS_MANY`- und `MANY_MANY`-Beziehungen gesetzt
|
|
werden. Diese Option ist seit Version 1.0.7 verfügbar.
|
|
|
|
Zusätzlich sind für bestimmte Beziehungen folgende Optionen beim `lazy loading`
|
|
verfügbar:
|
|
|
|
- `limit`: Begrenzt die auszuwählenden Zeilen. Diese Option ist bei der Beziehung
|
|
`BELONGS_TO` NICHT anwendbar.
|
|
|
|
- `offset`: Versatz der auszuwählenden Zeilen. Diese Option ist bei der Beziehung
|
|
`BELONGS_TO` NICHT anwendbar.
|
|
|
|
Nachfolgend modifizieren wir die `posts`-Beziehung beim `User` indem wir einige der
|
|
obigen Optionen einbeziehen:
|
|
|
|
~~~
|
|
[php]
|
|
class User extends CActiveRecord
|
|
{
|
|
public function relations()
|
|
{
|
|
return array(
|
|
'posts'=>array(self::HAS_MANY, 'Post', 'authorID',
|
|
'order'=>'??.createTime DESC',
|
|
'with'=>'categories'),
|
|
'profile'=>array(self::HAS_ONE, 'Profile', 'ownerID'),
|
|
);
|
|
}
|
|
}
|
|
~~~
|
|
|
|
Wenn wir jetzt auf `$author->posts` zugreifen, erhalten wir die `posts` des
|
|
`author` absteigend sortiert nach ihrer `creation time` (Erstellzeitpunkt).
|
|
Bei jedem Post wurden auch seine Kategorien geladen.
|
|
|
|
> Info|Info: Wenn ein Spaltenname in zwei oder mehr Tabellen auftaucht, die
|
|
verbunden werden sollen, muss er eindeutig gekennzeichnet werden. Dies geschieht durch
|
|
voranstellen des Tabellennamens vor den Spaltennamen. Aus `id` wird beispielsweise
|
|
`Team.id`. Bei relationalen AR-Abfragen haben wir diese Freiheit nicht, da AR den
|
|
SQL-Ausdruck automatisch erzeugt und jeder Tabelle systematisch ein Alias gegeben wird.
|
|
Um Konflikte mit Spaltennamen zu vermeiden, benutzen wir daher einen
|
|
Platzhalter, um zu markieren, dass wir eine zu kennzeichnende Spalte
|
|
verwenden. AR ersetzt den Platzhalter mit einem geeignet Tablellenalias und
|
|
sorgt so fur eine ordnungsgemäße eindeutige Kennzeichnung.
|
|
|
|
|
|
Dynamische Optionen für relationale Abfragen
|
|
--------------------------------------------
|
|
|
|
Seit Version 1.0.2 können wir dynamische Optionen für relationale Abfragen sowohl bei
|
|
[with()|CActiveRecord::with], als auch bei der `with`-Option nutzen. Die dynamischen
|
|
Optionen überschreiben die in der
|
|
[relations()|CActiveRecord::relations]-Methode spezifisierten.
|
|
Wollen wir beispielsweise im obigen `User`-Model den `eager loading`-Ansatz
|
|
nutzen, um die Beiträge, die zu einem Autor gehören in *aufsteigender
|
|
Reihenfolge* zu erhalten (die `order`-Option in der `relations`-Angabe verwendet
|
|
absteigende Reihenfolge), können wir das folgendermaßen erreichen:
|
|
|
|
~~~
|
|
[php]
|
|
User::model()->with(array(
|
|
'posts'=>array('order'=>'??.createTime ASC'),
|
|
'profile',
|
|
))->findAll();
|
|
~~~
|
|
|
|
Seit Version 1.0.5 können dynamische Abfrageoptionen auch beim `lazy
|
|
loading`-Ansatz verwendet werden. Dazu rufen wir die Methode mit dem namen des
|
|
verbundenen Objekts auf und übergeben die Abfrageoptionen als Parameter. Der
|
|
folgende Code liefert zum Beispiel die Beiträge eines Benutzers mit `status`
|
|
1:
|
|
|
|
~~~
|
|
[php]
|
|
$user=User::model()->findByPk(1);
|
|
$posts=$user->posts(array('condition'=>'status=1'));
|
|
~~~
|
|
|
|
|
|
Statistische Abfragen
|
|
---------------------
|
|
|
|
> Note|Hinweis: Statistische Abfragen werden seit Version 1.0.4 unterstützt.
|
|
|
|
Neben den oben beschriebenen relationalen Abfragen unterstützt Yii auch sogenannte
|
|
statistische Abfragen (auch: aggregierte Abfragen, engl.: aggregational query)
|
|
Mit ihnen können Informationen zu verbundenen Objekten ausgelesen werden, wie
|
|
z.B. die Anzahl von Kommentaren zu einem Beitrag, die durchschnittliche
|
|
Bewertung eines Produkts, etc. Statistische Abfragen können nur für Objekte
|
|
durchgeführt werden, die in einer `HAS_MANY`-Beziehung (z.B. ein Beitrag hat
|
|
viele Kommentare) oder einer `MANY_MANY`-Beziehung (z.B. ein Beitrag gehört zu
|
|
vielen Kategorien und eine Kategorie hat viele Beiträge) stehen.
|
|
|
|
Eine statistische Abfrage wird ähnlich wie eine oben beschriebene
|
|
relatoinale Abfrage durchgeführt. Analog dazu müssen wir zunächst die statistische Abfrage
|
|
in der [relations()|CActiveRecord::relations]-Methode des [CActiveRecord]
|
|
festlegen.
|
|
~~~
|
|
[php]
|
|
class Post extends CActiveRecord
|
|
{
|
|
public function relations()
|
|
{
|
|
return array(
|
|
'commentCount'=>array(self::STAT, 'Comment', 'postID'),
|
|
'categoryCount'=>array(self::STAT, 'Category', 'PostCategory(postID, categoryID)'),
|
|
);
|
|
}
|
|
}
|
|
~~~
|
|
|
|
Hier legen wir zwei statistische Abfragen fest: `commentCount` errechnet die
|
|
Anzahl der Kommentare zu einem Beitrag und `categoryCount` die Anzahl von
|
|
Kategorien, denen ein Beitrag zugeordnet wurde. Beachten Sie, dass `Post` und
|
|
`Comment` in einer `HAS_MANY`-Beziehung zueineander stehen, während `Post` und
|
|
`Category` über eine `MANY_MANY`-Beziehung (über die Verbindungstabelle
|
|
`PostCategory`) verknüpft sind.
|
|
|
|
Mit obiger Deklaration können wir die Anzahl der Kommentare eines Beitrags
|
|
über den Ausdruck `$post->commentCount` beziehen. Beim ersten Zugriff auf
|
|
diese Eigenschaft wird implizit eine SQL-Abfrage durchgeführt, um das
|
|
entsprechende Ergebnis zu bestimmen. Wie wir bereits wissen, handelt es sich
|
|
hierbei um den *lazy loading*-Ansatz. Wenn wir die Anzahl der Kommentare für
|
|
mehrere Beiträge bestimmen wollen, können wir auch die *eager loading*-Methode
|
|
verwenden:
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->with('commentCount', 'categoryCount')->findAll();
|
|
~~~
|
|
|
|
Dieser Befehl führt drei SQL-Anweisungen aus um alle Beiträge zusammen mit der Anzahl
|
|
ihrer Kommentare und Kategorien zurückzuliefern. Würden wir den *lazy
|
|
loading*-Ansatz verwenden, würde das in `2*N+1` SQL-Abfragen resultieren, wenn
|
|
es `N` Beiträge gibt.
|
|
|
|
Standardmäßig wird eine statistische Abfrage unter Verwendung von `COUNT`
|
|
durchgeführt (was in obigem Beispiel für die Ermittlung der Anzahl der Kommentare und
|
|
Kategorien der Fall ist). Wir können dies über zusätzliche Optionen bei der
|
|
Deklaration in [relations()|CActiveRecord::relations] anpassen. Hier die
|
|
verfügbaren Optionen:
|
|
|
|
- `select`: Der statistische Ausdruck. Vorgabewert ist `COUNT(*)`, was der
|
|
Anzahl der Kindobjekte entspricht.
|
|
|
|
- `defaultValue`: Der Wert, der jenen Einträgen zugeordnet werden soll, für
|
|
die die statistische Abfrage kein Ergenis liefert. Hat ein Beitrag z.B. keine
|
|
Kommentare, würde sein `commentCount` diesen Wert erhalten. Vorgabewert ist 0.
|
|
|
|
- `condition`: Die `WHERE`-Bedingung. Standardmäßig leer.
|
|
|
|
- `params`: Die Parameter, die an die erzeugte SQL-Anweisung gebunden
|
|
werden sollen. Sie sollten als Array aus Namen-/Wert-Paare angegeben werden.
|
|
|
|
- `order`: Die `ORDER BY`-Anweisung. Standardmäßig leer.
|
|
|
|
- `group`: Die `GROUP BY`-Anweisung. Standardmäßig leer.
|
|
|
|
- `having`: Die `HAVING`-Anweisung. Standarmäßig leer.
|
|
|
|
|
|
Relationale Abfragen mit benannten Bereichen
|
|
--------------------------------------------
|
|
|
|
> Note|Hinweis: Benannte Bereiche werden seit Version 1.0.5 unterstützt.
|
|
|
|
Auch relationale Abfragen können mit [benannten
|
|
Bereichen](/doc/guide/database.ar#named-scopes) kombiniert werden. Dabei
|
|
gibt es zwei Anwendungsfälle. Im ersten Fall werden benannte Bereiche auf
|
|
das Hauptmodel, im zweiten auf die verbundenen Objekte angewendet.
|
|
|
|
Der folgende Code zeigt, wie benannte Bereiche mit dem Hauptmodel
|
|
verwendet werden:
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->veroeffentlicht()->kuerzlich()->with('comments')->findAll();
|
|
~~~
|
|
|
|
Dies unterscheidet sich kaum vom Vorgehen bei nicht-relationalen Abfragen. Der einzige
|
|
Unterschied besteht im zusätzlichen Aufruf von `with()` nach der Kette
|
|
benannter Bereiche. Diese Abfrage würde also die kürzlich veröffentlichten
|
|
Beiträge zusammen mit ihren Kommentaren zurückliefern.
|
|
|
|
Und der folgende Code zeigt die Anwendung benannter Bereiche auf verbundene
|
|
Models:
|
|
|
|
~~~
|
|
[php]
|
|
$posts=Post::model()->with('comments:kuerzlich:freigegeben')->findAll();
|
|
~~~
|
|
|
|
Diese Abfrage liefert alle Beiträge zusammen mit ihren freigegebenen
|
|
Kommentaren zurück. Beachten Sie, dass `comments` sich auf den Namen der
|
|
Beziehung bezieht, während `kuerzlich` und `freigegeben` zwei benannte
|
|
Bereiche sind, die in der Modelklasse `Comment` deklariert sind. Der
|
|
Beziehungsname und die benannten Bereiche sollten durch Doppelpunkte
|
|
getrennt werden.
|
|
|
|
Benannte Bereiche können auch in den in [CActiveRecord::relations()]
|
|
festgelegten `with`-Optionen einer Beziehung angegeben werden. Würden wir im
|
|
folgenden Beispiel auf `$user->posts` zugreifen, würden alle
|
|
*freigegebenen* Kommentare des Beitrags zurückgeliefert werden.
|
|
|
|
~~~
|
|
[php]
|
|
class User extends CActiveRecord
|
|
{
|
|
public function relations()
|
|
{
|
|
return array(
|
|
'posts'=>array(self::HAS_MANY, 'Post', 'authorID',
|
|
'with'=>'comments:freigegeben'),
|
|
);
|
|
}
|
|
}
|
|
~~~
|
|
|
|
> Note|Hinweis: Bei relationalen Abfragen können nur benannte Bereiche
|
|
verwendet werden, die in [CActiveRecord::scopes] definiert wurden. Daher
|
|
können diese hier auch nicht parametrisiert werden.
|
|
|
|
<div class="revision">$Id: database.arr.txt 1248 2009-07-15 19:40:44Z qiang.xue $</div>
|