0 follower

Data Access Objects (DAO)

Data Access Objects (DAO) dostarcza generycznego API celem dostępu do danych przechowywanych w różnych systemach zarządzania bazą danych (DBMS). W rezultacie, ukryty DBMS może zostać zastąpiony przez inny bez potrzeby zmiany kodu, który używa DAO celem dostępu do danych.

Yii DAO jest stworzone w oparciu o PHP Data Objects (PDO) które jest rozszerzeniem dostarczającym ujednolicony dostęp do danych dla wielu popularnych DBMS, takich jak MySQL, PostgreSQL. Dlatego też, by używać Yii DAO, rozszerzenie PDO oraz poszczególne sterowniki PDO dla baz danych (e.g. PDO_MYSQL) muszą być zainstalowane.

Yii DAO składa się głównie z następujących czterech klas:

  • CDbConnection: reprezentuje połączenie z bazą danych,
  • CDbCommand: reprezentuje zapytanie SQL, wykonywane na bazie danych,
  • CDbDataReader: represents a forward-only stream of rows from a query result set,
  • CDbTransaction: reprezentuje transakcję DB.

W dalszej części, przedstawimy użycie Yii DAO w różnych scenariuszach.

1. Ustanawianie połączenia z bazą danych

Aby ustanowić połączenie z bazą danych należy utworzyć instancję CDbConnection a następnie aktywować połączenie. Aby połączyć się bazą danych potrzebny jest adres DNS. Użytkownik oraz hasło mogą być również potrzebne aby ustanowić połączenie. W przypadku gdy podczas łączenie nastąpi błąd (np. podano zły adres DNS lub złe hasło/nazwę użytkownika) zostanie rzucony odpowiedni wyjątek

$connection=new CDbConnection($dsn,$username,$password);
// ustanawianie połączenia. Możesz użyć try...catch aby złapać potencjalne wyjątki
$connection->active=true;
......
$connection->active=false;  // zamknij połączenie

Format adresu DNS zależy od używanego sterownika PDO dla danej bazy danych. Uogólniając, DNS składa się z nazwy sterownika PDO, po którym następuje przecinek a następnie zależna od sterownika składnia połączenia. Zobacz dokumentację PDO aby uzyskać więcej informacji. Poniżej znajduje się lista najczęściej używanych formatów DNS:

  • SQLite: sqlite:/scieżka/do/pliku/bazy,
  • 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

Ponieważ klasa CDbConnection rozszerza CApplicationComponent, możemy użyć jej jako komponent aplikacji. Aby to zrobić, skonfiguruj jako 'db' (lub używając innej nazwy) komponent aplikacji w konfiguracji aplikacji w następujący sposób,

array(
    ......
    'components'=>array(
        ......
        'db'=>array(
            'class'=>'CDbConnection',
            'connectionString'=>'mysql:host=localhost;dbname=testdb',
            'username'=>'root',
            'password'=>'hasło',
      'emulatePrepare'=>true,  // wymagane przez pewne instalacje MySQL         
        ),
    ),
)

Dostęp do połączenia DB, można uzyskać poprzez Yii::app()->db. Jest ono automatycznie aktywowane, chyba że wyraźnie skonfigurujemy CDbConnection::autoConnect jako false. Używając tego podejścia, jedno połączenie DB może być dzielone w wielu miejscach w naszym kodzie.

2. Wykonywanie wyrażeń SQL

Gdy połączenie z bazą danych jest ustanowione, można wykonywać wyrażania SQL za pomocą CDbCommand. Utworzenie instancji CDbCommand odbywa się poprzez wywołanie CDbConnection::createCommand() z określonym wyrażeniem SQL:

$command=$connection->createCommand($sql);
// jeśli wymagane, wyrażenie SQL może być zaktualizowane następująco:
// $command->text=$newSQL;

Wyrażenie SQL może zostać wykonane za pomocą CDbCommand w jeden z poniższych dwóch sposobów:

  • execute(): wywołuje wyrażenia SQL nie będące zapytaniami takie jak INSERT, UPDATE oraz DELETE. Jeśli zakończy się sukcesem, zwróci liczbę wierszy, na które wpłynęło wykonywane wyrażenia.

  • query(): wywołuje wyrażenie SQL, które zwraca wiersze z danymi, takie jak SELECT. Jeśli zakończy się sukcesem, zwróci instancję CDbDataReader, za pomocą której można przejrzeć wynikowe wiersze danych. Dla wygody został zaimplementowany zestaw metod queryXXX(), które to zwracają bezpośrednio wyniki zapytań.

Wyjątek zostanie rzucony, jeśli podczas wykonywania wyrażenia SQL wystąpi błąd.

$rowCount=$command->execute();   // wykonaj wyrażenie SQL nie będące zapytaniem
$dataReader=$command->query();   // wykonaj zapytanie SQL
$rows=$command->queryAll();      // zapytaj i zwróć wszystkie, wynikowe wiersze 
$row=$command->queryRow();       // zapytaj i zwróć pierwszy wiersz z wyników
$column=$command->queryColumn(); // zapytaj i zwróć pierwszą kolumnę spośród wyników
$value=$command->queryScalar();  // zapytaj i zwróć pierwsze pole w pierwszym wierszu

3. Wydobywanie wyników zapytań

Po wygenerowaniu przez CDbCommand::query() instancji klasy CDbDataReader, można zwrócić wiersze danych wynikowych poprzez powtarzające się wywoływanie metody CDbDataReader::read(). Można również użyć CDbDataReader w konstrukcji języka PHP foreach aby uzyskać wiersz po wierszu.

$dataReader=$command->query();
// powtarzaj wywołanie read() dopóki nie zwróci false
while(($row=$dataReader->read())!==false) { ... }
// używanie foreach do przeglądania każdego wiersza danych
foreach($dataReader as $row) { ... }
// zwrócenie wszystkich wierszy za jednym razem za pomocą jednej tablicy
$rows=$dataReader->readAll();

Uwaga: W odróżnieniu od query(), wszystkie metody queryXXX() zwracają dane bezpośrednio. Na przykład, queryRow() zwraca tablicę reprezentującą pierwszy wiersz wyniku zapytań.

4. Używanie transakcji

Kiedy aplikacja wykonuje kilka zapytań, za każdym razem czytając i/lub zapisując informacje w bazie danych, jest ważne by być pewnym, że na bazie danych nie została wykonana tylko część zapytań. W takim przypadku może zostać użyta transakcja reprezentowana w Yii poprzez instancję CDbTransaction:

  • rozpocznij transakcję;
  • wykonaj zapytania jedno po drugim. żadna zmiana w bazie danych nie jest widoczna na zewnątrz;
  • potwierdź (commit) transakcję. Zmiany będą widoczne jeśli transakcja się powiedzie;
  • jeśli jedno z zapytań nie powiedzie się, cała transakcja zostanie anulowana (roll-back).

Powyższy logika może zostać zaimplementowana używając następującego kodu:

$transaction=$connection->beginTransaction();
try
{
    $connection->createCommand($sql1)->execute();
    $connection->createCommand($sql2)->execute();
    //.... pozostałe wywołania SQL
    $transaction->commit();
}
catch(Exception $e) // jeśli zapytanie nie powiedzie się, wołany jest wyjątek
{
    $transaction->rollBack();
}

5. Przypinanie parametrów

Aby uniknąć ataków SQL injection oraz aby zwiększyć wydajność wykonywania często używanych wyrażeń SQL, można "przygotować" wyrażenie SQL z opcjonalnymi placeholderami parametrów, które to będą zastąpione przez aktualne parametry podczas procesu przypinania parametrów.

Placeholdery parametrów mogą być nawet nazwane (reprezentowane jako unikalne tokeny) lub mogą nie posiadać nazwy (reprezentowane za pomocą znaku zapytania). Aby zastąpić te placeholdery aktualnymi parametrami wywołaj CDbCommand::bindParam() lub CDbCommand::bindValue(). Parametry te nie muszą być objęte cudzysłowem, ukryty sterownik bazy danych zrobi to za Ciebie. Przypinanie parametrów musi nastąpić zanim wyrażenie SQL zostanie wykonane.

// SQL z dwoma placeholderami ":username" oraz ":email"
$sql="INSERT INTO users(username, email) VALUES(:username,:email)";
$command=$connection->createCommand($sql);
// zastąp placeholder ":username" aktualną wartością parametru username
$command->bindParam(":username",$username,PDO::PARAM_STR);
// zastąp placeholder ":email" aktualną wartością parametru email
$command->bindParam(":email",$email,PDO::PARAM_STR);
$command->execute();
// wstaw inny wiersz z nowym zestawem parametrów 
$command->bindParam(":username",$username2,PDO::PARAM_STR);
$command->bindParam(":email",$email2,PDO::PARAM_STR);
$command->execute();

Metody bindParam() oraz bindValue() są bardzo podobne. Jedyną różnicą jest to, że pierwsza przypina do parametru referencję zmiennej gdy druga wartość zmiennej. Dla parametrów które reprezentują duże bloki pamięci danych, druga metoda jest preferowaną ze względu na wydajność.

Aby uzyskać więcej informacji na temat przypinania parametrów zobacz odpowiednią dokumentację PHP.

6. Przypinanie kolumn

Podczas pobierania wyników zapytania, można również przypiąć do kolumny zmienne PHP tak, że są one automatycznie wypełniane ostatnimi danymi za każdym razem kiedy wiersz jest pobierany.

$sql="SELECT username, email FROM users";
$dataReader=$connection->createCommand($sql)->query();
// przypnij pierwszą kolumnę (username) do zmiennej the $username
$dataReader->bindColumn(1,$username);
// przypnij 2 kolumnę (email) do zmiennej $email
$dataReader->bindColumn(2,$email);
while($dataReader->read()!==false)
{
    // $username oraz $email zawierają nazwę użytkownika (username) oraz email dla aktualnego wiersza
}

~~~