Каждый современный человек владеет ноутбуком, но как самому убрать возникающие...
Фреймворк 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 = new PDO("mysql:host=$host;dbname=$db", $user, $password);
$db = new PDO("sqlite:$db);
PostgreSQL:
$db = new 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");
foreach($result as $user) {
$user->showInfo();
}
Как видите всё очень просто. Нам нужно просто указать константу 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 = new 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 = new 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`) VALUES("login2")"); $stmt = $db->exec("INSERT INTO `users`(`login`) VALUES("login3")"); $db->commit(); } catch(PDOException $e) { $db->rollBack(); }
Здесь мы начинаем транзакцию с помощью метода beginTransaction() . Дальше идёт какой-то код запросов. Затем мы вызываем метод commit() , чтобы подтвердить наши изменения. Если же что-то пошло не так, то в блоке catch мы вызываем метод rollBack() , который вернёт все наши данные в предыдущее состояние.
"А зачем собственно нужны эти транзакции?" - спросите вы. Чтобы ответить на этот вопрос, рассмотрим пример, который я привёл выше. Там вы вставляем в поле "логин" значение login1, login2, login3 .
Представим, что после того, как вставились login1 и login2 , произошла какая-то ошибка. Получится, что эти данные вставлены, а login3 - нет. Во многих случаях это недопустимо и нарушит работу приложения в будущем.
Как раз для предотвращения таких ситуаций и нужны транзакции. Если наш скрипт дал сбой, то метод rollBack() вернёт всё в первоначальный вид. Т.е. login1 и login2 также не будут вставлены. Давайте сэмулируем эту ошибку.
Try { $db = new 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`) VALUES("login2")"); exit("error"); $stmt = $db->exec("INSERT INTO `users`(`login`) VALUES("login3")"); $db->commit(); } catch(PDOException $e) { $db->rollBack(); }
После вставки login1 и login2 мы выходим из скрипта с помощью функции exit() . У нас выбрасывается исключение, мы попадаем в блок catch , а там мы возвращаем всё в первоначальный вид. Теперь, если мы посмотрим в базу данных, то не увидим там login1 и login2 .
На этом моменте мы закончим. Очевидно, что здесь мы разобрали далеко не всё, что предоставляет нам PDO, однако узнали основы работы с ним. Более подробную информацию о данном расширении вы всегда можете найти на официальном сайте PHP.
Материал подготовил Владислав Андреев специально для сайта сайт
P.S. Хотите двигаться дальше в освоении PHP и ООП? Обратите внимание на премиум-уроки по различным аспектам сайтостроения, включая программирование на PHP, а также на бесплатный курс по созданию своей CMS-системы на PHP с нуля с использованием ООП:
Понравился материал и хотите отблагодарить?
Просто поделитесь с друзьями и коллегами!
Сегодня я начинаю цикл статей, посвящённых PDO , в котором мы разберём, что такое PDO , зачем он нам нужен и как его использовать.
Наверняка, многие уже слышали аббревиатуру PDO , но мало кто знает, что же это. Вот давайте сегодня об этом и поговорим.
Что такое PDO?
PDO(PHP Data Objects) - это просто интерфейс , позволяющий нам абстрагироваться от конкретной базы данных. Лучше всего показать на примере.
Mysql_connect($host, $user, $pass); // MySQL
mysql_select_db($db);
Sqlite_open($db); // sqlite
Pg_connect("host=$host, dbname=$db, user=$user, password=$pass"); // PostgreSQL
В коде выше представлены способы для подключения к трём разным базам данных: MySQL , sqlite и PostgreSQL . Как видите, функции у каждой БД отличаются.
То же самое с другими действиями. Например, выборка данных из БД.
$sql = "INSERT INTO(name, pass) VALUES($name, $pass)";
Mysql_query($sql); // MySQL
sqlite_query($sql); // sqlite
pg_query($sql); // PostgreSQL
Зачем нужен PDO?
Представим, что у нас есть огромная база данных PostgreSQL , и мы решили сменить её на MySQL . Нам придётся переписывать очень много кода, и, скорее всего, без ошибок не обойдётся. Чтобы решить эту проблему и существует PDO , позволяющий нам не зависеть от конкретной базы.
Давайте рассмотрим, как мы теперь можем подключиться.
$db = new PDO("mysql:host=$host;dbname=$db", $user, $pass); // MySQL
$db = new PDO("sqlite:host=$host;dbname=$db", $user, $pass); // sqlite
$db = new PDO("pgsql:host=$host;dbname=$db", $user, $pass); // PostgreSQL
Как видно из кода выше, в этих трёх подключениях меняется только строчка с названием БД, а остальное всё то же самое.
Чтобы что-нибудь выбрать, мы можем написать так:
$db->exec($sql);
Всё! Запрос выполнится независимо от того, какая у нас база данных.
Поддержка
PDO доступен с PHP 5.1 . Чтобы мы могли "забыть", какую базу данных мы используем, за нас всё делают их драйверы . Чтобы их активировать, зайдите в файл php.ini и найдите там строчки, которые начинаются на extension=php_pdo_ , а затем идёт название базы данных, и раскоментируйте их.
На этом всё для вступительной статьи, а в следующей мы уже начнём разбираться, как использовать PDO .
мета-тег keywords пример (4)
Цель
Как я вижу, ваша цель в этом случае двоякая:
- создавать и поддерживать одно / многоразовое соединение для каждой базы данных
- убедитесь, что соединение настроено правильно
Решение
$provider = function() { $instance = new PDO("mysql:......;charset=utf8", "username", "password"); $instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $instance->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); return $instance; }; $factory = new StructureFactory($provider);
Затем в другом файле или ниже в том же файле:
$something = $factory->create("Something"); $foobar = $factory->create("Foobar");
Сама фабрика должна выглядеть примерно так:
Class StructureFactory { protected $provider = null; protected $connection = null; public function __construct(callable $provider) { $this->provider = $provider; } public function create($name) { if ($this->connection === null) { $this->connection = call_user_func($this->provider); } return new $name($this->connection); } }
Таким образом, вы можете иметь централизованную структуру, которая гарантирует, что соединение создается только тогда, когда это необходимо. Это также облегчило бы процесс модульного тестирования и обслуживания.
Поставщик в этом случае будет найден где-то на этапе начальной загрузки. Этот подход также даст четкое местоположение, где можно определить конфигурацию, которую вы используете для подключения к БД.
Имейте в виду, что это чрезвычайно упрощенный пример . Вам также может понравиться смотреть два следующих видео:
Кроме того, я настоятельно рекомендую прочитать правильный учебник об использовании PDO (есть журнал плохого учебника онлайн).
Время от времени я вижу вопросы о подключении к базе данных.
Большинство ответов - это не то, как я это делаю, или я просто не могу правильно ответить. Так или иначе; Я никогда не думал об этом, потому что способ, которым я это делаю, работает для меня.
Но вот сумасшедшая мысль; Возможно, я делаю все это неправильно, и если это так; Мне бы очень хотелось знать, как правильно подключиться к базе данных MySQL с помощью PHP и PDO и сделать ее доступной.
Вот как я это делаю:
Во-первых, вот моя файловая структура (урезанная) :
Public_html/ * index.php * initialize/ -- load.initialize.php -- configure.php -- sessions.php
index.php
На самом верху я require("initialize/load.initialize.php"); ,
load.initialize.php
# site configurations require("configure.php"); # connect to database require("root/somewhere/connect.php"); // this file is placed outside of public_html for better security. # include classes foreach (glob("assets/classes/*.class.php") as $class_filename){ include($class_filename); } # include functions foreach (glob("assets/functions/*.func.php") as $func_filename){ include($func_filename); } # handle sessions require("sessions.php");
Я знаю, что есть лучший, или более правильный способ включения классов, но не помню, что это было. Не получили времени, чтобы изучить его еще, но я думаю, что это было что-то с autoload . что-то вроде того...
configure.php
Здесь я в основном просто переопределяю некоторые php.ini
-properties и выполняю некоторые другие глобальные настройки для сайта
connect.php
Я установил соединение в класс, чтобы другие классы могли расширять
этот...
Class connect_pdo
{
protected $dbh;
public function __construct()
{
try {
$db_host = " "; // hostname
$db_name = " "; // databasename
$db_user = " "; // username
$user_pw = " "; // password
$con = new PDO("mysql:host=".$db_host."; dbname=".$db_name, $db_user, $user_pw);
$con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$con->exec("SET CHARACTER SET utf8"); // return all sql requests as UTF-8
}
catch (PDOException $err) {
echo "harmless error message if the connection fails";
$err->getMessage() . "
";
file_put_contents("PDOErrors.txt",$err, FILE_APPEND); // write some details to an error-log outside public_html
die(); // terminate connection
}
}
public function dbh()
{
return $this->dbh;
}
}
# put database handler into a var for easier access
$con = new connect_pdo();
$con = $con->dbh();
//
Здесь я действительно верю, что есть место для массового улучшения, так как я недавно начал изучать ООП и использовал PDO вместо mysql.
Поэтому я только что последовал за несколькими учебниками для начинающих и опробовал разные вещи...
sessions.php
Помимо обработки обычных сеансов, я также инициализирую некоторые классы в сеансе следующим образом:
If (!isset($_SESSION["sqlQuery"])){ session_start(); $_SESSION["sqlQuery"] = new sqlQuery(); }
Таким образом, этот класс доступен повсеместно. Это может быть не очень хорошая практика (?) ...
Во всяком случае, это то, что этот подход позволяет мне делать везде:
Echo $_SESSION["sqlQuery"]->getAreaName("county",9); // outputs: Aust-Agder (the county name with that id in the database)
Внутри моего класса
sqlQuery , который extends мой класс
connect_pdo , у меня есть открытая функция getAreaName которая обрабатывает запрос в моей базе данных.
Думаю, довольно аккуратный.
Работает как шарм
Так вот в основном, как я это делаю.
Кроме того, всякий раз, когда мне нужно что-то извлекать из моего БД из класса, я просто делаю что-то похожее на это:
$id = 123; $sql = "SELECT whatever FROM MyTable WHERE id = :id"; $qry = $con->prepare($sql); $qry -> bindParam(":id", $id, PDO::PARAM_INT); $qry -> execute(); $get = $qry->fetch(PDO::FETCH_ASSOC);
Поскольку я вставляю соединение в переменную внутри connect_pdo.php , я просто ссылаюсь на нее, и мне хорошо идти. Оно работает. Я получаю ожидаемые результаты...
Но независимо от этого; Я был бы очень признателен, если бы вы, ребята, могли сказать мне, если я уйду отсюда. Вместо этого я должен был бы изменить области, которые я мог или должен изменить для улучшения и т. Д. ...
Я очень хочу учиться...
$dsn = "mysql:host=your_host_name;dbname=your_db_name_here"; // define host name and database name $username = "you"; // define the username $pwd="your_password"; // password try { $db = new PDO($dsn, $username, $pwd); } catch (PDOException $e) { $error_message = $e->getMessage(); echo "this is displayed because an error was found"; exit(); }
Недавно я пришел к аналогичному ответу / вопросу самостоятельно. Это то, что я сделал, в случае, если кто-то заинтересован:
args = func_get_args(); } public function __call($method, $args) { if (empty($this->db)) { $Ref = new \ReflectionClass("\PDO"); $this->db = $Ref->newInstanceArgs($this->args); } return call_user_func_array(array($this->db, $method), $args); } }
Для его вызова вам нужно только изменить эту строку:
$DB = new \Library\PDO(/* normal arguments */);
И тип-намек, если вы используете его (\ Library \ PDO $ DB).
Это действительно похоже на принятый ответ и ваш; однако он имеет значительное преимущество. Рассмотрим этот код:
$DB = new \Library\PDO(/* args */); $STH = $DB->prepare("SELECT * FROM users WHERE user = ?"); $STH->execute(array(25)); $User = $STH->fetch();
Хотя он может выглядеть как обычный PDO (он изменяется только этим \Library\ only), он фактически не инициализирует объект, пока вы не вызовете первый метод, в зависимости от того, что это такое. Это делает его более оптимизированным, поскольку создание объекта PDO немного дорого. Это прозрачный класс, или то, что он называется Ghost , форма . Вы можете обрабатывать $ DB как обычный экземпляр PDO, передавать его, выполнять те же операции и т. Д.
Я бы предложил не использовать $_SESSION для доступа к вашему соединению с БД глобально.
Вы можете сделать одну из нескольких вещей (в порядке худших для лучших практик):
- Доступ к $dbh с использованием global $dbh внутри ваших функций и классов
Используйте одноэлементный реестр и доступ к нему глобально, например:
$registry = MyRegistry::getInstance(); $dbh = $registry->getDbh();
Внесите обработчик базы данных в нужные ему классы:
Class MyClass { public function __construct($dbh) { /* ... */ } }
Однако он немного более продвинутый и требует больше «проводки» без рамки. Таким образом, если инъекция зависимостей слишком сложна для вас, используйте реестр singleton вместо совокупности глобальных переменных.
PDO (PHP Data Objects) - расширение PHP, которое реализует взаимодействие с базами данных при помощи объектов. Профит в том, что отсутствует привязка к конкретной системе управления базами данных. PDO поддерживает СУБД: MySQL, PostgreSQL, SQLite, Oracle, Microsoft SQL Server и другие.
Почему стоит использовать PDO
Функции mysql в PHP для работы с БД давно уже устарели, на сегодняшний день желательно использовать mysqli или PDO (PHP Data Objects). Кроме того, mysqli - эта библиотека, которая по большому счёту, не предназначена для использования напрямую в коде. Она может послужить хорошим строительным материалом для создания библиотеки более высокого уровня. При работе с mysqli следует также помнить об обеспечении безопасности вашего приложения, в частности о защите от SQL-инъекций. В случае использования PDO (с его подготовленными запросами), такая защита идёт уже "из коробки", главное правильно применить необходимые методы.
Тестовая база данных с таблицей
// Из консоли Windows mysql> CREATE DATABASE `pdo-test` CHARACTER SET utf8 COLLATE utf8_general_ci; USE pdo-test; CREATE TABLE categories (id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id), name VARCHAR(255) NOT NULL); INSERT INTO `categories` (`name`) VALUES ("Ноутбуки и планшеты"), ("Компьютеры и периферия"), ("Комплектующие для ПК"), ("Смартфоны и смарт-часы"), ("Телевизоры и медиа"), ("Игры и приставки"), ("Аудиотехника"), ("Фото-видеоаппаратура"), ("Офисная техника и мебель"), ("Сетевое оборудование"), ("Крупная бытовая техника"), ("Товары для кухни"), ("Красота и здоровье"), ("Товары для дома"), ("Инструменты"), ("Автотовары");Установка PDO
// Установка в Linux sudo apt update sudo apt install php7.2-mysql sudo apt-get install pdo-mysql // В php.ini добавить extension=pdo.so extension=pdo_mysql.so // На Windows, как правило драйвер уже установлен, нужно просто проверить включен ли он в php.ini extension=php_pdo_mysql.dllПроверить доступные драйвера
print_r(PDO::getAvailableDrivers());Соединение с базой данных
Соединения устанавливаются автоматически при создании объекта PDO от его базового класса.
// Пример #1. Простое подключение $db = new PDO("mysql:host=localhost;dbname=pdo", "root", "password");
При ошибке подключения PHP выдаст ошибку:
Fatal error: Uncaught PDOException: ... // Пример #2. Обработка ошибок подключения try { $dbh = new PDO("mysql:host=localhost;dbname=pdo", "root", "password"); } catch (PDOException $e) { print "Error!: " . $e->getMessage(); die(); }
В этом примере подключения мы используем конструкцию try...catch . Многие спорят о целесообразности её использования. Лично я использую try...catch , она мне не мешает.
Подготовленные и прямые запросы
В PDO два способа выполнения запросов:
- Прямой - состоит из одного шага;
- Подготовленный - состоит из двух шагов.
Прямые запросы
- query() используется для операторов, которые не вносят изменения, например SELECT . Возвращает объект PDOStatemnt , из которого с помощью методов fetch() или fetchAll извлекаются результаты запроса. Можно его сравнить с mysql resource , который возвращала mysql_query() .
- exec() используется для операторов INSERT, DELETE, UPDATE . Возвращает число обработанных запросом строк.
Прямые запросы используются только в том случае, если в запросе отсутствуют переменные и есть уверенность, что запрос безопасен и правильно экранирован.
$stmt = $db->query("SELECT * FROM categories"); while ($row = $stmt->fetch()) { echo "
"; print_r($row); }Подготовленные запросы
Если же в запрос передаётся хотя бы одна переменная, то этот запрос в обязательном порядке должен выполняться только через подготовленные выражения. Что это значит? Это обычный SQL запрос, в котором вместо переменной ставится специальный маркер - плейсхолдер. PDO поддерживает позиционные плейсхолдеры (?), для которых важен порядок передаваемых переменных, и именованные (:name), для которых порядок не важен. Примеры:
$sql = "SELECT name FROM categories WHERE id = ?"; $sql = "SELECT name FROM categories WHERE name = :name";
Чтобы выполнить такой запрос, сначала его надо подготовить с помощью метода prepare() . Она также возвращает PDO statement , но ещё без данных. Чтобы их получить, надо исполнить этот запрос, предварительно передав в него наши переменные. Передать можно двумя способами: Чаще всего можно просто выполнить метод execute() , передав ему массив с переменными:
$stmt = $pdo->prepare("SELECT `name` FROM categories WHERE `id` = ?"); $stmt->execute([$id]); $stmt = $pdo->prepare("SELECT `name` FROM categories WHERE `name` = :name"); $stmt->execute(["name" => $name]);
Как видно, в случае именованных плейсхолдеров в execute() должен передаваться массив, в котором ключи должны совпадать с именами плейсхолдеров. После этого можно извлечь результаты запроса:
$id = 1; $stmt = $db->prepare("SELECT * FROM categories WHERE `id` = ?"); $stmt->execute([$id]); $category = $stmt->fetch(PDO::FETCH_LAZY); echo "
"; print_r($category);ВАЖНО! Подготовленные запросы - основная причина использовать PDO, поскольку это единственный безопасный способ выполнения SQL запросов, в которых участвуют переменные.
Получение данных. Метод fetch()
Мы уже выше познакомились с методом fetch() , который служит для последовательного получения строк из БД. Этот метод является аналогом функции mysq_fetch_array() и ей подобных, но действует по-другому: вместо множества функций здесь используется одна, но ее поведение задается переданным параметром. В подробностях об этих параметрах будет написано в , а в качестве краткой рекомендации посоветую применять fetch() в режиме FETCH_LAZY
$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()
Также у PDO statement есть метод для получения значения единственной колонки. Очень удобно, если мы запрашиваем только одно поле - в этом случае значительно сокращается количество кода:
$id = 1; $stmt = $db->prepare("SELECT `name` FROM categories WHERE `id` = ?"); $stmt->execute([$id]); $name = $stmt->fetchColumn(); echo "Category name: ".$name;
Получение данных. Метод fetchAll()
$data = $db->query("SELECT * FROM categories")->fetchAll(PDO::FETCH_ASSOC); foreach ($data as $k => $v){ echo "Category name: ".$v["name"]."
"; }PDO и оператор LIKE
Работая с подготовленными запросами, следует понимать, что плейсхолдер может заменять только строку или число. Ни ключевое слово, ни идентификатор, ни часть строки или набор строк через плейсхолдер подставить нельзя . Поэтому для LIKE необходимо сначала подготовить строку поиска целиком, а потом ее подставлять в запрос:
$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"]."
"; }Здесь может вознинуть проблема! Поиск может не работать, потому как из базы у вас приходят данные в неправильной кодировке. Необходимо добавить кодировку в подключение, если она там не указана!
$db = new PDO("mysql:host=localhost;dbname=pdo;charset=utf8", "root", "");
PDO и оператор LIMIT
Важно! Когда PDO работает в режиме эмуляции, все данные, которые были переданы напрямую в execute() , форматируются как строки. То есть, эскейпятся и обрамляются кавычками. Поэтому LIMIT ?,? превращается в LIMIT "10", "10" и очевидным образом вызывает ошибку синтаксиса и, соответственно, пустой массив данных.
Решение #1: Отключить режим эмуляции:
$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
Решение #2: Биндить эти цифры через bindValue() , принудительно выставляя им тип PDO::PARAM_INT:
$limit = 3; $stm = $db->prepare("SELECT * FROM categories LIMIT ?"); $stm->bindValue(1, $limit, PDO::PARAM_INT); $stm->execute(); $data = $stm->fetchAll(); echo "
"; print_r($data);PDO и оператор IN
При выборке из таблицы необходимо доставать записи, соответствующие всем значениям массива.
$arr = ; $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 "
"; print_r($data);Добавление записей
$name = "Новая категория"; $query = "INSERT INTO `categories` (`name`) VALUES (:name)"; $params = [ ":name" => $name ]; $stmt = $pdo->prepare($query); $stmt->execute($params);Изменение записей
$id = 1; $name = "Изменённая запись"; $query = "UPDATE `categories` SET `name` = :name WHERE `id` = :id"; $params = [ ":id" => $id, ":name" => $name ]; $stmt = $pdo->prepare($query); $stmt->execute($params);Удаление записей
$id = 1; $query = "DELETE FROM `categories` WHERE `id` = ?"; $params = [$id]; $stmt = $pdo->prepare($query); $stmt->execute($params);Использование транзакций
try { // Начало транзакции $pdo->beginTransaction(); // ... code // Если в результате выполнения нашего кода всё прошло успешно, // то зафиксируем этот результат $pdo->commit(); } catch (Exception $e) { // Иначе, откатим транзакцию. $pdo->rollBack(); echo "Ошибка: " . $e->getMessage(); }Важно! Транзакции в PDO работают только с таблицами InnoDB
В данной заметке мы познакомились с основными понятиями PDO, его установкой, подключением к БД и самые простые возможности выборки, изменения и удаления данных. В следующих заметках мы рассмотрим ещё несколько тем, касающихся PDO.