07. Создание класса Database и модели данных

Введение

В предыдущих уроках мы напрямую использовали PDO в каждом файле. Это удобно для начала, но когда проект растёт, код подключения к БД повторяется десятки раз.

Чтобы не дублировать и структурировать код, создадим отдельный класс Database, который будет отвечать за все операции с базой. А для каждой таблицы сделаем отдельную модель.

Инкапсуляция — это когда мы прячем внутренние детали и оставляем только удобный интерфейс для работы.

Проблема без инкапсуляции

Посмотрим на код без класса:

$pdo = new PDO('mysql:host=localhost;dbname=my_app','root','');
$stmt = $pdo->prepare('SELECT * FROM users');
$stmt->execute();
$users = $stmt->fetchAll();
Такой код быстро засоряет проект. Если завтра изменится имя базы или пароль — придётся править везде. Решение — спрятать подключение в класс.

Создаём класс Database


<?php
class Database {
  private $pdo; // подключение к базе

public function __construct($dbname = 'my_app', $host = 'localhost', $user = 'root', $pass = '') { $dsn = "mysql:host=$host;dbname=$dbname;charset=utf8mb4"; $options = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ];

$this->pdo = new PDO($dsn, $user, $pass, $options); }

public function query($sql, $params = []) { $stmt = $this->pdo->prepare($sql); $stmt->execute($params); return $stmt; } } ?>

Разбор кода

  • private $pdo — свойство, где хранится подключение к базе.
  • __construct() — выполняется при создании объекта и сразу подключается к MySQL.
  • query() — универсальный метод для выполнения SQL-запросов.

Теперь подключение выполняется один раз, и мы можем использовать этот класс везде.

Использование класса


<?php
require 'Database.php';

$db = new Database(); $users = $db->query('SELECT * FROM users')->fetchAll();

foreach ($users as $u) { echo $u['name'] . ' — ' . $u['email'] . '<br>'; } ?>

Создание модели данных

Каждая таблица в базе обычно представляется в коде отдельным классом-моделью. Например, таблица users — класс UserModel.

<?php
require 'Database.php';

class UserModel extends Database { public function getAll() { return $this->query('SELECT * FROM users')->fetchAll(); }

public function findById($id) { return $this->query('SELECT * FROM users WHERE id = ?', [$id])->fetch(); }

public function create($name, $email, $password) { $hash = password_hash($password, PASSWORD_DEFAULT); $this->query('INSERT INTO users (name,email,password) VALUES (?,?,?)', [$name, $email, $hash]); }

public function delete($id) { $this->query('DELETE FROM users WHERE id = ?', [$id]); } } ?>

Разбор методов модели

  • getAll() — возвращает всех пользователей
  • findById() — ищет пользователя по ID
  • create() — добавляет нового пользователя
  • delete() — удаляет пользователя из таблицы

Проверим работу модели


<?php
require 'UserModel.php';

$userModel = new UserModel();

// Добавим нового пользователя $userModel->create('Константин', 'konst@mail.com', '12345');

// Выведем всех пользователей $users = $userModel->getAll(); foreach ($users as $u) { echo '<p>' . htmlspecialchars($u['name']) . ' (' . htmlspecialchars($u['email']) . ')</p>'; } ?>

Инкапсуляция и наследование

Мы наследуем UserModel от Database, чтобы не повторять код подключения. Это пример повторного использования (реюза) логики.

Если потом появится ArticleModel или OrderModel, они смогут работать с базой точно так же.

Добавляем универсальный метод insert/update

Можно улучшить класс Database, добавив общие методы.

class Database {
  private $pdo;
  public function __construct() {
    $this->pdo = new PDO('mysql:host=localhost;dbname=my_app','root','');
  }

public function insert($table, $data) { $keys = array_keys($data); $fields = implode(',', $keys); $placeholders = implode(',', array_fill(0, count($data), '?')); $sql = "INSERT INTO $table ($fields) VALUES ($placeholders)"; $this->query($sql, array_values($data)); } }

$db = new Database(); $db->insert('users', [ 'name' => 'Иван', 'email' => 'ivan@example.com', 'password' => password_hash('12345', PASSWORD_DEFAULT) ]);

Что это даёт

Теперь можно вставлять данные в любую таблицу одной строкой — не нужно вручную писать SQL каждый раз.

Мини-проект: список пользователей

Создадим страницу users.php, которая покажет всех пользователей в виде таблицы.

<!doctype html>
<html lang='ru'>
<head>
  <meta charset='utf-8'>
  <title>Пользователи</title>
</head>
<body>
<h2>Список пользователей</h2>
<?php
require 'UserModel.php';
$userModel = new UserModel();
$users = $userModel->getAll();
?>
<table border='1' cellpadding='6'>
<tr><th>ID</th><th>Имя</th><th>Email</th><th>Дата регистрации</th></tr>
<?php foreach ($users as $u): ?>
<tr>
  <td><?= htmlspecialchars($u['id']) ?></td>
  <td><?= htmlspecialchars($u['name']) ?></td>
  <td><?= htmlspecialchars($u['email']) ?></td>
  <td><?= htmlspecialchars($u['created_at']) ?></td>
</tr>
<?php endforeach; ?>
</table>
</body>
</html>

Преимущества подхода с классом Database

  • Код становится структурированным и легко поддерживаемым
  • Изменить подключение можно в одном месте
  • Методы query, insert, fetch можно многократно переиспользовать
  • Модели (UserModel, ArticleModel и др.) делают проект масштабируемым

Итоги урока

Вы узнали:
  • Как создать класс Database для подключения и запросов
  • Как инкапсулировать логику PDO в одном месте
  • Как создать модель UserModel и работать с ней
  • Как использовать методы для добавления, поиска и удаления данных
  • Как построить мини-систему управления пользователями на PHP + ООП