- PHP PDO: Режимы получения данных, константы PDO::FETCH_*
- PDO::FETCH_BOTH
- PDO::FETCH_NUM
- PDO::FETCH_ASSOC
- PDO::FETCH_OBJ
- PDO::FETCH_LAZY
- PDO::FETCH_COLUMN
- PDO::FETCH_KEY_PAIR
- PDO::FETCH_UNIQUE
- PDO::FETCH_GROUP
- PDO::FETCH_CLASS
- PDO::FETCH_CLASSTYPE
- PDO::FETCH_PROPS_LATE
- PDO::FETCH_INTO
- PDO::FETCH_SERIALIZE
- PDO::FETCH_FUNC
- PDO::FETCH_NAMED
- PDO::FETCH_BOUND
PHP PDO: Режимы получения данных, константы PDO::FETCH_*
В прошлой заметке мы познакомились с основами PDO и простейшими запросами выборки данных из базы данных. В этой заметке мы научимся управлять режимами получения данных.
Все предопределенные константы здесь
PDO::FETCH_BOTH
Аналог mysql_fetch_array() . Все данные возвращаются в дублированном виде, с текстовыми индексами и цифровыми. Этот режим включен в PDO по умолчанию.
$stm = $db->query('SELECT * FROM categories LIMIT 1')->fetch(PDO::FETCH_BOTH); // Результат Array ( [id] => 1 [0] => 1 [name] => Ноутбуки и планшеты [1] => Ноутбуки и планшеты )
PDO::FETCH_NUM
Аналог mysql_fetch_row() . Только цифровые индексы:
$stm = $db->query('SELECT * FROM categories LIMIT 1')->fetch(PDO::FETCH_NUM); // Результат Array ( [0] => 1 [1] => Ноутбуки и планшеты )
PDO::FETCH_ASSOC
Аналог mysql_fetch_assoc() Только текстовые индексы.
$stm = $db->query('SELECT * FROM categories LIMIT 1')->fetch(PDO::FETCH_ASSOC); // Результат Array ( [id] => 1 [name] => Ноутбуки и планшеты )
PDO::FETCH_OBJ
Аналог mysql_fetch_object() без указания имени класса, возвращает экземпляр stdClass
$stm = $db->query('SELECT * FROM categories LIMIT 1')->fetch(PDO::FETCH_OBJ); // Результат stdClass Object ( [id] => 1 [name] => Ноутбуки и планшеты )
PDO::FETCH_LAZY
В этом режиме не тратится лишняя память, и к тому же к колонкам можно обращаться любым из трех способов — через индекс, имя, или свойство (через -> ). Недостатком же данного режима является то, что он не работает с fetchAll()
$stm = $db->query('SELECT `name` FROM categories')->fetch(PDO::FETCH_LAZY); // Результат PDORow Object ( [queryString] => SELECT `name` FROM categories [name] => Ноутбуки и планшеты )
PDO::FETCH_COLUMN
Когда необходимо получить только одну колонку из результата. Соответственно, имеет смысл только при использовании с 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] => Автотовары )
PDO::FETCH_KEY_PAIR
Малоизвестный, но очень полезный режим, когда из двух запрошенных полей содержимое первого становится ключом, а второго — значением одномерного массива.
$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 )
PDO::FETCH_UNIQUE
Похож на предыдущий, но в качестве значения возвращает всю оставшуюся строку. 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] => Комплектующие для ПК ) )
PDO::FETCH_GROUP
Группирует значения по первой колонке. К примеру, нижеследующий код разобьёт пользователей на мальчиков и девочек, и положит их в разные массивы:
$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', ), )
PDO::FETCH_CLASS
Создаёт объект указанного класса, заполняя его свойства данными из БД. Однако здесь, увы, начинаются неудобства и непоследовательность в работе вызывающих функций. Если для 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_CLASSTYPE
Очень интересная константа. Представляет собой не самостоятельный режим получения данных, а флаг-модификатор, изменяющий поведение других режимов. При её использовании 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::FETCH_PROPS_LATE
Ещё один флаг-модификатор. По умолчанию 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_INTO
В отличие от 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_SERIALIZE
Ещё один флаг для 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();
PDO::FETCH_FUNC
Для любителей замыканий. Работает только внутри fetchAll() . В параметры функции PDO передаёт переменные для каждого полученного поля, что может быть неудобным — нет доступа к именам полей, а только к значениям. К примеру, эмуляция работы PDO::FETCH_COLUMN :
$data = $pdo ->query('SELECT name FROM users') ->fetchAll(PDO::FETCH_FUNC, function($first) );
PDO::FETCH_NAMED
Почти то же самое, что 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" >
PDO::FETCH_BOUND
Очень интересный режим, сильно отличающийся от других. В отличие от всех остальных, он не возвращает не массив или объект. Вместо этого он присваивает значения переменным, предварительно указанным с помощью bindColumn() — режим, аналогичный тому, что используется в mysqli при получении данных из подготовленного запроса. Пример есть в документации
Материал подготовлен на основе официальной статьи .