Обʼєкти доступу до даних (DAO)
Обʼєкти доступу до даних (DAO) надають загальний API для доступу до даних, що зберігаються в різних СУБД. Це дозволяє без проблем змінити використовувану СУБД на будь-яку іншу без необхідності зміни коду, що використовує DAO для доступу до даних.
Yii DAO є надбудовою над PHP Data Objects (PDO) -
розширенням, яке надає уніфікований доступ до даних багатьох популярних СУБД, таких, як MySQL, PostgreSQL. Для використання Yii DAO необхідно, щоб були встановлені розширення PDO і драйвер PDO, що відповідає використовуваній базі даних (наприклад, PDO_MYSQL
).
Yii DAO складається із чотирьох основних класів:
- CDbConnection: представляє підключення до бази даних;
- CDbCommand: представляє вираз SQL та його виконання;
- CDbDataReader: представляє односпрямований потік рядків з даних, що повертаються у відповідь на SQL-запит;
- CDbTransaction: представляє транзакції бази даних.
Нижче ми проілюструємо використання Yii DAO.
Зʼєднання з базою даних
Для встановлення зʼєднання із базою необхідно створити екземпляр класу CDbConnection і активувати його. Додаткову інформацію, необхідну для підключення до БД (хост, порт, імʼя користувача, пароль тощо), вказуємо у DSN. У разі виникнення помилки в процесі зʼєднання з БД, буде викликано виключення (наприклад, невірний DSN або неправильні імʼя користувача/пароль).
$connection=new CDbConnection($dsn,$username,$password); // встановлюємо зʼєднання. Можна спробувати try…catch можливих виключень $connection->active=true; … $connection->active=false; // close connection
Формат DSN залежить від використовуваного драйвера PDO. Як правило, DSN складається із імені драйвера PDO, за яким слідує двокрапка, а далі вказуються параметри підключення, відповідні синтаксису підключення використовуваного драйвера. Докладніше з цим можна ознайомитися в документації по PDO. Нижче представлені кілька основних форматів DSN:
- SQLite:
sqlite:/path/to/dbfile
- MySQL/MariaDB:
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
Оскільки CDbConnection є підкласом CApplicationComponent, то ми можемо використовувати його у якості компонента. Для цього потрібно налаштувати
компонент db
у конфігурації додатка
наступним чином:
array( … 'components'=>array( … 'db'=>array( 'class'=>'CDbConnection', 'connectionString'=>'mysql:host=localhost;dbname=testdb', 'username'=>'root', 'password'=>'password', 'emulatePrepare'=>true, // необхідно для деяких версій MySQL ), ), )
Тепер ми можемо отримати доступ до зʼєднання з БД через Yii::app()->db
. Щоб зʼєднання не активувалося автоматично, необхідно встановити значення CDbConnection::autoConnect в false
. Цей спосіб дає нам можливість використання одного підключення до БД в будь-якому місці коду.
Виконання SQL-виразів
Коли зʼєднання з БД встановлено, ми можемо виконувати SQL-вирази, використовуючи CDbCommand. Для цього створюємо екземпляр CDbCommand шляхом виклику CDbConnection::createCommand() із зазначенням SQL-виразу:
$connection=Yii::app()->db; // так можна зробити, якщо в конфігурації описаний компонент зʼєднання "db" // Якщо не описаний - можна створити зʼєднання явно: // $connection=new CDbConnection($dsn,$username,$password); $command=$connection->createCommand($sql); // якщо необхідно, SQL-вираз можна оновити: // $command->text=$newSQL;
Існує два варіанти виконання SQL-виразу з використанням CDbCommand:
execute(): виконує SQL-вирази типу
INSERT
,UPDATE
іDELETE
. У разі успішного виконання, повертає кількість оброблених рядків;query(): виконує SQL-вирази, які повертають набори даних, наприклад, типу
SELECT
. У разі успішного виконання, повертає екземпляр класу CDbDataReader, через який доступні отримані дані. Для зручності також реалізовані методиqueryXXX ()
, повертають результати запиту напряму.
Якщо в процесі виконання SQL-виразу виникне помилка, буде викликано виключення.
$rowCount=$command->execute(); // виконання виразів типу `INSERT`, `UPDATE` та `DELETE` $dataReader=$command->query(); // виконання виразу типу `SELECT` $rows=$command->queryAll(); // запит і повернення всіх рядків результату $row=$command->queryRow(); // запит і повернення першого рядка результату $column=$command->queryColumn(); // запит і повернення першого стовпця результату $value=$command->queryScalar(); // запит і повернення першого поля в першому рядку
Обробка результатів запиту
Після того, як CDbCommand::query() створює екземпляр класу CDbDataReader, ми можемо отримати дані порядково шляхом повторного виклику методу CDbDataReader::read(). Для отримання даних рядок за рядком можна також використовувати CDbDataReader в конструкціях foreach
.
$dataReader=$command->query(); // багаторазово викликаємо read() до повернення методом значення false while(($row=$dataReader->read())!==false) { … } // використовуємо foreach для порядкового обходу даних foreach($dataReader as $row) { … } // отримуємо всі рядки разом в одному масиві $rows=$dataReader->readAll();
Примітка: Всі методи
queryXXX()
, на відміну від query(), повертають всі дані напряму. Наприклад, метод queryRow() повертає масив з першим рядком результату запиту.
Використання транзакцій
У випадку, коли додаток виконує декілька запитів, кожен з яких щось пише або читає з БД, важливо пересвідчитися, що набір запитів виконано повністю, а не наполовину. У цій ситуації можна скористатися транзакціями, що надаються екземпляром класу CDbTransaction:
- початок транзакції;
- виконання запитів по черзі, всі зміни даних у БД недоступні поза базою;
- підтвердження транзакції, якщо результат транзакції позитивний, то зміни стають доступні;
- якщо виникає помилка при виконанні будь-якого запиту, то вся транзація відкочується назад.
Цю послідовність дій можна реалізувати наступним чином:
$transaction=$connection->beginTransaction(); try { $connection->createCommand($sql1)->execute(); $connection->createCommand($sql2)->execute(); //… інші SQL запити $transaction->commit(); } catch(Exception $e) // у разі помилки при виконанні запиту викидається виключення { $transaction->rollback(); }
Звʼязування параметрів
З метою уникнення SQL-інʼєкцій і поліпшення продуктивності при виконанні повторно використовуваних SQL-виразів, ми можемо «готувати» SQL-вираз з маркерами параметрів (placeholder), які в процесі привʼязки будуть замінюватися на реальні значення.
Маркери параметрів можуть бути іменованими (унікальні маркери) або неіменовані (знаки питання). Для заміни маркерів на реальні значення потрібно викликати CDbCommand::bindParam() або CDbCommand::bindValue(). Додавати лапки до параметрів немає необхідності, тому що використовуваний драйвер бази даних все зробить сам. Звʼязування параметрів повинно бути завершено до виконання SQL-виразу.
// вираз SQL з двома маркерами «:username» та «:email» $sql="INSERT INTO tbl_user(username, email) VALUES(:username,:email)"; $command=$connection->createCommand($sql); // міняємо маркер «:username» на відповідне значення імені користувача $command->bindParam(":username",$username,PDO::PARAM_STR); // міняємо маркер «:email» на відповідне значення електронної пошти $command->bindParam(":email",$email,PDO::PARAM_STR); $command->execute(); // вставляємо наступний рядок з новими параметрами $command->bindParam(":username",$username2,PDO::PARAM_STR); $command->bindParam(":email",$email2,PDO::PARAM_STR); $command->execute();
Методи bindParam() і bindValue() дуже схожі, єдина відмінність полягає в тому, що перший звʼязує параметр до посилання на змінну PHP, а другий - до значення. Для параметрів, що представляють великий обсяг даних, з точки зору продуктивності краще використовувати метод bindParam().
Детальніше про звʼязування параметрів можна дізнатися у відповідній документації PHP.
Звʼязування полів
При обробці результатів запиту ми також можемо привʼязати поля таблиці до змінних PHP. Це дозволяє автоматично підставляти значення змінних для кожного рядка:
$sql="SELECT username, email FROM tbl_user"; $dataReader=$connection->createCommand($sql)->query(); // привʼязуємо перше поле (username) до змінної $username $dataReader->bindColumn(1,$username); // привʼязуємо друге поле (email) до змінної $email $dataReader->bindColumn(2,$email); while($dataReader->read()!==false) { // змінні $username та $email отримують значення полів username і email поточного рядка }
Використання префікса таблиць
Yii забезпечує вбудовану підтримку префіксів таблиць. Префікс таблиць - це рядок, що стоїть на початку імен таблиць в поточній підключеній БД. В основному використовується на загальному хостингу, де до однієї БД підключається декілька додатків, що використовують різні префікси таблиць для розрізнення приналежності таблиць до додатка. Наприклад, один додаток використовує префікс tbl_
, а інший - yii_
.
Для використання префікса таблиць, встановіть у властивості CDbConnection::tablePrefix бажаний префікс таблиць. Потім, у SQL виразі, використовуйте {{TableName}}
для посилання на імена таблиць, де TableName
- імʼя таблиці без префікса. Наприклад, якщо БД містить таблицю з імʼям tbl_user
, де tbl_
- це префікс таблиць, тоді ми можемо використовувати наступний код для запиту користувачів:
$sql='SELECT * FROM {{user}}'; $users=$connection->createCommand($sql)->queryAll();