Репозитории и запросы
Методов findBy() хватает для простых условий. Поиск по части строки, сравнения «больше» и «меньше», подсчёты пишут в репозитории — классе, где собраны все запросы к одной сущности. Контроллер при этом остаётся коротким.
Свой метод
// src/Repository/PostRepository.php
namespace App\Repository;
use App\Entity\Post;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
class PostRepository extends ServiceEntityRepository
{
/** @return Post[] */
public function findPublished(): array
{
return $this->createQueryBuilder('p')
->andWhere('p.published = true')
->getQuery()
->getResult();
}
}p — короткое имя сущности в запросе. В условиях пишут имена свойств класса, а не столбцов таблицы.
Параметры
Значения подставляют через параметры — это защищает от SQL-инъекций:
public function search(string $text): array
{
return $this->createQueryBuilder('p')
->andWhere('p.title LIKE :text')
->setParameter('text', '%' . $text . '%')
->getQuery()
->getResult();
}Сортировка и ограничение
public function findLatest(int $limit = 10): array
{
return $this->createQueryBuilder('p')
->andWhere('p.published = true')
->orderBy('p.createdAt', 'DESC')
->setMaxResults($limit)
->getQuery()
->getResult();
}Для постраничного вывода добавляют setFirstResult() — сколько записей пропустить.
Что вернуть
| Метод | Результат |
|---|---|
getResult() | Массив объектов |
getOneOrNullResult() | Один объект или null |
getSingleScalarResult() | Одно значение: число, строка |
getArrayResult() | Массивы вместо объектов |
Подсчёты
public function countPublished(): int
{
return (int) $this->createQueryBuilder('p')
->select('COUNT(p.id)')
->andWhere('p.published = true')
->getQuery()
->getSingleScalarResult();
}Условия по необходимости
Запрос собирают по частям — удобно для фильтров:
public function filter(?string $text, ?int $year): array
{
$query = $this->createQueryBuilder('p');
if ($text) {
$query->andWhere('p.title LIKE :text')
->setParameter('text', "%$text%");
}
if ($year) {
$query->andWhere('p.year = :year')
->setParameter('year', $year);
}
return $query->getQuery()->getResult();
}DQL
Тот же запрос можно записать строкой на языке DQL. Он похож на SQL, но работает с сущностями:
$posts = $this->getEntityManager()
->createQuery('SELECT p FROM App\Entity\Post p WHERE p.published = true ORDER BY p.id DESC')
->getResult();Частая ошибка
Значение нельзя вставлять в текст условия: ->andWhere("p.title = '$title'") открывает путь SQL-инъекции. Всегда используйте :имя и setParameter(). И второй промах: where() стирает условия, добавленные раньше, поэтому в сборке по частям применяйте andWhere().