В прошлой заметке мы познакомились с основами PDO и простейшими запросами выборки данных из базы данных. В этой заметке мы научимся управлять режимами получения данных.
Все предопределенные константы здесь
Аналог mysql_fetch_array(). Все данные возвращаются в дублированном виде, с текстовыми индексами и цифровыми. Этот режим включен в PDO по умолчанию.
$stm = $db->query('SELECT * FROM categories LIMIT 1')->fetch(PDO::FETCH_BOTH); // Результат Array ( [id] => 1 [0] => 1 [name] => Ноутбуки и планшеты [1] => Ноутбуки и планшеты ) Аналог mysql_fetch_row(). Только цифровые индексы:
$stm = $db->query('SELECT * FROM categories LIMIT 1')->fetch(PDO::FETCH_NUM); // Результат Array ( [0] => 1 [1] => Ноутбуки и планшеты ) Аналог mysql_fetch_assoc() Только текстовые индексы.
$stm = $db->query('SELECT * FROM categories LIMIT 1')->fetch(PDO::FETCH_ASSOC); // Результат Array ( [id] => 1 [name] => Ноутбуки и планшеты ) Аналог mysql_fetch_object() без указания имени класса, возвращает экземпляр stdClass
$stm = $db->query('SELECT * FROM categories LIMIT 1')->fetch(PDO::FETCH_OBJ); // Результат stdClass Object ( [id] => 1 [name] => Ноутбуки и планшеты ) В этом режиме не тратится лишняя память, и к тому же к колонкам можно обращаться любым из трех способов - через индекс, имя, или свойство (через ->). Недостатком же данного режима является то, что он не работает с fetchAll()
$stm = $db->query('SELECT `name` FROM categories')->fetch(PDO::FETCH_LAZY); // Результат PDORow Object ( [queryString] => SELECT `name` FROM categories [name] => Ноутбуки и планшеты ) Когда необходимо получить только одну колонку из результата. Соответственно, имеет смысл только при использовании с fetchAll() - и в этом случае возвращает сразу одномерный массив.
$stm = $db->query('SELECT `name` FROM categories')->fetchAll(PDO::FETCH_COLUMN); // Результат Array ( [0] => Ноутбуки и планшеты [1] => Компьютеры и периферия [2] => Комплектующие для ПК [3] => Смартфоны и смарт-часы [4] => Телевизоры и медиа [5] => Игры и приставки [6] => Аудиотехника [7] => Фото-видеоаппаратура [8] => Офисная техника и мебель [9] => Сетевое оборудование [10] => Крупная бытовая техника [11] => Товары для кухни [12] => Красота и здоровье [13] => Товары для дома [14] => Инструменты [15] => Автотовары ) Малоизвестный, но очень полезный режим, когда из двух запрошенных полей содержимое первого становится ключом, а второго - значением одномерного массива.
$stm = $db->query('SELECT `name`, `id` FROM categories')->fetchAll(PDO::FETCH_KEY_PAIR); // Результат Array ( [Ноутбуки и планшеты] => 1 [Компьютеры и периферия] => 2 [Комплектующие для ПК] => 3 [Смартфоны и смарт-часы] => 4 [Телевизоры и медиа] => 5 [Игры и приставки] => 6 [Аудиотехника] => 7 [Фото-видеоаппаратура] => 8 [Офисная техника и мебель] => 9 [Сетевое оборудование] => 10 [Крупная бытовая техника] => 11 [Товары для кухни] => 12 [Красота и здоровье] => 13 [Товары для дома] => 14 [Инструменты] => 15 [Автотовары] => 16 ) Похож на предыдущий, но в качестве значения возвращает всю оставшуюся строку. C fetch() этот режим не возвращает ничего вразумительного, а вот с fetchAll() как раз получается такой, весьма востребованный режим. Главное, чтобы первой колонкой в запросе выбиралось уникальное поле - тогда оно будет использовано в качестве индекса возвращаемого массива, вместо обычной нумерации
$stm = $db->query('SELECT * FROM categories LIMIT 3')->fetchAll(PDO::FETCH_UNIQUE); // Результат Array ( [1] => Array ( [name] => Ноутбуки и планшеты [0] => Ноутбуки и планшеты ) [2] => Array ( [name] => Компьютеры и периферия [0] => Компьютеры и периферия ) [3] => Array ( [name] => Комплектующие для ПК [0] => Комплектующие для ПК ) ) Группирует значения по первой колонке. К примеру, нижеследующий код разобьёт пользователей на мальчиков и девочек, и положит их в разные массивы:
$data = $pdo->query('SELECT sex, name, car FROM users')->fetchAll(PDO::FETCH_GROUP); // Результат array ( 'male' => array ( 0 => array ( 'name' => 'John', 'car' => 'Toyota', ), 1 => array ( 'name' => 'Mike', 'car' => 'Ford', ), ), 'female' => array ( 0 => array ( 'name' => 'Mary', 'car' => 'Mazda', ), 1 => array ( 'name' => 'Kathy', 'car' => 'Mazda', ), ), ) То есть, этот режим идеально подходит для классической задачи вывести события сгруппированные по дням (или вывести товары, сгруппированные по категориям). Также может комбинироваться с PDO::FETCH_COLUMN:
$sql = ""SELECT sex, name FROM users""; $data = $pdo->query($sql)->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_COLUMN); // Результат array ( 'male' => array ( 0 => 'John', 1 => 'Mike', ), 'female' => array ( 0 => 'Mary', 1 => 'Kathy', ), ) Создаёт объект указанного класса, заполняя его свойства данными из БД. Однако здесь, увы, начинаются неудобства и непоследовательность в работе вызывающих функций. Если для fetchAll() можно написать красиво и компактно
$data = $pdo->query('SELECT * FROM users LIMIT 1')->fetchAll(PDO::FETCH_CLASS, 'Foo'); то для fetch() приходится писать такую колбасу:
$stmt = $pdo->query('SELECT * FROM users LIMIT 1'); $stmt->setFetchMode( PDO::FETCH_CLASS, 'Foo'); $data = $stmt->fetch(); Из-за того что fetch() не позволяет передать имя класса, мы вынуждены пользоваться setFetchMode(). А учитывая, что эта функция возвращает булево значение, а не ссылку на объект, мы не можем использовать method chaining. Также следует помнить, что в этом режиме PDO будет вызывать магический метод __set() если свойство, совпадающее с именем поля, не найдено в объекте. Для PHP это означает, что если в объекте отсутствует такой метод, то все колонки строки, полученной из БД, будут назначены переменным класса. Если же мы хотим присвоить значения только существующим переменным, то этот момент надо контролировать с помощью метода __set(). Например
class Foo { private $name; public function __set($name, $value) {} } $data = $pdo->query('SELECT * FROM users LIMIT 1') ->fetchAll(PDO::FETCH_CLASS, 'Foo'); array(1) { [0]=> object(Foo)#3 (1) { [""name"":""Foo"":private]=> string(4) ""John"" } } в то время как у класса с пустым __set() будут заполнены только существующие свойства:
class Foo {} $data = $pdo->query('SELECT * FROM users LIMIT 1') ->fetchAll(PDO::FETCH_CLASS, 'Foo'); // Результат array(1) { [0]=> object(Foo)#3 (3) { [""name""] => string(4) ""John"" [""sex""] => string(4) ""male"" [""car""] => string(6) ""Toyota"" } } Можно, кстати, заметить, что PDO присваивает значения и приватным свойствам, что несколько неожиданно, но очень удобно.
Очень интересная константа. Представляет собой не самостоятельный режим получения данных, а флаг-модификатор, изменяющий поведение других режимов. При её использовании PDO будет брать имя класса из первой колонки полученных из БД данных. То есть, с её помощью код для fetch() можно сделать короче
class Foo {} $data = $pdo->query(""SELECT 'Foo', name FROM users LIMIT 1"") ->fetch(PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE); // Результат object(Foo)#3 (1) { [""name""]=> string(4) ""John"" } Ещё один флаг-модификатор. По умолчанию PDO присваивает значения свойствам класса до вызова конструктора. При помощи же данной константы это поведение можно изменить - сначала будет вызываться конструктор:
class Foo { private $name; public function __construct() { $this->name = NULL; } } $data = $pdo->query('SELECT name FROM users LIMIT 1') ->fetchAll(PDO::FETCH_CLASS, 'Foo'); var_dump($data); $data = $pdo->query('SELECT name FROM users LIMIT 1') ->fetchAll(PDO::FETCH_CLASS|PDO::FETCH_PROPS_LATE, 'Foo'); var_dump($data); // Результат array(1) { [0]=> object(Foo)#3 (1) { [""name"":""Foo"":private]=> NULL } } array(1) { [0]=> object(Foo)#4 (1) { [""name"":""Foo"":private]=> string(4) ""John"" } } В отличие от PDO::FETCH_CLASS не создаёт новый объект, а обновляет существующий. Соответственно, в качестве параметра передается переменная с объектом. По очевидным причинам имеет смысл только с fetch()
class Foo { public $name; public $state; public function __construct() { $this->name = NULL; } } $foo = new Foo; $foo->state = ""up'n'running""; var_dump($foo); $stmt = $pdo->query('SELECT name FROM users LIMIT 1'); $stmt->setFetchMode(PDO::FETCH_INTO, $foo); $data = $stmt->fetch(); var_dump($data, $foo); // Результат object(Foo)#2 (2) { [""name""] => NULL [""state""] => string(12) ""up'n'running"" } object(Foo)#2 (2) { [""name""] => string(4) ""John"" [""state""] => string(12) ""up'n'running"" } object(Foo)#2 (2) { [""name""] => string(4) ""John"" [""state""] => string(12) ""up'n'running"" } Как видно, fetch() возвращает тот же объект, что представляется мне несколько избыточным. Также, с сожалением приходится констатировать, что в отличие от PDO::FETCH_CLASS, этот режим не присваивает значения приватным свойствам.
Ещё один флаг для PDO::FETCH_CLASS. Должен возвращать объект, который хранился в БД в сериализованном виде. Конструктор не вызывается. На данный момент не работает. Должно быть что-то вроде такого
class foo {} $foo = new foo; $foo->status=""up'n'running""; $sFoo = serialize($foo); // записываем $sFoo в БД // и потом что-то вроде $stmt = $pdo->query('SELECT sFoo FROM table'); $stmt->setFetchMode(PDO::FETCH_CLASS|PDO::FETCH_SERIALIZE, 'foo'); $foo = $stmt->fetch(); Для любителей замыканий. Работает только внутри fetchAll(). В параметры функции PDO передаёт переменные для каждого полученного поля, что может быть неудобным - нет доступа к именам полей, а только к значениям. К примеру, эмуляция работы PDO::FETCH_COLUMN:
$data = $pdo ->query('SELECT name FROM users') ->fetchAll(PDO::FETCH_FUNC, function($first) {return $first;}); Почти то же самое, что PDO::FETCH_ASSOC, но с одним отличием. Много раз я встречал на форумах вопросы о том, как получить значения полей с одинаковыми именами из разных таблиц при джойне. Всегда ответ был один - писать алиасы руками в запросе или использовать цифровые индексы. А вот и ответ от PDO: получение данных в этом режиме аналогично PDO::FETCH_ASSOC, но если встречаются поля с одинаковыми именами, то все значения по очереди записываются во вложенный массив. Допустим, у нас есть таблицы users и companies, причем в обеих есть поле name. Если получать данные традиционным путём, то одно из полей будет съедено:
$data = $pdo->query(""SELECT * FROM users, companies WHERE users.name=username"")->fetch(); // Результат array(3) { [""name""] => string(10) ""ACME, Inc."" [""sex""] => string(4) ""male"" [""username""] => string(4) ""John"" } Если же указать это флаг, то все значения колонок с совпадающими именами будут собраны во вложенном массиве в порядке поступления:
$data = $pdo->query(""SELECT * FROM users, companies WHERE users.name=username"") ->fetch(PDO::FETCH_NAMED); // Результат array(3) { [""name""]=> array(2) { [0]=> string(4) ""John"" [1]=> string(10) ""ACME, Inc."" } [""sex""] => string(4) ""male"" [""username""] => string(4) ""John"" } Очень интересный режим, сильно отличающийся от других. В отличие от всех остальных, он не возвращает не массив или объект. Вместо этого он присваивает значения переменным, предварительно указанным с помощью bindColumn() - режим, аналогичный тому, что используется в mysqli при получении данных из подготовленного запроса. Пример есть в документации
Материал подготовлен на основе официальной статьи .
Оффлайн
Последняя авторизация: 25.03.2025 08:09
CleanCot © Четверг, 03 сентября 2026 00:07
Версия PHP: 8.4.6
Страница создана за 0.05343 секунды
Режим совместимости: legacyMode Выключен
Версия ядра: v.0.9.26 beta / Версия БД: v.0.9.26