Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Репозитории и запросы

Методов 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().

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.