Files
clinicpro/src/Doctor/Repository/DoctorRepository.php
T

196 lines
7.1 KiB
PHP

<?php
namespace App\Doctor\Repository;
use App\Auth\Entity\User;
use App\Clinic\Entity\Clinic;
use App\Doctor\Entity\Doctor;
use App\Doctor\Entity\DoctorAddress;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\Tools\Pagination\Paginator;
use Doctrine\Persistence\ManagerRegistry;
class DoctorRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Doctor::class);
}
public function findByUuid(string $uuid): ?Doctor
{
return $this->findOneBy(['uuid' => $uuid]);
}
public function findByUser(User $user): ?Doctor
{
return $this->findOneBy(['user' => $user]);
}
public function findOneByMobile(string $mobile): ?Doctor
{
return $this->createQueryBuilder('d')
->join('d.user', 'u')
->where('u.mobileNumber = :mobile')
->setParameter('mobile', $mobile)
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
}
public function findWithFilters(array $filters): array
{
$page = max(1, (int) ($filters['page'] ?? 1));
$limit = min(50, max(1, (int) ($filters['limit'] ?? 10)));
$sort = strtoupper($filters['sort'] ?? 'DESC') === 'ASC' ? 'ASC' : 'DESC';
$qb = $this->createQueryBuilder('d')
->leftJoin('d.specialties', 's')
->leftJoin('d.provinces', 'pr')
->leftJoin('d.cities', 'ci')
->distinct();
if (!empty($filters['state'])) {
$stateId = (int) $filters['state'];
$clinicIds = $this->doctorIdsViaClinicLocation('province', $stateId);
$qb->andWhere('pr.id = :state' . ($clinicIds ? ' OR d.id IN (:stateClinicDoctorIds)' : ''))
->setParameter('state', $stateId);
if ($clinicIds) {
$qb->setParameter('stateClinicDoctorIds', $clinicIds);
}
}
if (!empty($filters['city'])) {
$cityId = (int) $filters['city'];
$clinicIds = $this->doctorIdsViaClinicLocation('city', $cityId);
$qb->andWhere('ci.id = :city' . ($clinicIds ? ' OR d.id IN (:cityClinicDoctorIds)' : ''))
->setParameter('city', $cityId);
if ($clinicIds) {
$qb->setParameter('cityClinicDoctorIds', $clinicIds);
}
}
if (!empty($filters['specialty'])) {
$qb->andWhere('s.id = :specialty')->setParameter('specialty', (int) $filters['specialty']);
}
if (!empty($filters['gender'])) {
$qb->andWhere('d.gender = :gender')->setParameter('gender', $filters['gender']);
}
if (!empty($filters['degree'])) {
$qb->andWhere('d.degree = :degree')->setParameter('degree', $filters['degree']);
}
if (!empty($filters['name'])) {
$qb->andWhere('d.name LIKE :name')->setParameter('name', '%' . $filters['name'] . '%');
}
if (isset($filters['active'])) {
$qb->andWhere('d.activeDoctorAppointment = :active')
->setParameter('active', (bool) $filters['active']);
}
$qb->orderBy('d.doctorRate', $sort);
$total = (new Paginator($qb))->count();
$results = $qb->setFirstResult(($page - 1) * $limit)
->setMaxResults($limit)
->getQuery()
->getResult();
return [
'items' => $results,
'total' => $total,
'page' => $page,
'limit' => $limit,
'totalPages' => (int) ceil($total / $limit),
];
}
/**
* IDs of doctors who belong to a clinic whose own address (DoctorAddress with
* doctor IS NULL) is in the given city/province. Used so doctors without a
* direct address still surface under their clinic's location, without a
* per-row correlated subquery on the main search.
*
* @return int[]
*/
private function doctorIdsViaClinicLocation(string $field, int $locationId): array
{
$column = $field === 'city' ? 'ca.city' : 'ca.province';
$rows = $this->getEntityManager()->createQueryBuilder()
->select('cd.id AS doctorId')
->from(Clinic::class, 'cl')
->join('cl.doctors', 'cd')
->join(DoctorAddress::class, 'ca', Join::WITH, 'ca.clinicId = cl.id AND ca.doctor IS NULL')
->where(sprintf('IDENTITY(%s) = :loc', $column))
->setParameter('loc', $locationId)
->getQuery()
->getScalarResult();
return array_values(array_unique(array_map('intval', array_column($rows, 'doctorId'))));
}
public function findByClinicWithFilters(int $clinicId, array $filters): array
{
$page = max(1, (int) ($filters['page'] ?? 1));
$limit = min(50, max(1, (int) ($filters['limit'] ?? 10)));
$sort = strtoupper($filters['sort'] ?? 'DESC') === 'ASC' ? 'ASC' : 'DESC';
// Doctor has no inverse 'clinics' relation; the ManyToMany is owned by
// Clinic.doctors. Join Clinic and match its doctors collection to d.
$qb = $this->createQueryBuilder('d')
->innerJoin(Clinic::class, 'c', Join::WITH, 'd MEMBER OF c.doctors')
->leftJoin('d.specialties', 's')
->where('c.id = :clinicId')
->setParameter('clinicId', $clinicId)
->distinct();
if (!empty($filters['specialty'])) {
$qb->andWhere('s.id = :specialty')->setParameter('specialty', (int) $filters['specialty']);
}
if (!empty($filters['gender'])) {
$qb->andWhere('d.gender = :gender')->setParameter('gender', $filters['gender']);
}
if (!empty($filters['degree'])) {
$qb->andWhere('d.degree = :degree')->setParameter('degree', $filters['degree']);
}
if (!empty($filters['name'])) {
$qb->andWhere('d.name LIKE :name')->setParameter('name', '%' . $filters['name'] . '%');
}
if (isset($filters['active'])) {
$qb->andWhere('d.activeDoctorAppointment = :active')
->setParameter('active', (bool) $filters['active']);
}
$qb->orderBy('d.doctorRate', $sort);
$total = (new Paginator($qb))->count();
$results = $qb->setFirstResult(($page - 1) * $limit)
->setMaxResults($limit)
->getQuery()
->getResult();
return [
'items' => $results,
'total' => $total,
'page' => $page,
'limit' => $limit,
'totalPages' => (int) ceil($total / $limit),
];
}
public function save(Doctor $doctor, bool $flush = true): void
{
$this->getEntityManager()->persist($doctor);
if ($flush) {
$this->getEntityManager()->flush();
}
}
public function remove(Doctor $doctor, bool $flush = true): void
{
$this->getEntityManager()->remove($doctor);
if ($flush) {
$this->getEntityManager()->flush();
}
}
}