- Небольшой класс для работы с БД ( PDO )
- Вступление
- Реализация метода query
- Моя реализация
- Применение
- В заключении
- PHP-класс обертка для PDO
- Код PHP класса
- Примеры использования
- Получение одной записи из БД
- Результат:
- Получение нескольких записей из БД
- Результат:
- Получения значения
- Результат:
- Получения значений колонки
- Результат:
- Добавление в БД
- Все остальные запросы
- Класс-обёртка для PHP/PDO
- Класс
- Методы класса
- Примеры использования
Небольшой класс для работы с БД ( PDO )
Привет, Хабр! Очень много статей написано о PDO, но при этом очень мало реальных примеров. В этой статье я хочу представить свою версию класса для работы с базой данных (далее — БД). Эта статья будет полезна начинающим программистам, которые только осваивают эту технологию.
Внимание! Моё мнение может отличаться от вашего, поэтому хочу сразу сказать, что эта статья не есть истина в последней инстанции и реализация этого класса зависит от программиста и его предпочтений.
Вступление
Тут, я думаю, всё понятно, эти вещи можно не объяснять.
Далее большая часть пояснений будет содержаться в коде.
db = new PDO('mysql:host=' . $dbinfo['host'] . ';dbname=' . $dbinfo['dbname'], $dbinfo['login'], $dbinfo['password']); > > ?>
Прекрасно, мы подключились к БД. Теперь нам нужно создать метод, который позволит совершать SQL запросы.
db = new PDO('mysql:host=' . $dbinfo['host'] . ';dbname=' . $dbinfo['dbname'], $dbinfo['login'], $dbinfo['password']); > // Операции над БД public function query($sql, $params = []) < >> ?>
Реализация метода query
Мы готовы к реализации этого метода, но у нас возникает вопрос:
«Что за параметры он принимает и как он должен их использовать?»
Ответ на первый вопрос очевиден:
- $sql — переменная с текстом SQL запроса.
- $params — переменная с какими-то параметрами для запроса.
А что со вторым вопросом?
Всё так же просто, что бы ответить на этот вопрос, мы должны узнать, в каком виде нам подают эти параметры. А получаем мы их вот такими:
У незнающего человека возникает вопрос: «Что за двоеточие?» Я тут же отвечаю — такие запросы называются подготовленными и используются, дабы исключить возможность SQL инъекции.
Вернёмся к предыдущему вопросу и ответим на него:
Мы должны обойти массив $params и подставить значение в запрос.
Но сначала, мы должны подготовить запрос для подстановки этих значений. В итоге код будет выглядеть так:
db = new PDO('mysql:host=' . $dbinfo['host'] . ';dbname=' . $dbinfo['dbname'], $dbinfo['login'], $dbinfo['password']); > // Операции над БД public function query($sql, $params = []) < // Подготовка запроса $stmt = $this->db->prepare($sql); // Обход массива с параметрами // и подставление значений if ( !empty($params) ) < foreach ($params as $key =>$value) < $stmt->bindValue(":$key", $value); > > // Выполняем запрос $stmt->execute(); // Возвращаем ответ return $stmt->fetchAll(PDO::FETCH_ASSOC); > > ?>
Некоторые комментарии немного искажают истину, но при этом кардинально ничего не меняют
Мы имеем уже довольно мощный инструмент, использующий подготовленные запросы и требующий от разработчиков знание SQL, а не тонкостей класса. Но при этом, я бы зашил в этот класс типовые запросы, такие как:
- getAll() — двумерный массив, индексированный числами по порядку
- getRow() — одномерный массив, первую строку результата
Моя реализация
Эти методы могут быть реализованы по разному, но я представлю свою реализацию.
db = new PDO('mysql:host=' . $dbinfo['host'] . ';dbname=' . $dbinfo['dbname'], $dbinfo['login'], $dbinfo['password']); > // Операции над БД public function query($sql, $params = []) < // Подготовка запроса $stmt = $this->db->prepare($sql); // Обход массива с параметрами // и подставляем значения if ( !empty($params) ) < foreach ($params as $key =>$value) < $stmt->bindValue(":$key", $value); > > // Выполняя запрос $stmt->execute(); // Возвращаем ответ return $stmt->fetchAll(PDO::FETCH_ASSOC); > public function getAll($table, $sql = '', $params = []) < return $this->query("SELECT * FROM $table" . $sql, $params); > public function getRow($table, $sql = '', $params = []) < $result = $this->query("SELECT * FROM $table" . $sql, $params); return $result[0]; > > ?>
Здесь эти методы реализованы, возможно не лучшим образом, но реализованы.
Так же сюда можно добавить ещё функции, упрощающие жизнь, но лично мне этого с головой хватает.
Можно добавить следующие методы:
- getOne() — возвращает первый элемент первой строки результата
- getCol() — возвращает 1 колонку таблицы
- и т.д.
Применение
Мы написали наш класс. Теперь нам нужно протестировать его в реальном «бою».
Давайте попробуем. Для начала нам нужно создать любую таблицу в БД, допустим это будет таблица posts. Добавим туда пару записей и попробуем вывести их с помощью нашего класса.
Далее я не буду приводить код класса DB . Подразумевается, что этот класс либо написан выше, либо подключается к этому скрипту.
Так как ранее я не показал файл dbinfo.php — сейчас я приведу код этого конфигурационного файла.
'127.0.0.1', 'dbname' => 'test', 'login' => 'root', 'password' => '' ]; ?>
Это понятно и не требует объяснения, идём дальше. Теперь, давайте попросим БД test вернуть нам значение всех постов при помощи нашего класса.
// Создаём объект $db = new DB; // Получаем и выводим данные echo ""; print_r($db->getAll('posts')); ?>
На выходе мы имеем следующее:
Отлично, а теперь давайте выведем только первый пост.
// Создаём объект $db = new DB; // Получаем и выводим данные echo ""; print_r($db->getRow('posts')); ?>
Прекрасно. Ну и в конце добавим запись и выведем данные до и после.
// Создаём объект $db = new DB; // Получаем и выводим данные echo "До
"; print_r($db->getAll('posts')); echo "
После
"; $params = [ 'title' => 'Заголовок через PHP', 'author' => 'Автор через PHP' ]; $db->query('INSERT INTO `posts` ( title, author ) VALUES ( :title, :author )', $params); print_r($db->getAll('posts')); ?>
Мы получили то, что хотели и убедились в том, что этот класс рабочий.
В заключении
В конце статьи я хочу повториться и сказать, что моя реализация не идеальна, но всё таки данный класс работает и выполняет свою главную функцию - работает с базой данных. Надеюсь для вас эта статья была полезной
Ссылка на github: class DB
PHP-класс обертка для PDO
Класс значительно упрощает работу с PDO, сокращает код. Реализован на статических классах и не требует создание экземпляра класса.
Но имеет и ряд недостатков:
- Необходимо запоминать название методов.
- При ошибке в запросе PHP покажет неправильное место где произошла ошибка, что усложняет отладку.
Код PHP класса
class DB < public static $dsn = 'mysql:dbname=table;host=localhost'; public static $user = 'user'; public static $pass = 'password'; /** * Объект PDO. */ public static $dbh = null; /** * Statement Handle. */ public static $sth = null; /** * Выполняемый SQL запрос. */ public static $query = ''; /** * Подключение к БД. */ public static function getDbh() < if (!self::$dbh) < try < self::$dbh = new PDO( self::$dsn, self::$user, self::$pass, array(PDO::MYSQL_ATTR_INIT_COMMAND =>"SET NAMES 'utf8'") ); self::$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); > catch (PDOException $e) < exit('Error connecting to database: ' . $e->getMessage()); > > return self::$dbh; > /** * Закрытие соединения. */ public static function destroy() < self::$dbh = null; return self::$dbh; >/** * Получение ошибки запроса. */ public static function getError() < $info = self::$sth->errorInfo(); return (isset($info[2])) ? 'SQL: ' . $info[2] : null; > /** * Возвращает структуру таблицы в виде ассоциативного массива. */ public static function getStructure($table) < $res = array(); foreach (self::getAll("SHOW COLUMNS FROM ") as $row) < $res[$row['Field']] = (is_null($row['Default'])) ? '' : $row['Default']; >return $res; > /** * Добавление в таблицу, в случаи успеха вернет вставленный ID, иначе 0. */ public static function add($query, $param = array()) < self::$sth = self::getDbh()->prepare($query); return (self::$sth->execute((array) $param)) ? self::getDbh()->lastInsertId() : 0; > /** * Выполнение запроса. */ public static function set($query, $param = array()) < self::$sth = self::getDbh()->prepare($query); return self::$sth->execute((array) $param); > /** * Получение строки из таблицы. */ public static function getRow($query, $param = array()) < self::$sth = self::getDbh()->prepare($query); self::$sth->execute((array) $param); return self::$sth->fetch(PDO::FETCH_ASSOC); > /** * Получение всех строк из таблицы. */ public static function getAll($query, $param = array()) < self::$sth = self::getDbh()->prepare($query); self::$sth->execute((array) $param); return self::$sth->fetchAll(PDO::FETCH_ASSOC); > /** * Получение значения. */ public static function getValue($query, $param = array(), $default = null) < $result = self::getRow($query, $param); if (!empty($result)) < $result = array_shift($result); >return (empty($result)) ? $default : $result; > /** * Получение столбца таблицы. */ public static function getColumn($query, $param = array()) < self::$sth = self::getDbh()->prepare($query); self::$sth->execute((array) $param); return self::$sth->fetchAll(PDO::FETCH_COLUMN); > >
Примеры использования
Получение одной записи из БД
$item = DB::getRow("SELECT * FROM `category` WHERE `id` = ?", 1); // Или $item = DB::getRow("SELECT * FROM `category` WHERE `id` = :id", array('id' => 1)); print_r($item);
Результат:
Array ( [id] => 1 [parent] => 0 [name] => Мороженое )
Получение нескольких записей из БД
$items = DB::getAll("SELECT * FROM `category` WHERE `id` > 2"); print_r($items);
Результат:
Array ( [0] => Array ( [id] => 3 [parent] => 0 [name] => Фрукты ) [1] => Array ( [id] => 4 [parent] => 0 [name] => Ягоды ) [2] => Array ( [id] => 5 [parent] => 2 [name] => Грибы ) . )
Получения значения
$value = DB::getValue("SELECT `name` FROM `category` WHERE `id` = 2"); print_r($value);
Результат:
Получения значений колонки
$values = DB::getColumn("SELECT `name` FROM `category`"); print_r($values);
Результат:
Array ( [0] => Мороженое [1] => Овощи [2] => Фрукты [3] => Ягоды [4] => Грибы [5] => Морепродукты . )
Добавление в БД
Метод возвращает ID вставленной записи.
$insert_id = DB::add("INSERT INTO `category` SET `name` = ?", 'Яблоки');
Все остальные запросы
Выполняет запросы в БД, такие как DELETE , UPDATE , CREATE TABLE и т.д. В случаи успеха возвращает true .
DB::set("DELETE FROM `category` WHERE `id` > ? AND `parent` > ?", array(123, 0));
Класс-обёртка для PHP/PDO
В подведении итогов знакомства с PDO реализуем класс-обёртку над PHP для удобной работы с этим самым PDO ☺ Данный класс значительно упрощает работу с PDO, сокращает код. Реализован на статических методах и не требует создание экземпляра класса.
Класс
Config::DB_HOST */ const DB_HOST = '127.0.0.1'; // localhost const DB_USER = 'root'; const DB_PASSWORD = ''; const DB_NAME = 'pdo'; const CHARSET = 'utf8'; const DB_PREFIX = ''; /** * @var PDO */ static private $db; /** * @var null */ protected static $instance = null; /** * DB constructor. * @throws Exception */ public function __construct() < if (self::$instance === null)< try < self::$db = new PDO( 'mysql:host='.self::DB_HOST.';dbname='.self::DB_NAME, self::DB_USER, self::DB_PASSWORD, $options = [ PDO::ATTR_ERRMODE =>PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES ".self::CHARSET ] ); > catch (PDOException $e) < throw new Exception ($e->getMessage()); > > return self::$instance; > /** * @param $stmt * @return PDOStatement */ public static function query($stmt) < return self::$db->query($stmt); > /** * @param $stmt * @return PDOStatement */ public static function prepare($stmt) < return self::$db->prepare($stmt); > /** * @param $query * @return int */ static public function exec($query) < return self::$db->exec($query); > /** * @return string */ static public function lastInsertId() < return self::$db->lastInsertId(); > /** * @param $query * @param array $args * @return PDOStatement * @throws Exception */ public static function run($query, $args = []) < try< if (!$args) < return self::query($query); >$stmt = self::prepare($query); $stmt->execute($args); return $stmt; > catch (PDOException $e) < throw new Exception($e->getMessage()); > > /** * @param $query * @param array $args * @return mixed */ public static function getRow($query, $args = []) < return self::run($query, $args)->fetch(); > /** * @param $query * @param array $args * @return array */ public static function getRows($query, $args = []) < return self::run($query, $args)->fetchAll(); > /** * @param $query * @param array $args * @return mixed */ public static function getValue($query, $args = []) < $result = self::getRow($query, $args); if (!empty($result)) < $result = array_shift($result); >return $result; > /** * @param $query * @param array $args * @return array */ public static function getColumn($query, $args = []) < return self::run($query, $args)->fetchAll(PDO::FETCH_COLUMN); > public static function sql($query, $args = []) < self::run($query, $args); >>
Методы класса
DB::query() выполнить один запрос к БД с возвращением результата PDOStatement . его следует использовать только в том случае, если запрос не содержит необработанные данные, например из внешних источников.
DB::getRow() вернёт из БД одну запись (одна, категория, один пост и тп).
DB::getRows() вернёт из БД все записи (или несколько по условию).
DB::getValue() вернёт из БД одно значение (название категории).
DB::getColumn() вернёт из БД значения одной колонки (все названия категорий).
DB::sql() произвольный запрос.
Примеры использования
Выбрать одну категорию по id :
$data = $db::getRow("SELECT * FROM `category` WHERE `id` = ?", [$id]);
Выбрать имена всех категорий у которых parent_id больше $parent_id :
$data = $db::getRows("SELECT `title` FROM `category` WHERE `parent_id` > ?", [$parent_id]); foreach ($data as $item) < echo $item['title'].'
'; >
Выбрать имя категории по id :
$data = $db::getValue("SELECT `title` FROM `category` WHERE `id` > ?", [$id]);
Выбрать имена всех категорий:
$data = $db::getColumn("SELECT `title` FROM `category`"); foreach ($data as $item) < echo $item.'
'; >
$query = "INSERT INTO `category` ( `title`, `alias`, `parent_id`, `keywords`, `description` ) VALUES ( :title, :alias, :parent_id, :keywords, :description )"; $args = [ 'title' => $title, 'alias' => $alias, 'parent_id' => intval($parent_id), 'keywords' => $keywords, 'description' => $description ]; $db::sql($query, $args)
$query = "UPDATE `category` SET `title` = :title WHERE `id` = :id"; $args = [ 'id' => $id, 'title' => $title ]; $db::sql($query, $args)
$db::sql("DELETE FROM `category` WHERE `id` = ?", [$id]);
PDOx - быстрый, эффективный и полезный конструктор запросов и класс PDO для PHP.