feat(specialty): public doctor-counts endpoint (per city)
Add GET /api/v1/specialties/doctor-counts?city_id= returning every active specialty with number_of_doctors (distinct doctors via doctor_specialties, scoped by doctor_cities when city_id is given). Make /api/v1/specialties GET public. Powers the /specialties page count. Docs updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,31 @@ class SpecialtyController extends BaseController
|
||||
return $this->success(['data' => $items]);
|
||||
}
|
||||
|
||||
#[OA\Get(
|
||||
path: '/api/v1/specialties/doctor-counts',
|
||||
summary: 'Active specialties with the number of doctors (optionally scoped to a city)',
|
||||
parameters: [
|
||||
new OA\Parameter(name: 'city_id', in: 'query', required: false, schema: new OA\Schema(type: 'integer')),
|
||||
],
|
||||
responses: [
|
||||
new OA\Response(response: 200, description: 'Specialties with number_of_doctors'),
|
||||
]
|
||||
)]
|
||||
#[Route('/api/v1/specialties/doctor-counts', methods: ['GET'])]
|
||||
public function doctorCounts(Request $request): JsonResponse
|
||||
{
|
||||
$cityId = $request->query->get('city_id');
|
||||
$counts = $this->repo->doctorCountsByCity($cityId !== null ? (int) $cityId : null);
|
||||
|
||||
$items = array_map(function (Specialty $s) use ($counts) {
|
||||
$arr = $s->toArray();
|
||||
$arr['number_of_doctors'] = $counts[$s->getId()] ?? 0;
|
||||
return $arr;
|
||||
}, $this->repo->findActive(null));
|
||||
|
||||
return $this->success(['data' => $items]);
|
||||
}
|
||||
|
||||
// ── Admin CRUD ────────────────────────────────────────────────────────────
|
||||
|
||||
#[Route('/api/v1/admin/specialty', methods: ['POST'])]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Specialty\Repository;
|
||||
|
||||
use App\Doctor\Entity\Doctor;
|
||||
use App\Specialty\Entity\Specialty;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
@@ -28,6 +29,34 @@ class SpecialtyRepository extends ServiceEntityRepository
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Count distinct doctors per specialty, optionally limited to a city.
|
||||
* Mirrors the join filters used by the public doctor list (cities/specialties).
|
||||
*
|
||||
* @return array<int,int> specialtyId => doctorCount
|
||||
*/
|
||||
public function doctorCountsByCity(?int $cityId): array
|
||||
{
|
||||
$qb = $this->getEntityManager()->createQueryBuilder()
|
||||
->select('s.id AS specialty_id', 'COUNT(DISTINCT d.id) AS cnt')
|
||||
->from(Doctor::class, 'd')
|
||||
->join('d.specialties', 's')
|
||||
->groupBy('s.id');
|
||||
|
||||
if ($cityId !== null) {
|
||||
$qb->join('d.cities', 'c')
|
||||
->andWhere('c.id = :city')
|
||||
->setParameter('city', $cityId);
|
||||
}
|
||||
|
||||
$map = [];
|
||||
foreach ($qb->getQuery()->getArrayResult() as $row) {
|
||||
$map[(int) $row['specialty_id']] = (int) $row['cnt'];
|
||||
}
|
||||
|
||||
return $map;
|
||||
}
|
||||
|
||||
public function findBySlug(string $slug): ?Specialty
|
||||
{
|
||||
return $this->findOneBy(['slug' => $slug]);
|
||||
|
||||
Reference in New Issue
Block a user