mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-06 16:16:53 +01:00
274 lines
11 KiB
Plaintext
274 lines
11 KiB
Plaintext
Data Access Objects (DAO)
|
|
=========================
|
|
|
|
Data Access Objects (Datenzugriffsobjekte, DAO) bieten eine allgemeine
|
|
API (Programmierschnittstelle) für den Zugriff auf Daten, die in unterschiedlichen
|
|
Datenbankmanagementsystemen (DBMS) gespeichert sind. Dadurch kann das zugrundeliegende DBMS ausgetauscht
|
|
werden ohne dass der Code, der DAO für den Zugriff auf die Daten nutzt, geändert
|
|
werden muss.
|
|
|
|
Yii-DAO setzt auf [PHP Data Objects (PDO)](http://php.net/manual/en/book.pdo.php)
|
|
auf. Das ist eine Erweiterung, die einen einheitlichen Datenzugriff auf viele,
|
|
weit verbreitete DBMS biete, wie z.B. MySQL und PostgreSQL. Um Yii-DAO nutzen
|
|
zu können, müssen daher die PDO-Erweiterung und die spezifischen PDO-Datenbanktreiber,
|
|
(z.B. `PDO_MYSQL`) installiert werden.
|
|
|
|
Yii-DAO besteht hauptsächlich aus den folgenden vier Klassen:
|
|
|
|
- [CDbConnection]: repräsentiert eine Verbindung zur Datenbank.
|
|
- [CDbCommand]: repräsentiert eine SQL-Anweisung, die gegenüber der Datenbank ausgeführt werden soll.
|
|
- [CDbDataReader]: repräsentiert einen forward-only (sinngem.:nur vorwärts
|
|
gerichteten) Datenstrom von Zeilen aus der Ergebnismenge einer Suchanfrage.
|
|
- [CDbTransaction]: repräsentiert eine DB Transaktion.
|
|
|
|
Nachfolgend zeigen wir den Einsatz von Yii-DAO in unterschiedlichen
|
|
Anwendungsfällen.
|
|
|
|
Aufbauen der Datenbankverbindung
|
|
--------------------------------
|
|
|
|
Um eine Verbindung zur Datenbank aufzubauen, müssen Sie eine [CDbConnection]-
|
|
Instanz erzeugen und aktivieren. Um die Anmeldeinformationen anzugeben, ist
|
|
ein Data Source Name (DSN) erforderlich. Benutzername und ein Passwort können ebenfalls
|
|
benötigt werden. Im Falle eines Fehlers (also ungültiger DSN oder falscher
|
|
Benutzername bzw. Passwort) während des Verbindungsaufbaus wird
|
|
eine Exception ausgelöst.
|
|
|
|
~~~
|
|
[php]
|
|
$connection=new CDbConnection($dsn,$username,$password);
|
|
// Verbindung aufbauen. Sie können mögliche Exceptions
|
|
// mit try...catch auffangen
|
|
$connection->active=true;
|
|
......
|
|
$connection->active=false; // Verbindung beenden
|
|
~~~
|
|
|
|
Das DSN-Format hängt vom benutzten PDO-Datenbanktreiber ab. Im Allgemeinen
|
|
enthält ein DSN den PDO-Treibernamen, gefolgt von einem Doppelpunkt, gefolgt
|
|
von der treiberspezifischen Verbindungssyntax. Siehe [PDO
|
|
Dokumentation](http://www.php.net/manual/en/pdo.construct.php) für weitere
|
|
Informationen. Nachfolgend eine Liste der gebräuchlichsten DSN-Formate:
|
|
|
|
- SQLite: `sqlite:/path/to/dbfile`
|
|
- MySQL: `mysql:host=localhost;dbname=testdb`
|
|
- PostgreSQL: `pgsql:host=localhost;port=5432;dbname=testdb`
|
|
- SQL Server: `mssql:host=localhost;dbname=testdb`
|
|
- Oracle: `oci:dbname=//localhost:1521/testdb`
|
|
|
|
Da [CDbConnection] von [CApplicationComponent] abgeleitet ist, können wir sie auch
|
|
als [Applikationskomponente](/doc/guide/basics.application#application-component)
|
|
einsetzen. Dazu muss eine Anwendungskomponente `db` (die auch anderse benannt
|
|
sein kann) in der [Applikationskonfiguration](/doc/guide/basics.application#application-configuration)
|
|
wie folgt konfiguriert werden:
|
|
|
|
~~~
|
|
[php]
|
|
array(
|
|
......
|
|
'components'=>array(
|
|
......
|
|
'db'=>array(
|
|
'class'=>'CDbConnection',
|
|
'connectionString'=>'mysql:host=localhost;dbname=testdb',
|
|
'username'=>'root',
|
|
'password'=>'password',
|
|
'emulatePrepare'=>true, // wird von einigen MySQL-Installationen benötigt
|
|
),
|
|
),
|
|
)
|
|
~~~
|
|
|
|
Wir können dann auf die bereits automatisch aktivierte DB-Verbindung mittels
|
|
`Yii::app()->db` zugreifen, es sei denn, wir haben [CDbConnection::autoConnect]
|
|
mit false konfiguriert. Mit dieser Vorgehensweise kann eine einzelne
|
|
DB-Verbindung an vielen Stellen in unserm Code gemeinsam benutzt werden.
|
|
|
|
Ausführen von SQL-Anweisungen
|
|
-----------------------------
|
|
|
|
Nachdem eine Verbindung zur Datenbank aufgebaut ist, können mit [CDbCommand]
|
|
SQL-Anweisungen ausgeführt werden. Eine [CDbCommand]-Instanz wird erzeugt, indem
|
|
man [CDbConnection::createCommand()] mit der entsprechenden SQL-Anweisung
|
|
aufruft:
|
|
|
|
~~~
|
|
[php]
|
|
$command=$connection->createCommand($sql);
|
|
// falls erforderlich, kann die SQL Anweisung folgendermaßen aktualisiert werden:
|
|
// $command->text=$neuesSQL;
|
|
~~~
|
|
|
|
Eine SQL-Anweisung wird über [CDbCommand] auf eine der folgenden beiden Arten
|
|
ausgeführt:
|
|
|
|
- [execute()|CDbCommand::execute]: führt eine SQL-Anweisung aus, die keine
|
|
Abfrage ist, wie `INSERT`, `UPDATE` und `DELETE`. Bei Erfolg, liefert sie die Anzahl
|
|
der betroffenen Zeilen zurück.
|
|
|
|
- [query()|CDbCommand::query]: führt eine SQL-Abfrage wie `SELECT` aus, die
|
|
Datenzeilen zurückliefert. Falls erfolgreich, gibt sie eine [CDbDataReader]-Instanz
|
|
zurück, mit dem man die resultierenden Datenzeilen durchlaufen kann. Der Einfachheit
|
|
halber ist auch eine Reihe von `queryXXX()`-Methoden implementiert, die das Abfrageergebnis
|
|
direkt zurückliefern.
|
|
|
|
Falls ein Fehler während der Ausführung der SQL-Anweisung
|
|
auftritt, wird eine Exception ausgelöst,
|
|
|
|
~~~
|
|
[php]
|
|
$rowCount=$command->execute(); // führe die "Nicht-Abfrage"-SQL-Anweisung aus
|
|
$dataReader=$command->query(); // führe die SQL-Abfrage aus
|
|
$rows=$command->queryAll(); // Abfragen und Zurückgeben aller Zeilen des Ergebnisses
|
|
$row=$command->queryRow(); // Abfragen und Zurückgeben der ersten Zeile des Ergebnisses
|
|
$column=$command->queryColumn(); // Abfragen und Zurückgeben der ersten Spalte des Ergebnisses
|
|
$value=$command->queryScalar(); // Abfragen und Zurückgeben des ersten Feldes in der ersten Zeile
|
|
~~~
|
|
|
|
Arbeiten mit Query-Ergebnissen
|
|
------------------------------
|
|
|
|
Nachdem [CDbCommand::query()] eine Instanz von [CDbDataReader] erzeugt hat,
|
|
kann man, durch wiederholten Aufruf von [CDbDataReader::read()], Zeilen der
|
|
resultierenden Daten abfragen. Man kann [CDbDataReader] auch mit dem PHP
|
|
`foreach`-Konstrukt zum zeilenweise Abfragen nutzen.
|
|
|
|
~~~
|
|
[php]
|
|
$dataReader=$command->query();
|
|
// Wiederholter Aufruf von read() bis false zurückgegeben wird
|
|
while(($row=$dataReader->read())!==false) { ... }
|
|
// Mit foreach wird jede Datenzeile durchlaufen
|
|
foreach($dataReader as $row) { ... }
|
|
// Abfragen aller Zeilen auf einmal in ein einzelnes Array
|
|
$rows=$dataReader->readAll();
|
|
~~~
|
|
|
|
> Note|Hinweis: Anders als [query()|CDbCommand::query] liefern alle
|
|
`queryXXX()`-Methoden die Daten direkt zurück. Zum Beispiel gibt
|
|
[queryRow()|CDbCommand::queryRow] ein Array zurück, das die
|
|
erste Zeile des Abfrageergebnisses repräsentiert.
|
|
|
|
Verwenden von Transaktionen
|
|
---------------------------
|
|
|
|
Wenn eine Anwendung einige Abfragen ausführt, von denen jede Informationen
|
|
aus der Datenbank liest und/oder in sie schreibt, ist es wichtig, sicherzustellen,
|
|
dass in der Datenbank auch alle davon ausgeführt wurden.
|
|
In diesem Fall kann eine Transaktion gestartet werden, welche in Yii
|
|
durch eine [CDbTransaction]-Instanz repräsentiert wird:
|
|
|
|
- Beginne die Transaktion.
|
|
- Führe die Abfragen der Reihe nach aus. Alle Datenbankaktualisierungen sind für die
|
|
Außenwelt unsichtbar.
|
|
- Abschließen der Transaktion mit Commit. Die Aktualisierungen werden sichtbar, falls
|
|
die Transaktion erfolgreich war.
|
|
- Falls eine der Anfragen fehlschlägt, wird die ganze Transaktion
|
|
zurückgefahren.
|
|
|
|
Der obige Ablauf kann mit dem folgenden Code implementiert werden:
|
|
|
|
~~~
|
|
[php]
|
|
$transaction=$connection->beginTransaction();
|
|
try
|
|
{
|
|
$connection->createCommand($sql1)->execute();
|
|
$connection->createCommand($sql2)->execute();
|
|
//.... weitere SQL Abfragen
|
|
$transaction->commit();
|
|
}
|
|
catch(Exception $e) // Eine Exception wird ausgelöst, falls eine Abfrage fehlschlägt
|
|
{
|
|
$transaction->rollBack();
|
|
}
|
|
~~~
|
|
|
|
Binden von Parametern
|
|
---------------------
|
|
|
|
Um [SQL-Injection](http://de.wikipedia.org/wiki/SQL-Injection)-Angriffe zu
|
|
vermeiden, und um die Geschwindigkeit mehrfach verwendeter SQL-Anweisungen zu verbessern,
|
|
kann man eine SQL Anweisung mit optionalen Platzhaltern für Parameter
|
|
"vorbereiten" (engl. prepare). Die Platzhalter werden während des
|
|
Bindungsprozesses durch die eigentlichen Parameter ersetzt.
|
|
|
|
Die Platzhalter für Parameter können entweder benannt (durch eine eindeutige
|
|
Zeichenkette) oder unbenannt (dargestellt durch Fragezeichen) sein. Rufen Sie
|
|
[CDbCommand::bindParam()] oder [CDbCommand::bindValue()] auf, um diese Platzhalter
|
|
durch die eigentlichen Parameter zu ersetzen. Die Parameter müssen nicht in
|
|
Anführungszeichen gesetzt werden. Der zugrundeliegende Datenbanktreiber erledigt
|
|
das für sie. Das Binden der Parameter muss
|
|
vor der Ausführung der SQL Anweisung erfolgen.
|
|
|
|
~~~
|
|
[php]
|
|
// SQL-Anweisung mit zwei Platzhaltern ":username" und ":email"
|
|
$sql="INSERT INTO users(username, email) VALUES(:username,:email)";
|
|
$command=$connection->createCommand($sql);
|
|
// Ersetze den Platzhalter ":username" durch den tatsächlichen Benutzernamen
|
|
$command->bindParam(":username",$username,PDO::PARAM_STR);
|
|
// Ersetze den Platzhalter ":email" durch die tatsächliche E-Mail Adresse
|
|
$command->bindParam(":email",$email,PDO::PARAM_STR);
|
|
$command->execute();
|
|
// Füge eine weitere Zeile mit einem neuen Parametersatz ein
|
|
$command->bindParam(":username",$username2,PDO::PARAM_STR);
|
|
$command->bindParam(":email",$email2,PDO::PARAM_STR);
|
|
$command->execute();
|
|
~~~
|
|
|
|
Die Methoden [bindParam()|CDbCommand::bindParam] und [bindValue()|CDbCommand::bindValue]
|
|
sind sehr ähnlich. Der einzige Unterschied besteht darin,
|
|
dass erstere einen Parameter als Referenz auf eine PHP-Variable, letztere dagegen
|
|
mit einem Wert bindet. Für Parameter, die für einen großen Block von Daten
|
|
stehen, ist ersteres aus Performancegründen zu bevorzugen.
|
|
|
|
Weitere Einzelheiten zum Binden von Parametern finden Sie in der [einschlägigen PHP
|
|
Dokumentation](http://www.php.net/manual/en/pdostatement.bindparam.php).
|
|
|
|
Binden von Spalten
|
|
------------------
|
|
|
|
Beim Auslesen von Abfrageergebnissen kann man auch Spalten an PHP Variablen
|
|
binden, so dass sie jedes Mal beim Zugriff auf eine Zeile automatisch mit den
|
|
aktuellen Daten befüllt werden.
|
|
|
|
~~~
|
|
[php]
|
|
$sql="SELECT username, email FROM users";
|
|
$dataReader=$connection->createCommand($sql)->query();
|
|
// Binde die 1. Spalte (username) an die Variable $username
|
|
$dataReader->bindColumn(1,$username);
|
|
// Binde die 2. Spalte (email) an die Variable $email
|
|
/$dataReader->bindColumn(2,$email);
|
|
while($dataReader->read()!==false)
|
|
{
|
|
// $username und $email enthalten den Benutzernamen und die E-Mail-Adresse der aktuellen Zeile
|
|
}
|
|
~~~
|
|
|
|
Verwenden eines Tabellenpräfix
|
|
------------------------------
|
|
|
|
Seit Version 1.1.0 unterstützt Yii auch Tabellenpräfixe. Ein Präfix ist ein
|
|
String, der allen Tabellennamen der aktuellen Verbindung vorangestellt wird.
|
|
Es wird meist in gemeinsam genutzten Hosting-Umgebungen verwendet, wo mehrere
|
|
Anwendungen sich eine einzelne Datenbank teilen und zur Unterscheidung
|
|
verschiedene Präfixe verwenden. Eine Anwendung könnte z.B. `tbl_`, eine andere
|
|
`yii_` als Präfix verwenden.
|
|
|
|
Um ein Tabellenpräfix zu verwenden, konfigurieren Sie es in der Eigenschaft
|
|
[CDbConnection::tablePrefix]. In SQL-Ausdrücken verwenden Sie dann
|
|
`{{TabellenName}}` um sich auf Tabellennamen zu beziehen, wobei `TabellenName`
|
|
für den Namen der Tabelle ohne Präfix steht. Wenn die Datenbank z.B. eine
|
|
Tabelle `tbl_users` enthält und `tbl_` als Tabellenpräfix konfiguriert wurde,
|
|
können wir so eine Abfrage auf dieser Tabelle durchführen:
|
|
|
|
~~~
|
|
[php]
|
|
$sql='SELECT * FROM {{users}}';
|
|
$users=$connection->createCommand($sql)->queryAll();
|
|
~~~
|
|
|
|
<div class="revision">$Id: database.dao.txt 1479 2009-10-23 13:24:20Z qiang.xue $</div>
|