Инструменты пользователя

Инструменты сайта


mysql:pdo_basics

Это старая версия документа!


# PDO. Основы работы с MySQL

## Почему стоит использовать PDO

Функции `mysql` в PHP для работы с БД устарели, на сегодняшний день желательно использовать `mysqli` или `PDO` (PHP Data Objects).

`mysqli` - эта библиотека, которая по большому счёту, не предназначена для использования напрямую в коде. Она может послужить хорошим строительным материалом для создания библиотеки более высокого уровня.

При работе с `mysqli` следует также помнить об обеспечении безопасности вашего приложения, в частности о защите от SQL-инъекций. В случае использования PDO (с его подготовленными запросами), такая защита идёт уже «из коробки», главное правильно применить необходимые методы.

## Установка PDO

Установка в Linux: ```bash sudo apt update sudo apt install php7.4-mysql sudo apt-get install pdo-mysql ```

В php.ini добавить: ``` extension=pdo.so extension=pdo_mysql.so ```

На Windows, как правило драйвер уже установлен, нужно просто проверить включен ли он в php.ini ``` extension=php_pdo_mysql.dll ```

### Проверить доступные драйвера

```php print_r(PDO::getAvailableDrivers()); ```

## Соединение с базой данных

Соединения устанавливаются автоматически при создании объекта `PDO` от его базового класса.

Пример #1. Простое подключение:

```php $db = new PDO('mysql:host=localhost;dbname=pdo', 'root', 'password'); ```

При ошибке подключения PHP выдаст ошибку: ``` Fatal error: Uncaught PDOException: … ```

Пример #2. Обработка ошибок подключения

```php try {

$dbh = new PDO('mysql:host=localhost;dbname=pdo', 'root', 'password');

} catch (PDOException $e) {

print "Error!: " . $e->getMessage();
die();

} ```

В этом примере подключения используем конструкцию `try…catch`.

## Подготовленные и прямые запросы

В PDO два способа выполнения запросов:

- `Прямой` - состоит из одного шага; - `Подготовленный` - состоит из двух шагов.

### Прямые запросы

- `query()` используется для операторов, которые не вносят изменения, например `SELECT`. Возвращает объект `PDOStatemnt`, из которого с помощью методов `fetch()` или `fetchAll` извлекаются результаты запроса. Можно его сравнить с `mysql resource`, который возвращала `mysql_query()`. - `exec()` используется для операторов `INSERT, DELETE, UPDATE`. Возвращает число обработанных запросом строк.

*Прямые запросы используются только в том случае, если в запросе отсутствуют переменные и есть уверенность, что запрос безопасен и правильно экранирован*.

```php $stmt = $db→query(«SELECT * FROM categories»); while ($row = $stmt→fetch()) {

echo '<pre>';
print_r($row);

}

```

### Подготовленные запросы

*Если же в запрос передаётся хотя бы одна переменная*, то этот запрос в обязательном порядке должен выполняться *только через `подготовленные выражения`*.

Подготовленное выражение - это обычный SQL запрос, в котором вместо переменной ставится специальный маркер - *плейсхолдер*.

PDO поддерживает *позиционные плейсхолдеры* (`?`), для которых важен порядок передаваемых переменных, и *именованные* (`:name`), для которых порядок не важен. Примеры: ```php $sql = «SELECT name FROM categories WHERE id = ?»; $sql = «SELECT name FROM categories WHERE name = :name»; ```

Чтобы выполнить такой запрос, сначала его надо подготовить с помощью метода `prepare()`. Он также возвращает `PDO statement`, но ещё без данных. Чтобы их получить, надо исполнить этот запрос, предварительно передав в него наши переменные.

Передать можно двумя способами просто выполнив метод `execute()` и передав ему массив с переменными:

вар.1 ```php $stmt = $pdo→prepare(«SELECT `name` FROM categories WHERE `id` = ?»); $stmt→execute([$id]); ```

вар.2 ```php $stmt = $pdo→prepare(«SELECT `name` FROM categories WHERE `name` = :name»); $stmt→execute(['name' ⇒ $name]); ``` в случае именованных плейсхолдеров в `execute()` должен передаваться массив, в котором ключи должны совпадать с именами плейсхолдеров.

После этого можно извлечь результаты запроса: ```php $id = 1; $stmt = $db→prepare(«SELECT * FROM categories WHERE `id` = ?»); $stmt→execute([$id]); $category = $stmt→fetch(PDO::FETCH_LAZY); echo '<pre>'; print_r($category); ```

Важно! Подготовленные запросы - основная причина использовать PDO, поскольку это единственный безопасный способ выполнения SQL запросов, в которых участвуют переменные.

## Получение данных. ### Метод fetch()

Мы уже выше познакомились с методом `fetch()`, который служит для *последовательного получения* строк из БД.

У метода fetch() имеется множество праметров, которые здесь описываться не будут. В основном же рекомендуется применять `fetch()` в режиме `FETCH_LAZY`

```php $id = 1; $stmt = $db→prepare(«SELECT * FROM categories WHERE `id` = ?»); $stmt→execute([$id]); while ($row = $stmt→fetch(PDO::FETCH_LAZY)) {

  echo 'Category name: '.$row->name;

} ```

В этом режиме не тратится лишняя память, и к тому же к колонкам можно обращаться любым из трех способов - через индекс, имя, или свойство (через `→`).

Недостатком данного режима является то, что он не работает с `fetchAll()`

### Метод fetchColumn()

Это метод для получения значения единственной колонки. Очень удобно, если запрашиваем только одно поле - в этом случае значительно сокращается количество кода:

```php $id = 1; $stmt = $db→prepare(«SELECT `name` FROM categories WHERE `id` = ?»); $stmt→execute([$id]); $name = $stmt→fetchColumn(); echo 'Category name: '.$name; ```

### Метод fetchAll()

```php $data = $db→query(«SELECT * FROM categories»)→fetchAll(PDO::FETCH_ASSOC); foreach ($data as $k ⇒ $v){

echo 'Category name: '.$v['name'].'<br>';

} ```

## PDO и использование операторов

Работая с подготовленными запросами, следует понимать, что плейсхолдер может заменять только строку или число. *Ни ключевое слово, ни идентификатор, ни часть строки или набор строк* через плейсхолдер подставить нельзя.

### PDO и оператор LIKE

Поэтому для `LIKE` необходимо сначала подготовить строку поиска целиком, а потом ее подставлять в запрос:

```php $search = 'комп'; $query = «SELECT * FROM categories WHERE `name` LIKE ?»; $params = [«%$search%»]; $stmt = $db→prepare($query); $stmt→execute($params); $data = $stmt→fetchAll(PDO::FETCH_ASSOC); $i = 1; foreach ($data as $category){

echo $i++ . '. ' . $category['name'].'<br>';

} ```

Здесь может вознинуть проблема! Поиск может не работать, потому как из базы у вас приходят данные в неправильной кодировке.
Необходимо добавить кодировку в подключение, если она там не указана:

`$db = new PDO('mysql:host=localhost;dbname=pdo;charset=utf8mb4', 'root', '');`

### PDO и оператор LIMIT

==Важно!== Когда PDO работает в режиме эмуляции, все данные, которые были переданы напрямую в `execute()`, форматируются как строки. То есть, эскейпятся и обрамляются кавычками. Поэтому `LIMIT ?,?` превращается в `LIMIT '10', '10'` и очевидным образом вызывает ошибку синтаксиса и, соответственно, пустой массив данных.

Решение #1: Отключить режим эмуляции: ```php $db→setAttribute(PDO::ATTR_EMULATE_PREPARES, false); ```

Решение #2: Биндить эти цифры через `bindValue()`, принудительно выставляя им тип `PDO::PARAM_INT`: ```php $limit = 3; $stm = $db→prepare('SELECT * FROM categories LIMIT ?'); $stm→bindValue(1, $limit, PDO::PARAM_INT); $stm→execute(); $data = $stm→fetchAll(); echo '<pre>'; print_r($data); ```

### PDO и оператор IN

При выборке из таблицы необходимо доставать записи, соответствующие всем значениям массива.

```php $arr = [1,3,6]; $in = str_repeat('?,', count($arr) - 1) . '?'; $sql = «SELECT * FROM categories WHERE `id` IN ($in)»; $stm = $db→prepare($sql); $stm→execute($arr); $data = $stm→fetchAll(); echo '<pre>'; print_r($data); ```

## Примеры

### Добавление записей

```php $name = 'Новая категория'; $query = «INSERT INTO `categories` (`name`) VALUES (:name)»; $params = [

  ':name' => $name

]; $stmt = $pdo→prepare($query); $stmt→execute($params); ```

### Изменение записей

```php $id = 1; $name = 'Изменённая запись'; $query = «UPDATE `categories` SET `name` = :name WHERE `id` = :id»; $params = [

  ':id' => $id,
  ':name' => $name

]; $stmt = $pdo→prepare($query); $stmt→execute($params); ```

### Удаление записей

```php $id = 1; $query = «DELETE FROM `categories` WHERE `id` = ?»; $params = [$id]; $stmt = $pdo→prepare($query); $stmt→execute($params); ```

/var/www/html/dokuwiki/data/attic/mysql/pdo_basics.1668265289.txt.gz · Последнее изменение: (внешнее изменение)