0 follower

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) 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.

1. 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.

$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 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 einsetzen. Dazu muss eine Anwendungskomponente db (die auch anderse benannt sein kann) in der Applikationskonfiguration wie folgt konfiguriert werden:

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.

2. 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:

$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(): 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(): 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,

$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

3. 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.

$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();

Hinweis: Anders als query() liefern alle queryXXX()-Methoden die Daten direkt zurück. Zum Beispiel gibt queryRow() ein Array zurück, das die erste Zeile des Abfrageergebnisses repräsentiert.

4. 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:

$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();
}

5. Binden von Parametern

Um 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.

// 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() und 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.

6. 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.

$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
}