Привет, Хабр! Очень много статей написано о PDO, но при этом очень мало реальных примеров. В этой статье я хочу представить свою версию класса для работы с базой данных (далее - БД). Эта статья будет полезна начинающим программистам, которые только осваивают эту технологию.
Внимание! Моё мнение может отличаться от вашего, поэтому хочу сразу сказать, что эта статья не есть истина в последней инстанции и реализация этого класса зависит от программиста и его предпочтений.
Вступление
Начнём с класса DB.
<?php
// use PDO - нужно только в том случае, если вы
// Используете namespace выше. Далее по коду он будет
// Но если он вам не нужен - спокойно удаляйте
use PDO;
class DB
{
public function __construct()
{
}
}
?>
Тут, я думаю, всё понятно, эти вещи можно не объяснять.
Далее большая часть пояснений будет содержаться в коде.
<?php
use PDO;
class DB
{
// Переменная, хранящая объект PDO
private $db;
public function __construct()
{
// Файл dbinfo.php возвращает массив для
// Подключения к БД
$dbinfo = require 'path/to/dbinfo.php';
// Подключение
$this->db = new PDO('mysql:host=' . $dbinfo['host'] . ';dbname=' . $dbinfo['dbname'], $dbinfo['login'], $dbinfo['password']);
}
}
?>
Прекрасно, мы подключились к БД. Теперь нам нужно создать метод, который позволит совершать SQL запросы.
<?php
use PDO;
class DB
{
// Объект класса PDO
private $db;
// Соединение с БД
public function __construct()
{
$dbinfo = require 'path/to/dbinfo.php';
$this->db = new PDO('mysql:host=' . $dbinfo['host'] . ';dbname=' . $dbinfo['dbname'], $dbinfo['login'], $dbinfo['password']);
}
// Операции над БД
public function query($sql, $params = [])
{
}
}
?>
Реализация метода query
Мы готовы к реализации этого метода, но у нас возникает вопрос:
"Что за параметры он принимает и как он должен их использовать?"
Ответ на первый вопрос очевиден:
$sql
- переменная с текстом SQL запроса.$params
- переменная с какими-то параметрами для запроса.
А что со вторым вопросом?
Всё так же просто, что бы ответить на этот вопрос, мы должны узнать, в каком виде нам подают эти параметры. А получаем мы их вот такими:
<?php
$sql = "SELECT * FROM `table` WHERE id = :id";
$params = [
'id' => 5
];
?>
У незнающего человека возникает вопрос: "Что за двоеточие?" Я тут же отвечаю - такие запросы называются подготовленными и используются, дабы исключить возможность SQL инъекции.
Вернёмся к предыдущему вопросу и ответим на него:
Мы должны обойти массив $params
и подставить значение в запрос.
Но сначала, мы должны подготовить запрос для подстановки этих значений. В итоге код будет выглядеть так:
<?php
use PDO;
class DB
{
// Объект класса PDO
private $db;
// Соединение с БД
public function __construct()
{
$dbinfo = require 'path/to/dbinfo.php';
$this->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()
- одномерный массив, первую строку результата
Моя реализация
Эти методы могут быть реализованы по разному, но я представлю свою реализацию.
<?php
use PDO;
class DB
{
// Объект класса PDO
private $db;
// Соединение с БД
public function __construct()
{
$dbinfo = require 'path/to/dbinfo.php';
$this->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 - сейчас я приведу код этого конфигурационного файла.
<?php
// Не забудьте поменять данные
return [
'host' => '127.0.0.1',
'dbname' => 'test',
'login' => 'root',
'password' => ''
];
?>
Это понятно и не требует объяснения, идём дальше. Теперь, давайте попросим БД test
вернуть нам значение всех постов при помощи нашего класса.
<?php
// class DB {...}
// Создаём объект
$db = new DB;
// Получаем и выводим данные
echo "<pre>";
print_r($db->getAll('posts'));
?>
На выходе мы имеем следующее:
Отлично, а теперь давайте выведем только первый пост.
<?php
// class DB {...}
// Создаём объект
$db = new DB;
// Получаем и выводим данные
echo "<pre>";
print_r($db->getRow('posts'));
?>
Прекрасно. Ну и в конце добавим запись и выведем данные до и после.
<?php
// class DB {...}
// Создаём объект
$db = new DB;
// Получаем и выводим данные
echo "<h1>До</h1><pre>";
print_r($db->getAll('posts'));
echo "</pre><h1>После</h1><pre>";
$params = [
'title' => 'Заголовок через PHP',
'author' => 'Автор через PHP'
];
$db->query('INSERT INTO `posts` ( title, author ) VALUES ( :title, :author )', $params);
print_r($db->getAll('posts'));
?>
Результат:
Мы получили то, что хотели и убедились в том, что этот класс рабочий.
В заключении
В конце статьи я хочу повториться и сказать, что моя реализация не идеальна, но всё таки данный класс работает и выполняет свою главную функцию - работает с базой данных. Надеюсь для вас эта статья была полезной
Ссылка на github: class DB