0 follower

Data Access Objects (DAO)

Data Access Objects (DAO) tillhandahåller ett generellt programmeringsgränssnitt (API) för att ge tillgång till data lagrad i olika databahshanterare (DBMS). Detta medför att den underliggande databashanteraren kan bytas till en annan utan behov av ändringar i den kod som använder DAO för dataåtkomst.

Yii:s DAO har byggts ovanpå PHP Data Objects (PDO)-tillägget, vilket tillhandahåller enhetlig åtkomst till många populära databashanterare såsom MySQL, PostgreSQL. Av denna anledning behöver PDO-tillägget och den databasspecifika PDO- drivrutinen (t.ex. PDO_MYSQL) installeras innan Yii:s PDO används.

Yii:s DAO består i huvudsak av följande fyra klasser:

  • CDbConnection: representerar en anslutning till en databas.
  • CDbCommand: representerar en SQL-sats som skall exekveras mot en databas.
  • CDbDataReader: representerar en icke-reversibel följd av rader ur en frågas resultatmängd.
  • CDbTransaction: representerar en databastransaktion.

I följande stycken introduceras användning av Yii:s DAO i olika scenarier.

1. Upprätta en databasanslutning

För att upprätta en databasanslutning, skapa en instans av CDbConnection och aktivera den. Ett namn på en datakälla (DSN) erfordras för att specificera nödvändig information för anslutning till databasen. Ett användarnamn och ett lösenord kan också behövas för att upprätta anslutningen. En exception signaleras i händelse av att ett fel uppstår under upprättandet av anslutningen (t.ex. felaktig DSN eller ogiltigt användarnamn/lösenord).

$connection=new CDbConnection($dsn,$username,$password);
// establish connection. You may try...catch possible exceptions
$connection->active=true;
......
$connection->active=false;  // close connection

Formatet för ett DSN bestäms av vilken databasspecifik PDO-drivrutin som används. Generellt sett består ett DSN av namnet på PDO-drivrutinen följt av ett kolon, följt av drivrutinsspecifik anslutningssyntax. Se PDO-dokumentationen för fullständig information. Nedan återges en lista med vanligtvis använda DSN-format:

  • 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

Eftersom CDbConnection är en utvidgning av CApplicationComponent, kan vi även använda den som just en applikationskomponent. För att göra så, konfigurera in en db (eller annat namn) applikationskomponent i applikationskonfigurationen enligt följande,

array(
    ......
    'components'=>array(
        ......
        'db'=>array(
            'class'=>'CDbConnection',
            'connectionString'=>'mysql:host=localhost;dbname=testdb',
            'username'=>'root',
            'password'=>'password',
            'emulatePrepare'=>true,  // erfordras i vissa MySQL-installationer
        ),
    ),
)

Därefter kan databasanslutningen tillgås via Yii::app()->db som redan är automatiskt aktiverad, utom i det fall CDbConnection::autoConnect uttryckligen konfigurerats till false. Med användande av detta tillvägagångssätt kan en enda DB-anslutning delas av kod på flera ställen.

2. Exekvering av SQL-satser

När en databasanslutning väl har etablerats, kan SQL-satser exekveras med hjälp av CDbCommand. Man skapar en instans av CDbCommand genom anrop till CDbConnection::createCommand() med SQL-satsen angiven:

$command=$connection->createCommand($sql);
// if needed, the SQL statement may be updated as follows:
// $command->text=$newSQL;

En SQL-sats exekveras via CDbCommand på ett av följande två sätt:

  • execute(): genomför en SQL-sats som inte returnerar rader, såsom INSERT, UPDATE and DELETE. Vid felfritt genomförande returneras antalet rader som omfattades av operationen.

  • query(): genomför en SQL-sats som returnerar rader av data (eg. "påsar"), såsom SELECT. Vid felfritt genomförande, returneras en instans av CDbDataReader från vilken man kan traversera den resulterande mängden av rader. För bekvämlighets skull har även en uppsättning queryXXX()-metoder implementerats, vilka direkt returnerar respektive frågeresultat.

En exception signaleras om ett fel skulle inträffa under exekveringen av SQL-satser.

$rowCount=$command->execute();   // exekvera SQL som inte är en fråga
$dataReader=$command->query();   // exekvera en SQL-fråga
$rows=$command->queryAll();      // exekvera fråga och returnera alla resultatrader
$row=$command->queryRow();       // exekvera fråga och returnera första resultatraden
$column=$command->queryColumn(); // exekvera fråga och returnera första resultatkolumnen
$value=$command->queryScalar();  // exekvera fråga och returnera första fältet i första raden

3. Hämta frågeresultat

CDbCommand::query() genererat CDbDataReader-instansen, kan man hämta resulterande datarader genom att repetitivt anropa CDbDataReader::read(). Man kan även använda CDbDataReader i en i PHP-språket tillgänglig foreach-sats för att hämta data rad för rad.

$dataReader=$command->query();
// calling read() repeatedly until it returns false
while(($row=$dataReader->read())!==false) { ... }
// using foreach to traverse through every row of data
foreach($dataReader as $row) { ... }
// retrieving all rows at once in a single array
$rows=$dataReader->readAll();

Märk: Till skillnad från query(), returnerar alla queryXXX()-metoder data direkt. Till exempel returnerar queryRow()|CDbCommand::queryRow] en array som representerar den första raden i frågeresultatet.

4. Användning av transaktioner

När en applikation exekverar ett antal databasoperationer, som var och en läser information från och/eller skriver information till databasen, är det viktigt att försäkra sig om att databasen inte lämnas med några operationer ofullständigt utförda. En transaktion, representerad i Yii som en instans av CDbTransaction, kan inledas för att säkerställa detta:

  • Starta (begin) transaktionen.
  • Exekvera frågorna/operationerna en och en. Eventuella uppdateringar av databasen kan inte ses av av världen utanför.
  • Bekräfta (commit) transaktionen. Uppdateringar blir nu synliga eftersom transaktionen lyckades.
  • Om en av operationerna misslyckades reverseras hela transaktionen (rollback).

Ovanstående arbetsflöde kan implementeras med hjälp av följande kod:

$transaction=$connection->beginTransaction();
try
{
    $connection->createCommand($sql1)->execute();
    $connection->createCommand($sql2)->execute();
    //.... other SQL executions
    $transaction->commit();
}
catch(Exception $e) // an exception is raised if a query fails
{
    $transaction->rollBack();
}

5. Koppling av parametrar

För att undvika SQL- injekteringsattacker samt för förbättrad prestanda vid upprepad exekvering av SQL-satser, kan man i förekommande fall förbereda ("prepare") en SQL-sats med platsmarkörer för parametrar, som senare - vid koppling av parametrar (parameter binding) - kommer att ersättas med aktuella parametrar.

Parameterplatsmarkörerna kan antingen var namngivna (representerade av unika symboler) eller icke-namngivna (representerade av frågetecken). Anropa CDbCommand::bindParam() eller CDbCommand::bindValue() för att ersätta dessa platsmarkörer med de aktuella parametrarna. Parametrarna behöver inte omges av citationstecken; den underliggande databasdrivrutinen utför detta. Parameterkoppling måste ske innan SQL-satsen exekveras.

// an SQL with two placeholders ":username" and ":email"
$sql="INSERT INTO users(username, email) VALUES(:username,:email)";
$command=$connection->createCommand($sql);
// replace the placeholder ":username" with the actual username value
$command->bindParam(":username",$username,PDO::PARAM_STR);
// replace the placeholder ":email" with the actual email value
$command->bindParam(":email",$email,PDO::PARAM_STR);
$command->execute();
// insert another row with a new set of parameters
$command->bindParam(":username",$username2,PDO::PARAM_STR);
$command->bindParam(":email",$email2,PDO::PARAM_STR);
$command->execute();

Metoderna bindParam() och bindValue() är mycket snarlika. Den enda skillnaden är att den förra kopplar en parameter till en PHP-variabelreferens, den senare med ett värde. För parametrar som representerar stora block av dataminne är den förra metoden - av prestandaskäl - att föredra.

För fler detaljer om parameterkoppling, se den tillämpliga PHP- dokumentationen.

6. Koppling av kolumner

Vid hämtning av frågeresultat kan man också koppla kolumner till PHP-variabler så att de automatiskt uppdateras med senaste data var gång en rad hämtas.

$sql="SELECT username, email FROM users";
$dataReader=$connection->createCommand($sql)->query();
// bind the 1st column (username) with the $username variable
$dataReader->bindColumn(1,$username);
// bind the 2nd column (email) with the $email variable
$dataReader->bindColumn(2,$email);
while($dataReader->read()!==false)
{
    // $username and $email contain the username and email in the current row
}