Ответ
В разработке на PHP, особенно с фреймворками вроде Symfony/Laravel, выборка данных из БД осуществляется на разных уровнях абстракции. Вот основные подходы:
1. Нативный SQL (с использованием PDO): Прямое выполнение SQL-запросов. Дает максимальный контроль и производительность для сложных запросов.
$stmt = $pdo->prepare("SELECT id, name, email FROM users WHERE active = :active AND created_at > :date");
$stmt->execute(['active' => 1, 'date' => '2024-01-01']);
$users = $stmt->fetchAll(PDO::FETCH_ASSOC); // или fetch() для одной строки
2. Query Builder (Построитель запросов): Объектно-ориентированный API для построения SQL-запросов. Безопаснее и гибче, чем конкатенация строк.
// Пример с Doctrine DBAL (Symfony)
$queryBuilder = $connection->createQueryBuilder();
$users = $queryBuilder
->select('u.id', 'u.name', 'u.email')
->from('users', 'u')
->where('u.active = :active')
->andWhere('u.created_at > :date')
->setParameter('active', 1)
->setParameter('date', '2024-01-01')
->executeQuery()
->fetchAllAssociative();
3. ORM (Object-Relational Mapping, например, Doctrine ORM): Работа с данными как с объектами. Самый высокоуровневый подход.
- Методы репозитория:
$userRepository = $entityManager->getRepository(User::class); $user = $userRepository->find(123); // По первичному ключу $activeUsers = $userRepository->findBy(['active' => true]); // По простым критериям - DQL (Doctrine Query Language) или QueryBuilder Doctrine ORM: Для сложных запросов с объединениями.
$query = $entityManager->createQuery( 'SELECT u, p FROM AppEntityUser u JOIN u.posts p WHERE u.active = :active ORDER BY u.name' )->setParameter('active', 1); $users = $query->getResult(); // Возвращает массив объектов User с заполненной коллекцией posts
4. «Жадная» (EAGER) vs «Ленивая» (LAZY) загрузка:
- Ленивая (по умолчанию в Doctrine): Связанные сущности (например, посты пользователя) загружаются только при первом обращении к ним. Может привести к проблеме N+1 запроса.
- Жадная: Связанные сущности загружаются сразу в основном запросе (через
JOIN). Задается в аннотации@ORMManyToOne(fetch="EAGER")или в DQL/QueryBuilder явнымJOINи выборкой.
Критерии выбора:
- ORM (Doctrine) — для основной бизнес-логики, где удобна работа с объектами.
- Query Builder (DBAL) — для сложных отчетов или административных страниц, где ORM может быть неэффективен.
- Нативный SQL — для крайне специфичных или оптимизированных запросов, которые сложно выразить через ORM/Query Builder.
Ответ 18+ 🔞
Да ты посмотри, какая овердохуища вариантов нарисовалась! Прямо как в столовке меню: на первое, на второе и компот. Сейчас разжую, как есть, без соплей.
Ну, первое — это нативный SQL, через PDO. Старая, добрая, как дедовский топор. Максимальный контроль, скорость огонь, но и прострелить себе ногу проще простого, если неаккуратно. Всё руками пишешь, параметры сам биндишь. Для админских отчётов или какой-нибудь хитро-ёбанной аналитики — самое то. Только смотри, не накосячь с экранированием, а то будет тебе хиросима в базе данных.
$stmt = $pdo->prepare("SELECT id, name, email FROM users WHERE active = :active AND created_at > :date");
$stmt->execute(['active' => 1, 'date' => '2024-01-01']);
$users = $stmt->fetchAll(PDO::FETCH_ASSOC); // или fetch() для одной строки
Второй вариант — Query Builder, он же построитель запросов. Это уже не голый SQL, но ещё и не полный ORM. Как бы промежуточная стадия, удобная такая. Типа собираешь запрос из кубиков, и он сам за тебя параметры подставляет. Удобно, когда запрос динамический формируется, с кучей if. Риск SQL-инъекции почти нулевой, если, конечно, не пытаться быть гением и не вклеивать куски строк напрямую. В Symfony это обычно Doctrine DBAL.
// Пример с Doctrine DBAL (Symfony)
$queryBuilder = $connection->createQueryBuilder();
$users = $queryBuilder
->select('u.id', 'u.name', 'u.email')
->from('users', 'u')
->where('u.active = :active')
->andWhere('u.created_at > :date')
->setParameter('active', 1)
->setParameter('date', '2024-01-01')
->executeQuery()
->fetchAllAssociative();
И наконец, третий, самый жирный — ORM, в частности Doctrine. Это уже высший пилотаж, работа с базой как с объектами. Вообще, красота, ёпта. Забываешь про SQL, думаешь классами и связями. Но, бля, это палка о двух концах. С одной стороны — удобство, с другой — можно такую дичь наворотить с производительностью, что мама не горюй.
Вот смотри, простые штуки через репозиторий делаются в одну строку:
$userRepository = $entityManager->getRepository(User::class);
$user = $userRepository->find(123); // По первичному ключу
$activeUsers = $userRepository->findBy(['active' => true]); // По простым критериям
А для сложных вещей есть DQL (типа SQL, но для объектов) или его Query Builder. Особенно, когда нужно с джойнами.
$query = $entityManager->createQuery(
'SELECT u, p FROM AppEntityUser u JOIN u.posts p WHERE u.active = :active ORDER BY u.name'
)->setParameter('active', 1);
$users = $query->getResult(); // Возвращает массив объектов User с заполненной коллекцией posts
И вот тут главная засада подстерегает — ленивая загрузка. По умолчанию в Doctrine она включена. Это значит, что связанные посты (u.posts) из примера выше на самом деле не загрузятся в этом запросе. Они подтянутся отдельным запросом только тогда, когда ты к ним в коде первый раз обратишься. И если ты в цикле пройдёшься по 100 пользователям и для каждого выведешь его посты — у тебя будет 1 запрос за пользователей + 100 запросов за посты. Это и есть пресловутая проблема N+1, которая сожрёт твою производительность в ноль, ядрёна вошь!
Чтобы этого не было, нужно делать жадную загрузку — сразу тянуть всё нужное в одном запросе через JOIN. Либо в аннотации прописать fetch="EAGER", либо в DQL явно джойнить и селектить, как в примере выше.
Так какого хуя выбирать?
- Doctrine ORM — твой daily driver для бизнес-логики. Работаешь с сущностями, всё красиво, объектно.
- Query Builder (DBAL) — когда ORM начинает буксовать на сложных выборках, или пишешь какую-нибудь админку с фильтрами, где запрос собирается по кусочкам. Производительность лучше, чем у ORM, но не надо возиться с голыми строками SQL.
- Нативный SQL через PDO — когда всё остальное не справляется. Запрос на три экрана с оконными функциями, сложнейшая аналитика. Используй точечно, как скальпель, а не как лом.
Выбирай с умом, а то потом сиди и гадай, почему твоя страница грузится как будто на дворе 2002-й год и модемное соединение.