Методи pdo. Чому слід використовувати PDO для доступу до баз даних? Обробка результату, методи FETCH та FETCHALL

Термін PDOє скороченням поняття PHP Data Objects. Як можна судити за назвою, ця технологія дозволяє працювати з вмістом бази даних через об'єкти.

Чому не myqli чи mysql?

Найчастіше, щодо нових технологій, постає питання їх переваг перед старими-добрими та перевіреними інструментами, а також перекладу на них поточних та старих проектів.

Об'єктна орієнтованість PDO

PHPрозвивається дуже активно і прагне стати одним з найкращих інструментів для швидкої розробки веб-додатків як масового, так і корпоративного рівня.

Говорячи про PHP, матимемо на увазі сучасний об'єктно-орієнтований PHP, що дозволяє писати універсальний код, зручний для тестування та повторного використання.

Використання PDOдозволяє винести роботу з базою даних на об'єктно-орієнтований рівень та покращити переносимість коду. Насправді, використання PDOне так складно, як можна було б подумати.

Абстракція

Уявимо, що ми вже тривалий час розробляємо додаток з використанням MySQL. І ось, одного разу, виникає необхідність замінити MySQLна PostgreSQL.

Як мінімум, нам доведеться замінити усі виклики mysqli_connect() (mysql_connect())на pg_connect()та, за аналогією, інші функції, що використовуються для запиту та обробки даних.

При використанні PDO, ми обмежимося зміною декількох параметрів у конфігураційних файлах.

Зв'язування параметрів

Використання пов'язаних параметрів надає велику гнучкість у складанні запитів та дозволяє покращити захист від SQLін'єкцій.

Отримання даних у вигляді об'єктів

Ті, хто вже використовує ORM(object-relational mapping - об'єктно-реляційне відображення даних), наприклад, Doctrine, знають зручність представлення даних із таблиць БД як об'єктів. PDOдозволяє отримувати дані у вигляді об'єктів та без використання ORM.

Розширення mysql більше не підтримується

Підтримка розширення mysqlостаточно видалена з нового PHP 7. Якщо ви плануєте переносити проект на нову версію PHPвже зараз слід використовувати в ньому, як мінімум, mysqli. Звичайно, краще починати використовувати PDOякщо ви ще не зробили цього.

Мені здається, що цих причин достатньо для відміни ваг у бік використання PDO. Тим більше не потрібно нічого додатково встановлювати.

Перевіряємо наявність PDO у системі

Версії PHP 5.5і вище, найчастіше, вже містити розширення для роботи з PDO. Для перевірки достатньо виконати у консолі просту команду:

php-i | grep "pdo"

Тепер відкриємо його в будь-якому браузері та знайдемо потрібні дані пошуком по рядку PDO.

Знайомимося з PDO

Процес роботи з PDOне надто відрізняється від традиційного. Загалом, процес використання PDOвиглядає так:

  1. Підключення до бази даних;
  2. За необхідності, підготовка запиту та зв'язування параметрів;
  3. Виконання запиту.

Підключення до бази даних

Для підключення до бази даних необхідно створити новий об'єкт PDOі передати йому ім'я джерела даних, так само відомого як DSN.

У загальному випадку, DSNскладається з імені драйвера, відокремленого двокрапкою від рядка підключення, специфічного для кожного драйвера PDO.

Для MySQL, підключення виконується так:

$connection = новий PDO("mysql:host=localhost;dbname=mydb;charset=utf8", "root", "root");

$connection = новий PDO ( "mysql:host=localhost;dbname=mydb;charset=utf8", "root", "root");

В даному випадку, DSNмістить ім'я драйвера mysql, вказівка ​​хоста (можливий формат host=ІМ'Я_ХОСТА:ПОРТ), ім'я бази даних, кодування, ім'я користувача MySQLта його пароль.

Запити

На відміну від mysqli_query(), в PDOє два типи запитів:

  • Повертають результат ( select, show);
  • Не повертають результат ( insert, deteleта інші).

Насамперед, розглянемо другий варіант.

Виконання запитів

Розглянемо приклад виконання запиту з прикладу insert.

$connection->exec("INSERT INTO users VALUES (1, "somevalue"");

$connection -> exec();

Звичайно, цей запит повертає кількість порушених рядків і побачити його можна наступним чином.

$affectedRows = $connection->exec("INSERT INTO users VALUES (1, "somevalue""); echo $affectedRows;

$affectedRows = $connection -> exec ( "INSERT INTO users VALUES (1, "somevalue"") ;

echo $affectedRows ;

Отримання результатів запиту

У разі використання mysqli_query()код міг би бути наступним.

$result = mysql_query("SELECT * FROM users"); while($row = mysql_fetch_assoc($result)) ( echo $row["id"] . " " . $row["name"]; )

$result = mysql_query ("SELECT * FROM users");

while ($row = mysql_fetch_assoc ($result ) ) (

Для PDO, код буде простіше та лаконічніше.

foreach($connection->query("SELECT * FROM users") as $row) ( echo $row["id"] . " " . $row["name"]; )

foreach ($connection -> query ("SELECT * FROM users" ) as $row ) (

echo $row [ "id" ] . " ". $row ["name"];

Режими отримання даних

як і в mysqli, PDOдозволяє отримувати дані у різних режимах. Для визначення режиму, клас PDOмістить відповідні константи.

  • PDO:: FETCH_ASSOC- Повертає масив, індексований на ім'я стовпця в таблиці бази даних;
  • PDO:: FETCH_NUM- Повертає масив, індексований за номером стовпця;
  • PDO:: FETCH_OBJ- Повертає анонімний об'єкт з іменами властивостей, що відповідають іменам стовпців. Наприклад, $row->id міститиме значення зі стовпця id.
  • PDO:: FETCH_CLASS— повертає новий екземпляр класу зі значеннями властивостей, що відповідають даним із рядка таблиці. Якщо вказано параметр PDO:: FETCH_CLASSTYPE(наприклад PDO:: FETCH_CLASS | PDO:: FETCH_CLASSTYPE

), ім'я класу буде визначено значення першого стовпця.Примітка

: це не повний список, всі можливі константи та варіанти їх комбінації доступні в документації.

Приклад отримання асоціативного масиву:

$statement = $connection->query("SELECT * FROM users"); while($row = $statement->fetch(PDO::FETCH_ASSOC)) ( echo $row["id"] . " " . $row["name"]; )

$statement = $connection ->

echo $row [ "id" ] . " ". $row ["name"];

), ім'я класу буде визначено значення першого стовпця. while ($row = $statement -> fetch (PDO:: FETCH_ASSOC ) ) ( : Рекомендується завжди вказувати режим вибірки, оскільки режим PDO:: FETCH_BOTH

вимагатиме вдвічі більше пам'яті — фактично, буде створено два масиви, асоціативний та звичайний. PDO:: FETCH_CLASSРозглянемо використання режиму вибірки . Створимо клас:

User

class User ( protected $id; protected $name; public function getId() ( return $this->id; ) public function setId($id) ( $this->id = $id; ) public function getName() ( return $this->name; ) public function setName($name) ( $this->name = $name; ) )

class User

protected $id;

protected $name;

public function getId()

return $this -> id ;

public function setId ($id )

$this -> id = $id;

public function getName()

return $this -> name ;

public function setName ($name)

$this -> name = $name;

Тепер виберемо дані та відобразимо дані за допомогою методів класу:

$statement = $connection->query("SELECT * FROM users"); while($row = $statement->fetch(PDO::FETCH_CLASS, "User")) ( echo $row->getId() . " " . $row->getName(); )

$statement = $connection -> query ("SELECT * FROM users");

while ($row = $statement -> fetch (PDO:: FETCH_CLASS , "User" ) ) (

echo $row -> getId() . " ". $row -> getName();

Підготовлені запити та зв'язування параметрів PDOДля розуміння суті та всіх переваг зв'язування параметрів потрібно детальніше розглянути механізми . При виклику$statement -> query () PDOпідготує запит, виконає його та поверне результат.

При виклику $connection -> prepare ()створюється підготовлений запит. Підготовлені запити — це здатність системи керування базами даних отримати шаблон запиту, скомпілювати його та виконати після отримання значень змінних, використаних у шаблоні. Подібним чином працюють шаблонізатори Smartyі Twig.

При виклику $statement -> execute ()передаються значення для підстановки шаблон запиту і СУБД виконує запит. Ця дія аналогічна виклику функції шаблонизатора render().

Приклад використання підготовлених запитів у PHP PDO:

У коді вище підготовлено запит вибірки запису з полем idрівним значенню, яке буде підставлено замість : id. На цьому етапі СУБД виконає аналіз та компіляцію запиту, можливо з використанням кешування (залежить від налаштувань).

Тепер потрібно передати відсутній параметр і виконати запит:

$ id = 5; $statement->execute([ ":id" => $id ]);

Переваги використання пов'язаних параметрів

Можливо, після розгляду механізму роботи підготовлених запитів та пов'язаних параметрів переваги їх використання стали очевидними.

PDOнадає зручну можливість екранування даних користувача, наприклад, такий код більше не потрібен:

Натомість, тепер доцільно робити так:

Можна навіть вкоротити код, використовуючи нумеровані параметри замість іменованих:

У той же час використання підготовлених запитів дозволяє покращити продуктивність при багаторазовому використанні запиту по одному шаблону. Приклад вибірки п'яти випадкових користувачів із бази даних:

$numberOfUsers = $connection->query("SELECT COUNT(*) FROM users")->fetchColumn(); $users = ; $statement = $connection->prepare("SELECT * FROM users WHERE id = ? LIMIT 1"); for ($i = 1; $i<= 5; $i++) { $id = rand(1, $numberOfUsers); $users = $statement->execute([$id])->fetch(PDO::FETCH_OBJ); )

$numberOfUsers = $connection -> query ("SELECT COUNT(*) FROM users" ) -> fetchColumn() ;

$users = ;

for ($i = 1; $i<= 5 ; $i ++ ) {

$id = rand (1, $numberOfUsers);

$users = $statement -> execute ([$id]) -> fetch (PDO:: FETCH_OBJ);

При виклику методу prepare ()СУБД проведе аналіз і скомпілює запит, при необхідності використовує кешування. Пізніше, у циклі for, відбувається лише вибірка даних із зазначеним параметром. Такий підхід дозволяє швидше отримати дані, зменшивши час роботи програми.

При отриманні загальної кількості користувачів у базі даних було використано метод fetchColumn (). Цей метод дозволяє отримати значення одного стовпця і є корисним при отриманні значень скалярних, таких як кількість, сума, максимально або мінімальне значення.

Пов'язані значення та оператор IN

Часто, при початку роботи з PDO, виникають труднощі з оператором IN. Наприклад, уявімо, що користувач вводить кілька імен, розділених комами. Введення користувача зберігається в змінній $names.

З'єднання з базою данихвстановлюється тоді, коли створюється екземпляр класу PDO. Не має значення, який драйвер Ви хочете використати; Вам завжди потрібно використовувати клас PDO. Його конструктор приймає параметри для визначення джерела бази даних (відомий як DSN), і необов'язкові параметри для імені користувача і пароля.

З'єднання з MySQL:

$dbh = новий PDO("mysql:host=localhost;dbname=test", $user, $pass);

Якщо виникнуть помилки з'єднання, то буде викинуто виняток: об'єкт класу PDOException. Ви можете зловити його, якщо захочете опрацювати цю ситуацію, або можете залишити його для глобального обробника винятків, який встановлюється через set_exception_handler().

Обробка помилок з'єднання:

try ( $dbh = new PDO("mysql:host=localhost;dbname=test", $user, $pass); foreach($dbh->query('SELECT * from FOO') as $row) ( print_r($ row); ) $dbh = null; ) catch (PDOException $e) ( die("Помилка! Все. Приїхали... ".$e->getMessage()); )

Увага:Якщо Ви не впіймаєте виняток, кинуте конструктором PDO, стандартна дія, здійснена двигуном zend, повинна зупинити скрипт і показати трасування. Ця хрень видасть всі Ваші інтимні подробиці спілкування з цією базою. Тобто покаже докладні деталі з'єднання з базою даних, включаючи ім'я користувача та пароль!На Вашому совісті зловити цей виняток, або явно (через твердження try catch), або неявно через set_exception_handler().

Після успішного з'єднання з базою даних воно залишається активним протягом усього життя екземпляра об'єкта PDO. Щоб закрити з'єднання, Ви повинні зруйнувати об'єкт, гарантуючи, що всі посилання, що залишаються, видалені – зробити це можна, надавши значення NULL змінної, яка містить об'єкт. Якщо Ви не зробите цього явно, PHP автоматично закриє з'єднання, коли скрипт завершить роботу.

Закриття з'єднання:

$dbh = новий PDO("mysql:host=localhost;dbname=test", $user, $pass);

Багато веб-програм виграють від створення постійних з'єднань із серверами баз даних. Постійні з'єднання не закриваються, коли скрипт завершує роботу, а кешуються і знову використовуються, коли інший скрипт просить з'єднання, використовуючи самі облікові дані для з'єднання. Кеш постійних з'єднань дозволяє уникнути накладних витрат на встановлення нового з'єднання щоразу, коли скрипт повинен спілкуватися з базою даних, внаслідок чого веб-програми працюють швидше.

Встановлення постійного з'єднання:

$dbh = новий PDO("mysql:host=localhost;dbname=test", $user, $pass, array(PDO::ATTR_PERSISTENT => true));

Майте на увазі:Якщо Ви хочете використовувати постійне з'єднання, Ви повинні встановити PDO::ATTR_PERSISTENTу масиві опцій драйвера, що передається конструктору класу PDO. Встановлюючи цей атрибут через PDO::setAttribute() після створення екземпляра об'єкта, драйвер не використовуватиме постійні зв'язки. А так само, в цьому випадку вам не вдасться розширити клас PDOStatement для якихось своїх потреб, тобто. не можна буде встановити: PDO::ATTR_STATEMENT_CLASS

  • Переклад

Багато PHP-розробників звикли використовувати для роботи з базами даних розширення mysql і mysqli. Але з версії 5.1 в PHP існує зручніший спосіб - PHP Data Objects. Цей клас, скорочено іменований PDO, надає методи роботи з об'єктами і prepared statements , які помітно підвищать вашу продуктивність!

Введення у PDO

«PDO – PHP Data Objects – це прошарок, який пропонує універсальний спосіб роботи з кількома базами даних.»

Турботу про особливості синтаксису різних СУБД вона залишає розробнику, але робить процес перемикання між платформами набагато менш болючим. Нерідко для цього потрібно лише змінити рядок підключення до бази даних.


Ця стаття написана для людей, які користуються mysql та mysqli, щоб допомогти їм у переході на більш потужний та гнучкий PDO.

Підтримка СУБД

Це розширення може підтримувати будь-яку систему керування базами даних, для якої існує PDO-драйвер. На момент написання статті доступні такі драйвери:
  • PDO_CUBRID (CUBRID)
  • PDO_DBLIB (FreeTDS / Microsoft SQL Server / Sybase)
  • PDO_FIREBIRD (Firebird/Interbase 6)
  • PDO_IBM (IBM DB2)
  • PDO_INFORMIX (IBM Informix Dynamic Server)
  • PDO_MYSQL (MySQL 3.x/4.x/5.x)
  • PDO_OCI (Oracle Call Interface)
  • PDO_ODBC (ODBC v3 (IBM DB2, unixODBC and win32 ODBC))
  • PDO_PGSQL (PostgreSQL)
  • PDO_SQLITE (SQLite 3 і SQLite 2)
  • PDO_SQLSRV (Microsoft SQL Server)
  • PDO_4D (4D)
Втім, не всі є на вашому сервері. Побачити список доступних драйверів можна так:
print_r(PDO::getAvailableDrivers());

Підключення

Способи підключення до різних СУБД можуть відрізнятися незначно. Нижче наведено приклади підключення до найпопулярніших із них. Можна помітити, що перші три мають ідентичний синтаксис, на відміну SQLite.
try ( # MS SQL Server і Sybase через PDO_DBLIB $DBH = new PDO("mssql:host=$host;dbname=$dbname", $user, $pass); $DBH = new PDO("sybase:host=$host ;dbname=$dbname", $user, $pass); # MySQL через PDO_MYSQL $DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass); # SQLite $DBH = new PDO("sqlite:my/database/path/database.db"); ) catch(PDOException $e) ( echo $e->getMessage(); )
Будь ласка, зверніть увагу на блок try/catch – завжди варто обертати у нього всі свої PDO-операції та використовувати механізм виключень (про це трохи далі).

$DBH розшифровується як "database handle" і буде використовуватися протягом усієї статті.

Закрити будь-яке підключення можна шляхом перевизначення його змінної null.
# закриває підключення $DBH = null;
Більше інформації на тему відмінних опцій різних СУБД та методи підключення до них можна знайти на php.net.

Винятки та PDO

PDO вміє викидати винятки при помилках, тому все має бути в блоці try/catch. Відразу після створення підключення, PDO можна перевести в будь-який із трьох режимів помилок:
$DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT); $DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); $DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
Але варто зауважити, що помилка при спробі з'єднання завжди викликатиме виняток.

PDO::ERRMODE_SILENT

Це стандартний режим. Приблизно те саме ви, швидше за все, використовуєте для відлову помилок у розширеннях mysql і mysqli. Наступні два режими більше підходять для програмування DRY.

PDO::ERRMODE_WARNING

Цей режим викличе стандартний Warning та дозволить скрипту продовжити виконання. Зручний при налагодженні.

PDO::ERRMODE_EXCEPTION

У більшості ситуацій цей тип контролю виконання скрипта є кращим. Він викидає виняток, що дозволяє вам спритно обробляти помилки та приховувати педантичну інформацію. Як, наприклад, тут:
# підключаємося до бази даних try ( $DBH = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass); $DBH->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION) ; # Чорт! Набрав DELECT замість SELECT! $DBH->prepare("DELECT name FROM people")->execute(); .txt", $e->getMessage(), FILE_APPEND); )
У SQL-вираженні є синтаксична помилка, яка викликає виняток. Ми можемо записати деталі помилки у лог-файл та людською мовою натякнути користувачеві, що щось трапилося.

Insert та Update

Вставка нових та оновлення існуючих даних є одними з найчастіших операцій із БД. У випадку PDO цей процес зазвичай складається з двох кроків. (У наступній секції все стосується як UPDATE, так і INSERT)


Тривіальний приклад вставки нових даних:
# STH означає "Statement Handle" $STH = $DBH->prepare("INSERT INTO folks (first_name) values ​​("Cathy")"); $STH->execute();
Взагалі-то можна зробити те саме одним методом exec(), але двокроковий спосіб дає всі переваги prepared statements. Вони допомагають у захисті від SQL-ін'єкцій, тому є сенс їх використовувати навіть при одноразовому запиті.

Prepared Statements

Використання попереднього стану зміцнює захист від SQL-ін'єкцій.

Prepared statement - це заздалегідь скомпільований SQL-вираз, який може бути багаторазово виконано шляхом надсилання серверу лише різних наборів даних. Додатковою перевагою є неможливість провести SQL-ін'єкцію через дані, що використовуються у placeholder'ах.

Нижче наведено три приклади prepared statements.
# без placeholders - двері SQL-ін'єкцій відчинені! $STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values ​​($name, $addr, $city)"); # безіменні placeholders $STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values ​​(?, ?, ?)"); # іменні placeholders $STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values ​​(:name, :addr, :city)");
Перший приклад тут лише порівняння, його варто уникати. Різниця між безіменними та іменними placeholder'ами в тому, як ви передаватимете дані в prepared statements.

Безіменні placeholder'и

# призначаємо змінні кожному placeholder, з індексами від 1 до 3 $ STH-> bindParam (1, $ name); $STH->bindParam(2, $addr); $STH->bindParam(3, $city); # вставляємо один рядок $name = "Daniel" $addr = "1 Wicked Way"; $ city = "Arlington Heights"; $STH->execute(); # вставляємо ще один рядок, вже з іншими даними $name = "Steve" $addr = "5 Circle Drive"; $ city = "Schaumburg"; $STH->execute();
Тут два кроки. На першому ми призначаємо всім placeholder'ам змінні (рядки 2-4). Потім призначаємо цим змінним значення та виконуємо запит. Щоб надіслати новий набір даних, просто змініть значення змінних і виконайте запит ще раз.

Якщо у вашому SQL-вираженні багато параметрів, то призначати кожному змінною дуже незручно. У таких випадках можна зберігати дані в масиві та передавати його:
# набір даних, які ми вставлятимемо $data = array("Cathy", "9 Dark and Twisty Road", "Cardiff"); $STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values ​​(?, ?, ?)"); $STH->execute($data);
$data вставиться на місце першого placeholder'а, $data - на місце другого і т.д. Але будьте уважні: якщо ваші індекси збиті, це не працюватиме.

Іменні placeholder'и

# першим аргументом є ім'я placeholder'а # його прийнято починати з двокрапки # хоча працює і без них $ STH-> bindParam (": name", $ name);
Тут також можна передавати масив, але він має бути асоціативним. У ролі ключів мають виступати, як можна здогадатися, імена placeholder'ів.
# дані, які ми вставляємо $data = array("name" => "Cathy", "addr" => "9 Dark and Twisty", "city" => "Cardiff"); $STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values ​​(:name, :addr, :city)"); $STH->execute($data);
Одним із зручностей використання іменних placeholder'ів є можливість вставки об'єктів безпосередньо до бази даних, якщо назви властивостей збігаються з іменами параметрів. Вставку даних, наприклад, ви можете виконати так:
# клас для простенького об'єкта class person ( public $name; public $addr; public $city; function __construct($n,$a,$c) ( $this->name = $n; $this->addr = $a ; $this->city = $c; ) # так далі... ) $cathy = new person("Cathy","9 Dark and Twisty","Cardiff"); а тут найцікавіше $STH = $DBH->prepare("INSERT INTO folks (name, addr, city) values ​​(:name, :addr, :city)"); $STH->execute((array)$cathy);
Перетворення об'єкта на масив при execute() призводить до того, що властивості вважаються ключами масиву.

Вибірка даних



Дані можна одержати за допомогою методу ->fetch(). Перед викликом бажано явно вказати, в якому вигляді вони вам потрібні. Є кілька варіантів:
  • PDO::FETCH_ASSOC:повертає масив із назвами стовпців у вигляді ключів
  • PDO::FETCH_BOTH (за замовчуванням):повертає масив з індексами як у вигляді назв стобців, так і їх порядкових номерів
  • PDO::FETCH_BOUND:надає значення стовпців відповідним змінним, заданим за допомогою методу -> bindColumn()
  • PDO::FETCH_CLASS:надає значення стовпців відповідним властивостям зазначеного класу. Якщо для якогось стовпця немає властивості, воно буде створено
  • PDO::FETCH_INTO:оновлює існуючий екземпляр вказаного класу
  • PDO::FETCH_LAZY:поєднує в собі PDO::FETCH_BOTH і PDO::FETCH_OBJ
  • PDO::FETCH_NUM:повертає масив із ключами у вигляді порядкових номерів стовпців
  • PDO::FETCH_OBJ:повертає анонімний об'єкт із властивостями, що відповідають іменам стовпців
Насправді вам зазвичай вистачить трьох: FETCH_ASSOC, FETCH_CLASS, і FETCH_OBJ. Для встановлення формату даних використовується наступний синтаксис:
$STH->setFetchMode(PDO::FETCH_ASSOC);
Також можна встановити його безпосередньо при виклику методу ->fetch().

FETCH_ASSOC

У цьому форматі створюється асоціативний масив із назвами стовпців як індексів. Він повинен бути знайомий тим, хто використовує розширення mysql/mysqli.
# оскільки це звичайний запит без placeholder'ів, # можна відразу використовувати метод query() $STH = $DBH->query("SELECT name, addr, city from folks"); # встановлюємо режим вибірки $STH->setFetchMode(PDO::FETCH_ASSOC); while($row = $STH->fetch()) ( echo $row["name"] . "\n"; echo $row["addr"] . "\n"; echo $row["city"] . "\n";
Цикл while() перебере результат запиту.

FETCH_OBJ

Цей тип отримання даних створює екземпляр класу std для кожного рядка.
# створюємо запит $STH = $DBH->query("SELECT name, addr, city from folks"); # вибираємо режим вибірки $STH->setFetchMode(PDO::FETCH_OBJ); # виводимо результат while($row = $STH->fetch()) ( echo $row->name . "\n"; echo $row->addr . "\n"; echo $row->city . "\ n"; )

FETCH_CLASS

При використанні fetch_class дані заносяться до екземплярів зазначеного класу. У цьому значення призначаються властивостям об'єкта ДО виклику конструктора. Якщо властивості з іменами, що відповідають назвам стовпців, не існують, вони будуть створені автоматично (з областю видимості public).

Якщо ваші дані потребують обов'язкової обробки відразу після їх отримання з бази даних, її можна реалізувати у конструкторі класу.

Наприклад візьмемо ситуацію, коли вам потрібно приховати частину адреси проживання людини.
class secret_person ( public $name; public $addr; public $city; public $other_data; function __construct($other = "") ( $this->addr = preg_replace("//", "x", $this-> addr); $this->other_data = $other;
При створенні об'єкта всі латинські літери в нижньому регістрі повинні замінити x. Перевіримо:
$STH = $DBH->query("SELECT name, addr, city from folks"); $STH->setFetchMode(PDO::FETCH_CLASS, "secret_person"); while($obj = $STH->fetch()) ( echo $obj->addr; )
Якщо у базі даних адреса виглядає як '5 Rosebud', то на виході вийде '5 Rxxxxxx'.

Звичайно, іноді потрібно, щоб конструктор викликався ПЕРЕД присвоєнням значень. PDO таке також дозволяє.
$STH->setFetchMode(PDO::FETCH_CLASS | PDO::FETCH_PROPS_LATE, "secret_person");
Тепер, коли ви доповнили попередній приклад додатковою опцією (PDO::FETCH_PROPS_LATE), адреса не буде змінюватися, оскільки після запису значень нічого не відбувається.

Нарешті, за необхідності можна передавати конструктору аргументи безпосередньо під час створення об'єкта:
$STH->setFetchMode(PDO::FETCH_CLASS, "secret_person", array("stuff"));
Можна навіть передавати різні аргументи кожному об'єкту:
$i = 0; while($rowObj = $STH->fetch(PDO::FETCH_CLASS, "secret_person", array($i))) ( // щось робимо $i++; )

Інші корисні методи

Хоча ця стаття не може (і не намагається) охопити всі аспекти роботи з PDO (це величезний модуль!), Залишити без згадки наступні кілька функцій не можна.
$DBH->lastInsertId();
Метод -> lastInsertId() повертає id останнього вставленого запису. Він завжди викликається у об'єкта бази даних (у статті він називається $DBH), а не об'єкта з виразом ($STH).
$DBH->exec("DELETE FROM folks WHERE 1"); $DBH->exec("SET time_zone = "-8:00"");
Метод ->exec() використовується для операцій, які не повертають жодних даних, крім кількості порушених записів.
$safe = $DBH->quote($unsafe);
Метод ->quote() ставить лапки в рядкових даних в такий спосіб, що їх стає безпечно використовувати у запитах. Стане в нагоді, якщо ви не використовуєте prepared statements.
$rows_affected = $STH->rowCount();
Метод -> rowCount () повертає кількість записів, які взяли участь у операції. На жаль, ця функція відмовлялася працювати із SELECT-запитами аж до PHP 5.1.6. Якщо оновити версію PHP неможливо, кількість записів можна отримати так:
$sql = "SELECT COUNT(*) FROM folks"; if ($STH = $DBH->query($sql)) ( # перевіряємо кількість записів if ($STH->fetchColumn() > 0) ( # робимо тут повноцінну вибірку, тому що дані знайдені! ) else ( # виводимо повідомлення про те, що даних, що задовольняють запиту, не знайдено ) )

Висновок

Сподіваюся, цей матеріал допоможе комусь із вас здійснити міграцію з розширень mysql та mysqli.


3 червня 2018 Андрій Чернишов Переклад Туторіал 1737 0

PDO є акронімом для PHP Data Objects: це PHP розширення для роботи з базами даних, використовуючи об'єкти. Одна з його переваг полягає в тому, що воно не прив'язане безпосередньо до певної бази даних: його інтерфейс дозволяє отримати доступ до різних середовищ, включаючи: MySQL, SQLite, PostgreSQL, Microsoft SQL Server.

Цей посібник має на меті надати повний огляд PDO та провести читача крок за кроком від створення та підключення до бази даних, до вибору найбільш підходящих методів вибірки, демонструючи як створити підготовлені запити та описуючи можливі режими помилок.

Створення тестової бази даних та таблиці

Насамперед, ми створимо базу даних:

CREATE DATABASE solar_system; GRANT ALL PRIVILEGES ON solar_system.* TO "testuser"@"localhost" IDENTIFIED BY "testpassword";

Ми видали користувачеві testuser всі привілеї в базі даних solar_system , використовуючи testpassword для пароля. Тепер давайте створимо таблицю і заповнимо її якоюсь інформацією:

USE solar_system; CREATE TABLE planets (id TINYINT(1) NOT NULL AUTO_INCREMENT, PRIMARY KEY(id), name VARCHAR(10) NOT NULL, color VARCHAR(10) NOT NULL); INSERT INTO planets(name, color) VALUES("earth", "blue"), ("mars", "red"), ("jupiter", "strange");

Опис з'єднання DSN (Data Source Name)

Тепер, коли у нас є база даних, ми повинні встановити DSN . DSN розшифровується як Data Source Name і є набором інформації, необхідної для підключення до бази даних, DSN має форму рядка. Синтаксис відрізняється залежно від бази даних, підключення до якої потрібно, але оскільки ми використовуємо MySQL/MariaDB, нам потрібно задати такі:

  • Тип драйвера, що використовується для підключення;
  • Ім'я комп'ютера-хоста, на якому запущено базу даних;
  • Порт для підключення (необов'язково);
  • Назва бази даних;
  • Кодування (необов'язково).

Формат рядка в нашому випадку буде таким (ми зберігатимемо його в змінній $dsn):

$dsn = "mysql:host=localhost;port=3306;dbname=solar_system;charset=utf8";

Насамперед ми задали database prefix або префікс бази даних. У цьому випадку, оскільки ми підключаємося до бази даних типу MySQL/MariaDB, ми використовуємо mysql. Потім ми відокремили префікс від решти рядка двокрапкою і кожна наступна секція відокремлена від інших крапкою з комою.

У наступних двох секціях ми задали hostname , на якому запущена база даних та port, що використовується для підключення. Якщо порт не вказано, буде використано порт за замовчуванням, у цьому випадку це 3306 . Відразу після database name вказується charset.

Створення PDO об'єкту

Тепер, коли наш DSN готовий, ми розпочнемо створення PDO object . Конструктор PDO використовує рядок DSN як перший параметр, ім'я користувача бази даних другим параметром, пароль – третім та необов'язковий масив налаштувань – четвертим.

$options = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ]; $pdo = новий PDO($dsn, "testuser", "testpassword", $options);

Налаштування також можна задати і після створення об'єкта, використовуючи метод SetAttribute() :

$pdo->SetAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

Налаштування PDO побачення при помилках

Погляньмо на деякі опції доступні для PDO::ATTR_ERRMODE . Ці опції є дуже важливими, тому що вони визначають поведінку PDO у разі виникнення помилок. Можливі опції:

PDO::ERRMODE_SILENT

Опція за промовчанням. PDO просто видасть код помилки та повідомлення про помилку. Їх можна буде отримати за допомогою методів errorCode() і errorInfo() .

PDO::ERRMODE_EXCEPTION

Ця опція, як на мене, рекомендована для використання. З її допомогою, окрім видачі коду помилки та інформації, PDO видасть виняток PDOException, яке перерве хід виконання скрипта, а ще вона корисна при PDO transactions (їх ми розглянемо трохи пізніше).

PDO::ERRMODE_WARNING

З цією опцією, PDO видасть код помилки та повідомлення про неї, як і за PDO::ERRMODE_SILENT , але ще й покаже попередження WARNING , яке не перериває роботу скрипта.

Налаштування методу вибірки за умовчанням

Ще одне важливе налаштування регулюється за допомогою константи PDO::DEFAULT_FETCH_MODE . Вона дозволяє за промовчанням налаштувати роботу методу fetch() , який буде використовуватися для отримання результатів запиту. Ось найчастіше використовувані опції:

PDO::FETCH_BOTH

При його використанні отримані результати будуть індексовані і за цілими числами, і за назвами стовпців. Використання його у методі отримання низки з таблиці планет видасть нам такі результаты:

$stmt = $pdo->query("SELECT * FROM planets"); $results = $stmt->fetch(PDO::FETCH_BOTH);

Array ( => 1 => 1 => earth => earth => blue => blue)

PDO::FETCH_ASSOC

З цією константою результати будуть записані в асоціативний масив, в якому кожен ключ буде ім'ям стовпця, а кожне значення - позначати певне значення в ряду:

$stmt = $pdo->query("SELECT * FROM planets"); $results = $stmt->fetch(PDO::FETCH_ASSOC);

Array ( => 1 => earth => blue)

PDO::FETCH_NUM

Використовуючи PDO::FETCH_NUM константу ми отримаємо 0-indexed array:

Array ( => 1 => earth => blue)

PDO::FETCH_COLUMN

Ця константа корисна для отримання лише значень зі стовпця, а метод поверне всі результати усередині простого одновимірного масиву. Наприклад, такий запит:

$stmt = $pdo->query("SELECT name FROM planets");

В результаті:

Array ( => earth => mars => jupiter)

PDO::FETCH_KEY_PAIR

Ця константа корисна для отримання лише значень зі стовпця, а метод поверне всі результати усередині простого одновимірного масиву. Наприклад, такий запит:

Ця константа корисна, коли потрібно отримати значення двох стовпців. Метод fetchAll() поверне результати як асоціативного масиву. У цьому масиві дані з першого стовпця будуть вказані у формі ключів, а з другого – як значення:

$stmt = $pdo->query("SELECT name, color FROM planets"); $result = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);

Array ( => blue => red => strange)

PDO::FETCH_OBJECT

При використанні константи PDO::FETCH_OBJECT , буде створено anonymous object за кожен отриманий ряд. Його (публічні) властивості будуть названі так само, як і стовпці, а результати запиту будуть використані як значення. Використання цього методу для того ж запиту, як і вище, призведе до наступного результату:

$results = $stmt->fetch(PDO::FETCH_OBJ);

stdClass Object ( => earth => blue)

PDO::FETCH_CLASS

Використовуючи fetch() з PDO::FETCH_CLASS необхідно використовувати метод setFetchMode() на об'єкт, перед тим як намагатися отримати дані, наприклад:

$stmt = $pdo->query("SELECT name, color FROM planets"); $stmt->setFetchMode(PDO::FETCH_CLASS, "Planet");

Ми ставимо константу PDO::FETCH_CLASS як перший аргумент методу setFetchMode() та назву класу, використану для створення об'єкта (у нашому випадку «Planet») – другим аргументом. Тепер запускаємо код:

$planet = $stmt->fetch();

Повинен вийти об'єкт Planet:

Var_dump($planet);

Planet Object ( => earth => blue)

Зауважте, як значення, отримані із запиту, були призначені до відповідних характеристик об'єкта, незважаючи на те, що вони приватні.

Призначення характеристик після створення об'єкта

Клас «Planet», не мав жодного певного конструктора, так що проблем з призначенням характеристик не виникло; Але що якщо клас має конструктор, в якому характеристики задаються і змінюються? Оскільки значення призначено до запуску конструктора, вони будуть перезаписані.

PDO допомагає надати константу FETCH_PROPS_LATE: при її використанні значення будуть призначені після створення об'єкта. Приклад:

Class Planet ( private $name; private $color; public function __construct($name = moon, $color = grey) ( $this->name = $name; $this->color = $color; ) public function setName($ planet_name) ( $this->name = $planet_name; ) public function setColor($planet_color) ( $this->color = $planet_color; ) public function getName() ( return $this->name; ) public function getColor() ( return $this->color; ) )

Ми змінили наш клас Planet, створивши конструктор, який візьме два аргументи: name name та сolor. Ці аргументи мають базові значення: moon і gray, що означає, що, якщо інших значень не буде задано, будуть ці.

У цьому випадку, якщо ми не використовуємо FETCH_PROPS_LATE , не важливо, які значення будуть отримані з бази даних, всі характеристики залишаться базовими, тому що в процесі створення об'єкта вони будуть перезаписані. Для того, щоб це перевірити, запустимо наступний запит:

$stmt = $pdo->query("SELECT name, color FROM solar_system WHERE name = "earth""); $stmt->setFetchMode(PDO::FETCH_CLASS, "Planet"); $planet = $stmt->fetch();

А тепер розглянемо об'єкт Planet та перевіримо, які значення відповідають його характеристикам:

Як і очікувалося, отримані з бази даних значення перезаписані значеннями за замовчуванням. Тепер, ми продемонструємо вирішення проблем, використовуючи константу FETCH_PROPS_LATE (і той самий запит, що й попередній):

$stmt->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, "Planet"); $planet = $stmt->fetch(); var_dump($planet);

object(Planet)#4 (2) ( ["name":"Planet":private]=> string(5) "earth" ["color":"Planet":private]=> string(4) "blue" )

Нарешті отримано бажаний результат. Але що якщо конструктор класу не має базових значень, і вони мають бути задані? Це простіше: ми можемо задати параметри конструктора у вигляді масиву, як третій аргумент, після імені класу, використовуючи метод setFetchMode() . Наприклад, давайте змінимо конструктор:

Class Planet ( private $name; private $color; public function __construct($name, $color) ( $this->name = $name; $this->color = $color; ) [...] )

Аргументи конструктора тепер обов'язкові, тому ми запускаємо:

$stmt->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, "Planet", ["moon", "gray"]);

У цьому випадку зазначені нами параметри служать лише як базові значення, потрібні для роботи об'єкта без помилок: вони будуть переписані значеннями бази даних.

Отримання кількох об'єктів

Звичайно ж, можна отримати відразу кілька результатів у формі об'єктів, або використовуючи метод fetch() , або за допомогою циклу:

While ($planet = $stmt->fetch()) (// Щось справам з результатами)

Або отримавши всі результати одразу. У цьому випадку, як говорилося раніше, використовуючи метод fetchAll() вам потрібно буде вказувати режим fetch не перед запуском методу, але в той момент, коли він запуститься:

$stmt->fetchAll(PDO::FETCH_CLASS|PDO_FETCH_PROPS_LATE, "Planet", ["moon", "gray"]);

PDO::FETCH_INTO

При використанні цієї константи PDO не створює новий об'єкт, замість оновлюючи характеристики вже існуючого, але тільки якщо він public або у разі використання методу __set() всередині об'єкта.

Підготовлені проти прямих запитів

У PDO є два шляхи роботи із запитами: використовувати прямі та надійніші - підготовлені.

Прямі запити

Другий метод, натомість, повертає номер ряду, який був змінений запитом: ми використовуємо його у випадках, які замінюють ряди, таких як INSERT, DELETE або UPDATE. Прямі запити повинні бути використані тільки у випадках відсутності змінних у запитах, і в безпеці методу немає жодних сумнівів.

Підготовлені запити

PDO також підтримує запити, що виконуються в два кроки, підготовлені: вони корисні, коли в запитах є змінні, і більш безпечні в цілому, оскільки метод prepare() зробить усі необхідні дії за нас. Погляньмо, як використовуються змінні. Уявіть, що хочемо вставити характеристики планети в таблицю Planets . Для початку, підготуємо запит:

$stmt = $pdo->prepare("INSERT INTO planets(name, color) VALUES(?, ?)");

Як і говорилося раніше, ми використовуємо метод prepare(), який використовує SQL запит як аргумент, використовуючи тимчасові значення для змінних. Тимчасові значення можуть бути двох типів: позиційні та іменні.

Позиційні

Використовуючи? позиційні часові значення, код виходить коротший, але ми повинні задати дані, які будуть вставлені, в тому ж порядку, що і імена стовпців, в масиві представленому як аргумент методу execute() :

$stmt->execute([$planet->name, $planet->color]);

Іменні

Використовуючи іменні тимчасові значення named placeholders, нам не потрібен певний порядок, але ми отримаємо більше коду в результаті. При запуску методу execute() , ми повинні задати дані у формі асоціативного масиву, в якому кожен ключ – ім'я використаного тимчасового значення, а значення, що асоціюється, буде тим, що перенесеться в запит. Наприклад, попередній запит стане таким:

$stmt = $pdo->prepare("INSERT INTO planets(name, color) VALUES(:name, :color)"); $stmt->execute(["name" => $planet->name, "color" => $planet->color]);

Обидві методи prepare() і execute() можуть бути використані для запитів, які змінюють або просто отримують інформацію з бази даних. У першому випадку ми використовуємо fetch методи перераховані зверху для отримання інформації, а в другому – використовуючи метод rowCount() .

Методи bindValue() та bindParam()

Для надання значень, які будуть вставлені в запит, можуть використовуватися методи bindValue() і bindParam() . Перший прив'язує значення заданої змінної до позиційного або іменного значення часу, використаному при підготовці запиту. Взявши за приклад попередній випадок, ми зробимо:

$stmt->bindValue("name", $planet->name, PDO::PARAM_STR);

Ми прив'язуємо значення $planet->name до тимчасового значення: name. Зауважте, що використовуючи обидва методи bindValue() і bindParam() ми можемо також задати тип змінної, як третій аргумент, використовуючи відповідну константу PDO, в цьому випадку PDO::PARAM_STR .

Використовуючи замість bindParam() ми можемо прив'язати змінну до відповідного тимчасового значення, що використовується в підготовці запиту. Зауважте, що в цьому випадку змінна пов'язана з reference і її значення буде змінено на тимчасове, тільки коли запуститься метод execute() . Синтаксис такий самий, як і минулого разу:

$stmt->bindParam("name", $planet->name, PDO::PARAM_STR)

Ми прив'язали змінну, а чи не її значення $planet->name до:name ! Як сказано вище, заміна відбудеться тільки при запуску методу execute() , так що тимчасове значення буде замінено значення змінної в той момент.

PDO Транзакції

Транзакції дозволяють зберегти послідовність під час запуску множинних запитів. Всі запити виконуються «партіями» і відносяться до бази даних, тільки якщо вони вдало виконані. Транзакції не працюватимуть з усіма базами даних, і не з усіма SQL конструкціями, оскільки деякі з них викликають проблеми.

Як екстремальний і дивний приклад, уявіть, що користувач повинен вибрати список планет і кожен раз, коли він робить новий вибір, вам потрібно буде видалити попередній з бази даних, перш ніж вставляти новий. Що, якщо видалення відбудеться, а вставка – ні? Ми отримаємо користувача без планет! В основному, транзакції застосовуються так:

$pdo->beginTransaction(); try ( $stmt1 = $pdo->exec("DELETE FROM planets"); $stmt2 = $pdo->prepare("INSERT INTO planets(name, color) VALUES (?, ?)"); foreach ($planets as $planet) ( $stmt2->execute([$planet->getName(), $planet->getColor()]); ) $pdo->commit(); ) catch (PDOException $e) ( $pdo-> rollBack();

В першу чергу метод beginTransaction() в об'єкті PDO відключає autocommit запиту, потім запити запускаються в необхідному порядку. У цей момент, якщо не виникає виняток PDOException, запити автоматично пропускаються через метод commit() , в іншому випадку – через метод rollBack() транзакції скасовуються і autocommit відновлюється.

Таким чином, при множинних запитах завжди буде послідовність. Це досить очевидно, але транзакції PDO можуть бути використані тільки PDO::ATTR_ERRMODE встановлений на PDO::ERRMODE_EXCEPTION .

Фреймворк Bootstrap: швидка адаптивна верстка

Покроковий відеокурс з основ адаптивної верстки у фреймворку Bootstrap.

Навчіться верстати просто, швидко та якісно, ​​використовуючи потужний та практичний інструмент.

Верстайте на замовлення та отримуйте гроші.

Безкоштовний курс "Сайт на WordPress"

Бажаєте освоїти CMS WordPress?

Отримайте уроки з дизайну та верстки сайту на WordPress.

Навчіться працювати з темами та нарізати макет.

Безкоштовний відеокурс з малювання дизайну сайту, його верстки та встановлення на CMS WordPress!

*Наведіть курсор миші, щоб призупинити прокручування.

Назад вперед

Основи роботи з розширенням PDO

Сьогодні ми з вами розберемо дуже цікаву тему – основи роботи з розширенням PDOдля PHP.

PDO (PHP Data Objects)- це просто інтерфейс, який дозволяє працювати з різними базами даних без урахування їх специфіки. За допомогою PDO ми можемо легко перемикатися між різними базами даних та керувати ними. Щоб стало зрозуміліше, розберемо приклад.

Як ми мали підключатися раніше до бази MySQL?

Mysql_connect($host, $user, $password); mysql_select_db($db);

Щоб підключитися до SQLiteми мали написати так:

Sqlite_open($db);

Якщо нам потрібна база даних PostgreSQL, то треба написати так:

Pg_connect("host=$host, dbname=$db, user=$user, password=$password");

Не дуже зручно, так? Виходить, що якщо ми захочемо змінити базу даних, нам доведеться переробляти багато коду. І ось, щоб це виправити, з'явилося спеціальне PHP-розширення. PDO.

Давайте подивимося, як ми тепер можемо підключитись до бази:

$db = новий PDO("mysql:host=$host;dbname=$db", $user, $password);

$db = новий PDO("sqlite:$db);

PostgreSQL:

$db = новий PDO("pgsql:host=$host;dbname=$db", $user, $password);

Як бачите, все те саме, крім рядка підключення. Це єдина відмінність.


Тепер давайте розглянемо, як раніше ми мали виконувати запити:

$sql = "INSERT INTO(name, email) VALUES($name, $email)"; // MySQL mysql_query($sql); // SQLite sqlite_query($sql); // PostgreSQL pg_query($sql);

Тепер ми можемо абстрагуватися від цього:

// PDO $result = $db->exec($sql);

Всі! Наш запит буде виконано незалежно від того, яку БД ми використовуємо, а змінну resultпотрапить кількість порушених рядків.

Однак вибрати щось із бази даних у такий спосіб ми не зможемо. Для вибірки нам потрібно використовувати не exec, а query.

$sql = "SELECT name FROM users"; $result = $db->query($sql);

Тепер давайте згадаємо і про безпекуадже всі дані потрібно перевіряти. Як ми це робили раніше?

$sql = "SELECT * FROM users WHERE name = $name"; $name = $_POST["name"]; // MySQL $name = mysql_real_escape_string($name); // SQLite $name = sqlite_escape_string($name); // PostgreSQL $name = pg_escape_string($name);

Тепер нам не потрібно цього робити. PDOзробить усе за нас.

$name = $db->quote($name); $result = $db->query($sql);

PDO сам все перевірить та опрацює передані дані. Круто?:) Далі ще крутіше! Продовжимо.

Як ми раніше перетворювали результат на масив?Розглянемо з прикладу бази MySQL.

$result = mysql_query($sql); // Так $ row = mysql_fetch_assoc ($ result); // Або так ... $ row = mysql_fetch_array ($ result, FETCH_ASSOC);

Так само, як і асоціативний, ми могли отримати і нумерований масив. Тепер розглянемо як це робиться у PDO:

$stmt = $db->query($sql); // Асоціативний $result = $stmt->FETCH(PDO::FETCH_ASSOC); // Нумерований $result = $stmt->FETCH(PDO::FETCH_NUM); // Обидва типи масивів одночасно $result = $stmt->FETCH(PDO::FETCH_BOTH); // Об'єкт $result = $stmt->FETCH(PDO::FETCH_OBJ);

Використовувати це також дуже просто:

// Асоціативний echo $result["name"]; // Нумерований echo $result; // Об'єкт echo $result->name;

Для "лінивих" є така річ:

$stmt = $db->query($sql); $result = $stmt->FETCH(PDO::FETCH_LAZY);

Він повертає одразу всі 3 типи. Тобто. це FETCH_BOTHі FETCH_OBJразом. Як ви вже здогадалися, після цього доступ до даних можна отримати будь-яким із трьох способів:

Echo $result->name; echo $result["name"]; echo $result;

Однак Fetchповертає лише один запис, тому, якщо ми хочемо отримати всі записи, треба використовувати FetchAll.

$stmt = $db->query("SELECT * FROM users"); $result = $stmt->FetchAll(PDO::FETCH_ASSOC); foreach($result as $user) ( echo $user["name"]."
"; }

Але є ще одна класна штука, пов'язана з Fetch. З її допомогою ми можемо заповнити наш клас даними з БД автоматично.

Class User ( public $login; public $id; public function showInfo() ( echo " ".$this->id.""." : ".$this->login."
"; ) ) $db = new PDO("mysql:host=localhost;dbname=test", "root", ""); $stmt = $db->query("SELECT * FROM `users`"); $ result = $stmt->fetchAll(PDO::FETCH_CLASS, "User");

Як бачите, все дуже просто. Нам потрібно просто вказати константу FETCH_CLASSі через кому в лапках назва класу, куди будуть вставлені дані.

Потім перебираємо у циклі об'єкт та виводимо потрібну нам інформацію.
Увага!Назви властивостей у класі мають збігатися з назвами полів у базі даних.

Крім того, ми можемо створювати так звані підготовлені запити. У чому їх плюси?

1. Ми можемо один раз підготувати запит, а потім запускати його стільки разів, скільки нам потрібно. Причому як із такими самими, і з іншими параметрами.

Коли запит підготовлено, СУБД аналізує його, компілює та оптимізує план його виконання. У разі складних запитів час на виконання буде відчутним, якщо ми запускаємо його з різними параметрами. У разі підготовлених запитів це робиться один раз і, отже, часу витрачається менше.

2. Параметри підготовленого запиту не потрібно екранувати лапками, драйвер робить це автоматично. Якщо у додатку використовуються лише підготовлені запити, то SQL-ін'єкції майже неможливі.

PDO може емулювати підготовлені запитиякщо вони не підтримуються драйвером. Тепер, давайте розглянемо, як їх використовувати?

$stmt = $db->prepare("INSERT INTO users (name, login) VALUES (:name, :login)"); $stmt->bindParam(":name", $name); $stmt->bindParam(":login", $login); // Вставимо один рядок із такими значеннями $name = "vasya"; $login = "vasya123"; $stmt->execute(); // Тепер інший рядок з іншими значеннями $ name = "petya"; $login = "petya123"; $stmt->execute();

Метод bindParamдозволяє встановити параметри. Думаю тут все зрозуміло. Спочатку там, де хочемо, щоб були вставлені дані, пишемо такий рядок. :ім'яА потім вказуємо, звідки вони братимуться. У даному випадку вони братимуться зі змінних. nameі login.

Тепер ми можемо використовувати цей запит з різними параметрами скільки завгодно, а щоб його виконати, потрібно викликати метод execute. Це були іменованіпараметри. Також є і не іменовані.

$stmt = $db->prepare("INSERT INTO users (name, login) VALUES (?, ?)"); // Дані зі змінної name будуть вставлені замість першого питання $stmt->bindParam(1, $name); // Дані змінної login будуть вставлені замість другого знака питання $stmt->bindParam(2, $login); // Вставимо один рядок із такими значеннями $name = "vasya"; $login = "vasya123"; $stmt->execute(); // Тепер інший рядок з іншими значеннями $ name = "petya"; $login = "petya123"; $stmt->execute();

Наступний момент - як нам відловлювати помилки?

Для цього є клас PDOException. Я рекомендую усі ваші запити писати в блоці try-catch.

Try ( $db = новий PDO("myql:host=localhost;dbname=test", "root", ""); $stmt = $db->query("SELECT * FROM users"); $result = $stmt ->fetch(PDO::FETCH_ASSOC); echo $result["login"]; ) catch(PDOException $e) ( echo "Помилка: ".$e->getMessage()."
"; echo "На лінії: ".$e->getLine(); )

Тут ми припустилися помилки і написали myqlзамість mysql. І клас PDOExceptionнам про це напише.

У нього кілька методів, але найчастіше використовувані це getMessage(), який повертає нам текст помилки та getLine(), який повертає номер рядка, на якому допущена помилка.

Ну і насамкінець поговоримо про транзакції. Спочатку наведу код.

Try ( $db = новий PDO("mysql:host=localhost;dbname=test", "root", ""); $db->beginTransaction(); $stmt = $db->exec("INSERT INTO `users $stmt = $db->exec("INSERT INTO `users`(`login`) VALUES("login2")"); $stmt = $db- >exec("INSERT INTO `users`(`login`) VALUES("login3")"); $db->commit();

Тут ми починаємо транзакцію за допомогою методу beginTransaction(). Далі йде якийсь код запиту. Потім ми викликаємо метод commit(), щоб підтвердити наші зміни. Якщо ж щось пішло не так, то у блоці catchми викликаємо метод rollBack(), який поверне всі наші дані до попереднього стану.

"А навіщо власне потрібні ці транзакції?" - Запитайте ви. Щоб відповісти на це питання, розглянемо приклад, який я навів вище. Там ви вставляємо в полі "логін" значення login1, login2, login3.

Припустимо, що після того, як вставилися login1і login2, сталася якась помилка Вийде, що ці дані вставлені, а login3- Ні. У багатьох випадках це неприпустимо і порушить роботу програми у майбутньому.

Якраз для запобігання таким ситуаціям і потрібні транзакції. Якщо наш скрипт дав збій, то метод rollBack()поверне все до первісного вигляду. Тобто. login1і login2також не буде вставлено. Семулюємо цю помилку.

Try ( $db = новий PDO("mysql:host=localhost;dbname=test", "root", ""); $db->beginTransaction(); $stmt = $db->exec("INSERT INTO `users `(`login`) VALUES("login1")"); $stmt = $db->exec("INSERT INTO `users`('login`) ; $stmt = $db->exec("INSERT INTO `users`(`login`) VALUES("login3")"); $db->commit(); rollBack();

Після вставки login1і login2ми виходимо зі скрипту за допомогою функції exit(). У нас викидається виняток, ми потрапляємо до блоку catch, А там ми повертаємо все в первісний вигляд. Тепер, якщо ми подивимось у базу даних, то не побачимо там login1і login2.

На цьому моменті ми закінчимо. Очевидно, що тут ми розібрали не все, що надає нам PDO, проте дізналися основи роботи з ним. Більш детальну інформацію про дане розширення ви завжди можете знайти на офіційному сайті PHP.

Матеріал підготував Владислав Андрєєв спеціально для сайту

P.S.Хочете рухатися далі в освоєнні PHP та ОВП? Зверніть увагу на преміум-уроки з різних аспектів сайтобудування, включаючи програмування на PHP, а також на безкоштовний курс створення своєї CMS-системи на PHP з нуля з використанням ООП:

Сподобався матеріал і хочете віддячити?
Просто поділіться з друзями та колегами!


mob_info