- Подробности
-
Категория: PHP. Базы данных
Доступ к базе данных в PHP
Практическая часть использования PDO API для работы с базой данных.
Что?
Для соединения с базой данных MySQL имеется три разных API:
mysql
mysqli
— MySQL Improved (улучшенная)
pdo
— PHP Data Objects (объекты данных PHP)
Традиционный API mysql
хорошо выполняет свою работу и получил широкое распространение благодаря тому, что делает процесс получения записей из базы данных очень простым. Например:
06 |
mysql_connect( 'localhost' , 'username' , 'password' ) or die ( 'Could not connect: ' . mysql_error()); |
09 |
mysql_select_db( 'someDatabase' ) or die ( 'Не могу выбрать базу данных' ); |
12 |
$query = "SELECT * from someTable" ; |
13 |
$result = mysql_query( $query ) or die ( 'Query failed: ' . mysql_error()); |
15 |
# Фильтрация строк и вывод нужной информации |
16 |
while ( $row = mysql_fetch_object( $result )) { |
Да, приведенный пример очень прост. Но у него есть существенные недостатки:
- Устарел: хотя официально его не признают устаревшим, но для новых приложений и обучения лучше использовать другие API.
- Очистка: процесс очистки ввода пользователя от ненужного кода ложится на плечи разработчика.
- Гибкость: данный API не гибкий и работает только с базой данных MySQL. А если нужно перейти на другую?
PDO (PHP Data Objects) предоставляет более мощный инструмент для использования. Не нужно думать о драйвере базы данных, можно использовать подготовленные выражения, исключаются инъекции кода SQL.
Как?
На первый взгляд PDO API выглядит устрашающе. Не потому что он чрезмерно сложен, а потому что mysql
является очень простым! Нужно просто сделать несколько несложных действий.
Соединение
Вы уже знаете устаревший способ соединения с базой данной MySQL:
2 |
mysql_connect( 'localhost' , 'username' , 'password' ) or die ( 'Could not connect: ' . mysql_error()); |
При работе с PDO нужно создать новый экземпляр класса, указать драйвер, имя базы данных, имя пользователя и пароль:
1 |
$conn = new PDO( 'mysql:host=localhost;dbname=myDatabase' , $username , $password ); |
Ничего сложного.
Преимуществом данного подхода является простота изменения базы данных. Достаточно поменять имя источника данных. Нет зависимости от MySQL как при использовании функции mysql_connect
.
Ошибки
Но что если произойдет ошибка при соединении с базой данных? Обернем все в блок try/catch
:
2 |
$conn = new PDO( 'mysql:host=localhost;dbname=myDatabase' , $username , $password ); |
3 |
$conn ->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
4 |
} catch(PDOException $e ) { |
5 |
echo 'ERROR: ' . $e ->getMessage(); |
Обратите внимание на то, что по умолчанию для PDO установлен режим работы с ошибками PDO::ERRMODE_SILENT
. Если все оставить без изменении, то придется вручную обрабатывать ошибки после выполнения запроса.
1 |
echo $conn ->errorCode(); |
2 |
echo $conn ->errorInfo(); |
При разработке лучше устанавливать режим PDO::ERRMODE_EXCEPTION
, в котором необработанные ошибки будут вызывать исключения и остановку скрипта.
Доступны следующие режимы работы с ошибками:
PDO::ERRMODE_SILENT
PDO::ERRMODE_WARNING
PDO::ERRMODE_EXCEPTION
Получаем данные
Итак, у нас есть соединение с базой данных и теперь нужно получить данные. Есть два способа решить поставленную задачу: query
и execute
. Рассмотрим оба.
Query
05 |
$name = 'Joe' ; # user-supplied data |
08 |
$conn = new PDO( 'mysql:host=localhost;dbname=myDatabase' , $username , $password ); |
09 |
$conn ->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
11 |
$data = $conn ->query( 'SELECT * FROM myTable WHERE name = ' . $conn ->quote( $name )); |
13 |
foreach ( $data as $row ) { |
16 |
} catch(PDOException $e ) { |
17 |
echo 'ERROR: ' . $e ->getMessage(); |
Хотя выше приведенный пример будет работать, нам нужно вручную очищать данные пользователя с помощью метода PDO::quote
. Данный метод эквивалентен функции mysql_real_escape_string
(в обоих случаях строка очищается и заключается в кавычки). В случаях, когда нужно построить запрос SQL с использованием пользовательских данных, лучше применять подготовленные выражения. То есть, если запрос SQL не зависит от ввода пользователя, метод query
будет отличным выбором, а результат можно обработать выражением foreach
.
Execute
07 |
$conn = new PDO( 'mysql:host=localhost;dbname=myDatabase' , $username , $password ); |
08 |
$conn ->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
10 |
$stmt = $conn ->prepare( 'SELECT * FROM myTable WHERE id = :id' ); |
11 |
$stmt ->execute( array ( 'id' => $id )); |
13 |
while ( $row = $stmt ->fetch()) { |
16 |
} catch(PDOException $e ) { |
17 |
echo 'ERROR: ' . $e ->getMessage(); |
В данном примере используется также метод prepare
для подготовки запроса, перед тем, как присоединить данные пользователя. В таком случае инъекция SQL теоретически невозможна, так как сами данные не вставляются в текст запроса SQL. Используется именованный параметр (:id
) для обозначения места.
Затем выполняется запрос с передачей массива, который содержит данные, которые следует использовать на месте именованного параметра.
1 |
$stmt ->execute( array ( 'id' => $id )); |
В качестве альтернативы можно использовать метод bindParam
:
1 |
$stmt ->bindParam( ':id' , $id , PDO::PARAM_INT); |
Обрабатываем данные
После вызова метода execute
есть несколько способов получить данные: массив (по умолчанию), объект и так далее. В выше приведенном примере используется режим по умолчанию: PDO::FETCH_ASSOC
. Его легко поменять, если нужно:
1 |
while ( $row = $stmt ->fetch(PDO::FETCH_OBJ)) { |
Теперь результат будет в виде объекта. Доступны следующие опции:
- PDO::FETCH_ASSOC: Возвращает массив.
- PDO::FETCH_BOTH: Возвращает массив, проиндексированный по колонке с именем.
- PDO::FETCH_BOUND: Возвращает TRUE и назначает значение столбцов в наборе результата связанным переменным PHP.
- PDO::FETCH_CLASS: Возвращает новый экземпляр указанного класса.
- PDO::FETCH_OBJ: Возвращает анонимный объект, с именами свойств, соответствующих столбцам.
Выше приведенный код не имеет обратной связи на случай отсутствия результата. Исправим ситуацию:
01 |
$stmt ->execute( array ( 'id' => $id )); |
03 |
# Получаем массив, который содержит все строки результата |
04 |
$result = $stmt ->fetchAll(); |
06 |
# Если получена одна или более строк… |
07 |
if ( count ( $result ) ) { |
08 |
foreach ( $result as $row ) { |
12 |
echo "Ничего не найдено." ; |
Полный код будет выглядеть следующим образом:
03 |
$conn = new PDO( 'mysql:host=localhost;dbname=someDatabase' , $username , $password ); |
04 |
$stmt = $conn ->prepare( 'SELECT * FROM myTable WHERE id = :id' ); |
05 |
$stmt ->execute( array ( 'id' => $id )); |
07 |
$result = $stmt ->fetchAll(); |
09 |
if ( count ( $result ) ) { |
10 |
foreach ( $result as $row ) { |
14 |
echo "Ничего не найдено." ; |
16 |
} catch(PDOException $e ) { |
17 |
echo 'ERROR: ' . $e ->getMessage(); |
Многократное выполнение
PDO проявляет свою силу в случаях, когда надо выполнить один и тот же запрос SQL несколько раз, но с разными параметрами.
02 |
$conn = new PDO( 'mysql:host=localhost;dbname=someDatabase' , $username , $password ); |
03 |
$conn ->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
05 |
# Подготавливаем запрос один раз |
06 |
$stmt = $conn ->prepare( 'INSERT INTO someTable VALUES(:name)' ); |
07 |
$stmt ->bindParam( ':name' , $name ); |
16 |
} catch(PDOException $e ) { |
17 |
echo $e ->getMessage(); |
Как только запрос подготовлен, его можно выполнить несколько раз с разными параметрами. Выше приведенный код вставляет две строки в базу данных: одна с именем “Kevin,” а другая — “Steven.”
Типовые операции с базой данных (CRUD)
Рассмотрим выполнение типовых операций с данными (CRUD — Create, Read, Update, Delete создание, чтение, обновление, удаление). Обратите внимание, что код для них будет очень похожим.
Создание (Вставка)
02 |
$pdo = new PDO( 'mysql:host=localhost;dbname=someDatabase' , $username , $password ); |
03 |
$pdo ->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
05 |
$stmt = $pdo ->prepare( 'INSERT INTO someTable VALUES(:name)' ); |
07 |
':name' => 'Justin Bieber' |
11 |
echo $stmt ->rowCount(); |
12 |
} catch(PDOException $e ) { |
13 |
echo 'Error: ' . $e ->getMessage(); |
Обновление
02 |
$name = "Joe the Plumber" ; |
05 |
$pdo = new PDO( 'mysql:host=localhost;dbname=someDatabase' , $username , $password ); |
06 |
$pdo ->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
08 |
$stmt = $pdo ->prepare( 'UPDATE someTable SET name = :name WHERE id = :id' ); |
14 |
echo $stmt ->rowCount(); |
15 |
} catch(PDOException $e ) { |
16 |
echo 'Error: ' . $e ->getMessage(); |
Удаление
04 |
$pdo = new PDO( 'mysql:host=localhost;dbname=someDatabase' , $username , $password ); |
05 |
$pdo ->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
07 |
$stmt = $pdo ->prepare( 'DELETE FROM someTable WHERE id = :id' ); |
08 |
$stmt ->bindParam( ':id' , $id ); |
11 |
echo $stmt ->rowCount(); |
12 |
} catch(PDOException $e ) { |
13 |
echo 'Error: ' . $e ->getMessage(); |
Результат в виде объекта
Одним из замечательных свойств PDO (а также и mysqli) является возможность представления результата запроса в виде экземпляра класса или объекта. Например:
05 |
public function full_name() |
07 |
return $this ->first_name . ' ' . $this ->last_name; |
12 |
$pdo = new PDO( 'mysql:host=localhost;dbname=someDatabase' , $username , $password ); |
13 |
$pdo ->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); |
15 |
$result = $pdo ->query( 'SELECT * FROM someTable' ); |
17 |
# Выводим результат как объект |
18 |
$result ->setFetchMode(PDO::FETCH_CLASS, 'User' ); |
20 |
while ( $user = $result ->fetch()) { |
21 |
# Вызываем наш метод full_name |
22 |
echo $user ->full_name(); |
24 |
} catch(PDOException $e ) { |
25 |
echo 'Error: ' . $e ->getMessage(); |
Заключение
Если вы все еще используете старый API mysql
для работы с базой данных, то следует остановиться. Несмотря на то, что он еще не считается устаревшим, код будет иметь более высокий уровень безопасности и поддержки, если перевести его на PDO.
Данный урок подготовлен для вас командой сайта ruseller.com
Источник урока: net.tutsplus.com/tutorials/php/php-database-access-are-you-doing-it-correctly/
Перевел: Сергей Фастунов