Работать напрямую с PDO в коде не очень удобно из-за большого количества низкоуровневых деталей и повторяющегося шаблонного кода. Кроме того, нам постоянно приходится преобразовывать данные в одну и в другую сторону.
Чтобы решить эту проблему, можно скрыть работу с базой за какой-то абстракцией. Один из вариантов такой изоляции называют DAO (Data Access Object).
Концепция DAO очень проста. Она сводится к созданию класса под каждую таблицу в базе данных. В классе реализуются методы, которые сохраняют, удаляют или ищут сущности в этой таблице. Когда речь идет о пользователях, наш класс DAO может выглядеть так:
class UserDAO {
private \PDO $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function save(User $user): void
{
// Если пользователь новый, выполняем вставку
// Иначе обновляем
if (is_null($user->getId())) {
$sql = "INSERT INTO users (username, phone) VALUES (?, ?)";
$stmt = $this->pdo->prepare($sql);
$username = $user->getUsername();
$phone = $user->getPhone();
$stmt->bindParam(1, $username);
$stmt->bindParam(2, $phone);
$stmt->execute();
// Извлекаем идентификатор и добавляем в сохраненный объект
$id = (int) $this->pdo->lastInsertId();
$user->setId($id);
} else {
// Здесь код обновления существующей записи
}
}
public function find(int $id): ?User
{
$sql = "SELECT * FROM users WHERE id = ?";
$stmt = $this->pdo->prepare($sql);
$stmt->execute([$id]);
$result = $stmt->fetch();
if ($result) {
$user = new User($result['username'], $result['phone']);
$user->setId($id);
return $user;
}
return null;
}
}
Метод save()
в этом классе не только сохраняет данные в базу, но и изменяет переданный объект, устанавливая внутри него идентификатор. Зачем это нужно? Код приложения работает с объектом, а не с базой напрямую. Соответственно, любые изменения в базе должны отражаться на объекте.
Если бы мы не установили идентификатор после сохранения пользователя, то не смогли бы:
- Формировать ссылки — например, ссылку на редактирование пользователя
- Сравнивать объекты друг с другом
- Отличать существующих пользователей от новых, которых мы еще не сохранили в базу данных
- Обеспечить работу кода так, чтобы полноценная версия
save()
проверяла наличие идентификатора и выполняла обновление данных, если его нет
Рассмотрим несколько примеров использования DAO:
// Устанавливаем соединение с базой данных
$conn = new PDO('sqlite::memory:');
$conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$dao = new UserDAO($conn);
$user = new User('John', '555-1234');
$user->getId(); // null
$dao->save($user);
$user->getId(); // Здесь уже выводится какой-то id
$user2 = $dao->find($user->getId());
$user2->getId() == $user1->getId();
Самостоятельная работа
- Выполните шаги из урока на своем компьютере
- Перепишите код нашего приложения, используя DAO для таблицы пользователей. Чтобы это сделать, создайте класс
User
, который будет представлять пользователя - Добавьте в DAO еще один метод, который сможет удалять пользователей из таблицы по их идентификатору
Задача со звездочкой. База данных в памяти хороша для обучения и тестирования, но для рабочего приложения не годится. Заменим SQLite в памяти на полнофункциональную базу данных. Одна из наиболее распространенных СУБД — PostgreSQL. Для подключения к ней вам потребуется установить соответствующий драйвер и поднять локально PostgreSQL. Сделать это можно по нашей инструкции
- Подключите созданный DAO к PostgreSQL. Попробуйте добавлять и удалять пользователей при помощи DAO, убедитесь, что все работает корректно
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.