Правильная выборка Doctrine

Имеется Entity User: username, roles (json) и т.д.
Имеется Entity Appointment: patient_id, doctor_id и т.д.
Мне нужно составить такой запрос, чтобы вернул всех уникальных пользователей, где doctor_id = нужному. DISTINCT в данном случае не подходит из-за поля roles. Как правильно составить запрос?
Приведу в пример свой случай: имеется 3 записи в Appointment с одинаковыми doctor_id и patient_id. Когда делается выборка пользователей через inner join Appointment, то пациент возвращается трижды после запроса, что нарушает пагинацию и вывод общего количества.

    $pageQb = $this->createQueryBuilder('u');
    $pageQb->select('count(u.id)');
    $qb = $this->createQueryBuilder('u');
    $qb->join('u.appointments', 'a')
        ->andWhere("a.doctor = :doctor")
        ->setParameter("doctor", $searchParams['doctor']);
    $pageQb->join('u.appointments', 'a')
        ->andWhere("a.doctor = :doctor")
        ->setParameter("doctor", $searchParams['doctor']);
    $qb->setFirstResult(($page - 1) * $pageSize);
    $qb->setMaxResults($pageSize);
    try {
        $totalEntities = (int) $pageQb->getQuery()->getSingleScalarResult();
        $totalPages = ceil($totalEntities / $pageSize);
    } catch (NoResultException $e) {
        $totalEntities = 0;
        $totalPages = 0;
    } catch (NonUniqueResultException $e) {
        $totalEntities = 0;
        $totalPages = 0;
    }
    return [$totalEntities, $totalPages, $qb->getQuery()->getResult()];

pageQb->getQuery()->getSingleScalarResult() вернет 3, $qb->getQuery()->getResult() вернет одну уникальную запись, но если повторить этот запрос в БД, то там тоже будет 3 записи. На пагинацию это тоже влияет

doctor и patient - это Entity User, связь ManyToOne.

UPD: Итого, пришел к следующему варианту на данный момент:

    $qb = $this->createQueryBuilder('u');
    $qb->join('u.appointments', 'a')
        ->andWhere("a.doctor = :doctor")
        ->setParameter("doctor", $searchParams['doctor']);
    $qb->setFirstResult(($page - 1) * $pageSize);
    $qb->setMaxResults($pageSize);
    $paginator = new Paginator($qb, false);
    $totalEntities = $paginator->count();
    $totalPages = (int) ceil($totalEntities / $pageSize);
    $users = [];
    foreach ($paginator as $user) {
        $users[] = $user;
    }
    return [$totalEntities, $totalPages, $users];
    

Ответы (1 шт):

Автор решения: Егор Банин

Когда доктрина получает результат из базы и создаёт объекты сущностей, она не отображает каждую найденную строку на отдельную сущность, она немного умнее. Она знает, что при джойне часть колонок будет дублироваться, и она правильно обработает все строки не создавая лишних сущностей. В итоге запрос у вас корректный.

Но пагинация в случае с джойном тоже не так проста. У вас будут дублироваться колонки в строках результата (сделайте запрос без count в любом mysql-клинте и посмотрите на результат запроса с join). Чтобы вы не заморачивались с пагинацией каждый раз (хотя я советую вам заморочиться хотя бы один раз, просто чтобы понимать как это работает) доктрина содержит специальные инструменты для пагинации. Вы создаёте объект пагинатора и можете вызвать count($paginator), чтобы получить общее количество страниц. Чтобы всё сработало, не забудьте использовать setFirstResult и setMaxResults.

→ Ссылка