0 follower

Data Access Objects (DAO)

Data Access Objects (DAO) dostarcza generycznego API umożliwiającego dostęp do danych przechowywanych w różnych systemach zarządzania bazą danych (ang. DBMS, pol. SZBD). W rezultacie, użyty SZBD może zostać zastąpiony przez inny bez potrzeby zmiany kodu używającego DAO aby uzyskać dostęp do danych.

Yii DAO zostało utworzone w oparciu o PHP Data Objects (PDO) które jest rozszerzeniem dostarczającym ujednolicony dostęp do danych dla wielu popularnych SZBD, takich jak MySQL, PostgreSQL. Dlatego też, by używać Yii DAO, rozszerzenie PDO oraz poszczególne sterowniki PDO dla baz danych (np. 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 instrukcję SQL, wykonywaną dla bazy danych,
  • CDbDataReader: reprezentuje przeglądany jedynie w przód strumień wierszy pochodzących z zestawu wyników zapytania,
  • 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ć ją. Aby połączyć się z bazą danych potrzebny jest adres DSN. Użytkownik oraz hasło mogą być również potrzebne aby ustanowić połączenie. W przypadku gdy podczas łączenia nastąpi błąd (np. podano zły adres DSN 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 DSN zależy od używanego, dla danej bazy danych, sterownika PDO. Uogólniając, DSN składa się z nazwy sterownika PDO, po którym następuje dwukropek 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 DSN:

  • 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 dziedziczy z klasy CApplicationComponent, możemy użyć jej jako komponent aplikacji. Aby to zrobić, należy skonfigurować komponent aplikacji 'db' (można użyć innej nazwy) 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 instrukcji SQL

Gdy połączenie z bazą danych zostało ustanowione, można wykonywać instrukcje SQL za pomocą CDbCommand. Utworzenie instancji CDbCommand odbywa się poprzez wywołanie metody CDbConnection::createCommand() z określoną instrukcją SQL:

$connection=Yii::app()->db;   // zakładamy, że masz skonfigurowane połączenie "db"
// Jeśli nie, możesz bezpośrednio utworzyć połączenie z bazą danych:
// $connection=new CDbConnection($dsn,$username,$password);
$command=$connection->createCommand($sql);
// jeśli jest to wymagane, instrukcja SQL może być zmieniona następująco:
// $command->text=$newSQL;

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

  • execute(): wykonuje instrukcję SQL nie będącą zapytaniem taką jak INSERT, UPDATE oraz DELETE. Jeśli wywołanie zakończy się sukcesem, metoda zwróci liczbę wierszy, na które wpłynęło wykonywanie instrukcji.

  • query(): wykonuje instrukcję SQL, taką jak SELECT, która zwraca wiersze z danymi. Jeśli wywołanie zakończy się sukcesem, metoda 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ń.

Jeżeli podczas wykonywania indtrukcji SQL wystąpił błąd zostanie rzucony wyjątek.

$rowCount=$command->execute();   // wykonaj instrukcję 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 spośród 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. Pobieranie wyników zapytań

Po wygenerowaniu przez metodę CDbCommand::query() instancji klasy CDbDataReader, można zwrócić wiersze danych wynikowych poprzez powtarzanie wywoływania metody CDbDataReader::read(). Instancję CDbDataReader można używać w konstrukcji foreach języka PHP co powoduje dostęp do danych wiersz po wierszu.

$dataReader=$command->query();
// powtarzaj wywołanie read() dopóki nie zwróci ono wartoś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 metody query(), wszystkie metody typu 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, ważnym jest by upewnić się, że na bazie danych nie została wykonana tylko część z tych zapytań. W takim przypadku może zostać zainicjowana 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 SQLi
    $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 instrukcji SQL, można "przygotować" instrukcję SQL z opcjonalnymi symbolami zastępczymi (ang. placeholders) parametrów, które to będą zastąpione przez aktualne parametry podczas procesu przypinania parametrów.

Symbole zastępcze parametrów mogą być zarówno nazwane (reprezentowane jako unikalne tokeny) lub mogą nie posiadać nazwy (reprezentowane za pomocą znaku zapytania). Aby zastąpić te symbole aktualnymi parametrami wywołaj metodę CDbCommand::bindParam() lub CDbCommand::bindValue(). Parametry te nie muszą być objęte cudzysłowem, użyty sterownik bazy danych zrobi to za Ciebie. Przypinanie parametrów musi nastąpić zanim instrukcja SQL zostanie wykonana.

// SQL z dwoma symbolami zastępczymi ":username" oraz ":email"
$sql="INSERT INTO tbl_user (username, email) VALUES(:username,:email)";
$command=$connection->createCommand($sql);
// zastąp symbol ":username" aktualną wartością parametru username
$command->bindParam(":username",$username,PDO::PARAM_STR);
// zastąp symbol ":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, pierwsza z nich jest korzystna 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, by były one automatycznie wypełniane najnowszymi danymi za każdym razem kiedy pobieramy wiersz.

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

7. Używanie prefiksów w tabelach

Yii dostarcza zintegrowanego wsparcia dla prefiksów tabel. Prefiks tabeli to łańcuch znaków, który poprzedza nazwę tabel w bazie danych, z którą jesteśmy połączeni. Prefiksów używa się najczęsciej we współdzielonych środowiku hostingowym, gdzie wiele aplikacji dzieli jedną tabelę bazodanową i używa różnych prefiksów tabeli w celu rozróżnienia ich od siebie. Na przykład, jedna aplikacja może używać prefiksu tbl_, druga zaś innego, np. yii_.

Aby móc używać prefiksów tabel, należy przypisać właściwości CDbConnection::tablePrefix pożądany prefiks tabeli. Następnie, w zapytaniach SQL należy używać konstrukcji {{TableName}}, gdzie TableName wskazuje na nazwę tabeli bez prefiksu. Na przykład, jeśli baza danych zawiera tabelę o nazwie tbl_user, gdzie tbl_ zostało skonfigurowane jako prefiks tabeli, wtedy możemy używać następującego kodu, aby zapytać o użytkowników:

$sql='SELECT * FROM {{user}}';
$users=$connection->createCommand($sql)->queryAll();

Found a typo or you think this page needs improvement?
Edit it on github !