mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-05 15:54:07 +01:00
255 lines
12 KiB
Plaintext
255 lines
12 KiB
Plaintext
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)](http://php.net/manual/en/book.pdo.php)
|
|
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.
|
|
|
|
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 DNS.
|
|
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 DNS lub złe hasło/nazwę użytkownika)
|
|
zostanie rzucony odpowiedni wyjątek.
|
|
|
|
~~~
|
|
[php]
|
|
$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, dla danej bazy danych, sterownika PDO. Uogólniając,
|
|
DNS 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](http://www.php.net/manual/en/pdo.construct.php)
|
|
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] dziedziczy z klasy [CApplicationComponent], możemy użyć jej jako
|
|
[komponent aplikacji](/doc/guide/basics.application#application-component).
|
|
Aby to zrobić, należy skonfigurować komponent aplikacji 'db' (można użyć innej nazwy)
|
|
w [konfiguracji aplikacji](/doc/guide/basics.application#application-configuration)
|
|
w następujący sposób:
|
|
|
|
~~~
|
|
[php]
|
|
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.
|
|
|
|
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:
|
|
|
|
~~~
|
|
[php]
|
|
$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()|CDbCommand::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()|CDbCommand::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.
|
|
|
|
~~~
|
|
[php]
|
|
$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
|
|
~~~
|
|
|
|
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.
|
|
|
|
~~~
|
|
[php]
|
|
$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();
|
|
~~~
|
|
|
|
> Note|Uwaga: W odróżnieniu od metody [query()|CDbCommand::query], wszystkie metody typu `queryXXX()`
|
|
zwracają dane bezpośrednio. Na przykład, [queryRow()|CDbCommand::queryRow]
|
|
zwraca tablicę reprezentującą pierwszy wiersz wyniku zapytań.
|
|
|
|
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:
|
|
|
|
~~~
|
|
[php]
|
|
$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();
|
|
}
|
|
~~~
|
|
|
|
Przypinanie parametrów
|
|
----------------------
|
|
|
|
Aby uniknąć [ataków SQL injection](http://en.wikipedia.org/wiki/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.
|
|
|
|
~~~
|
|
[php]
|
|
// 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()|CDbCommand::bindParam] oraz [bindValue()|CDbCommand::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](http://www.php.net/manual/en/pdostatement.bindparam.php).
|
|
|
|
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.
|
|
|
|
~~~
|
|
[php]
|
|
$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
|
|
}
|
|
~~~
|
|
|
|
Używanie prefiksów w tabelach
|
|
-----------------------------
|
|
|
|
Poczynając od wersji 1.1.0, 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:
|
|
|
|
~~~
|
|
[php]
|
|
$sql='SELECT * FROM {{user}}';
|
|
$users=$connection->createCommand($sql)->queryAll();
|
|
~~~
|
|
|
|
<div class="revision">$Id: database.dao.txt 2266 2010-07-17 13:58:30Z qiang.xue $</div> |