---
name: add-admin-endpoint
description: Add a new paginated admin API endpoint to AdminApiController. Use when the user wants to add a backend admin list, stats, or action endpoint — things like "add an endpoint for X", "create an admin API for Y", "I need a route that lists Z".
---

## Target file
`src/Admin/Controller/AdminApiController.php`

All admin endpoints live here. The class already has `#[IsGranted('ROLE_ADMIN')]` and injects `EntityManagerInterface $em`.

## Checklist

1. **Stats endpoint** (optional but standard): a separate `#[Route('/api/v1/admin/{entity}/stats')]` method that returns counts via raw SQL (`$this->em->getConnection()->fetchOne()`). Return with `$this->success([...])`.

2. **List endpoint**: use QueryBuilder with `->getArrayResult()` — never load full entities for list queries (entity getters may not exist for all fields). Pattern:

```php
#[Route('/api/v1/admin/{entities}', methods: ['GET'])]
public function list{Entity}(Request $request): JsonResponse
{
    $page  = max(1, (int) $request->query->get('page', 1));
    $limit = min(100, max(1, (int) $request->query->get('limit', 15)));
    $search = trim((string) $request->query->get('search', ''));

    $qb = $this->em->createQueryBuilder()
        ->select('e.id, e.uuid, e.someField, e.createdAt')
        ->from(SomeEntity::class, 'e');

    if ($search !== '') {
        $qb->andWhere('e.name LIKE :s')->setParameter('s', "%$search%");
    }

    $total = (clone $qb)->select('COUNT(e.id)')->getQuery()->getSingleScalarResult();

    $items = $qb
        ->orderBy('e.id', 'DESC')
        ->setFirstResult(($page - 1) * $limit)
        ->setMaxResults($limit)
        ->getQuery()
        ->getArrayResult();

    return $this->paginated($items, (int) $total, $page, $limit);
}
```

3. **Action endpoints** (toggle status, etc.) follow this shape:

```php
#[Route('/api/v1/admin/{entities}/{uuid}/status', methods: ['POST'])]
public function toggle{Entity}Status(string $uuid): JsonResponse
{
    $entity = $this->em->getRepository(SomeEntity::class)->findOneBy(['uuid' => $uuid]);
    if (!$entity) return $this->error('NOT_FOUND', 'Entity not found', 404);

    $entity->setIsActive(!$entity->getIsActive());
    $this->em->flush();

    return $this->success(['is_active' => $entity->getIsActive()]);
}
```

## Critical rules

- **Always use `getArrayResult()`** for list queries. Never call entity getters inside admin list methods.
- `createdAt` and `updatedAt` are Unix integer timestamps — do not format them in PHP, let the frontend handle it.
- For JOINs to categories (city, state, specialty), use LEFT JOIN in DQL and select the name field directly into the array result.
- Response shape for lists: `$this->paginated($items, $total, $page, $limit)` — frontend reads `data?.data` for items and `data?.meta?.totalRecords` for count.
- Add `use` imports for any new entity class at the top of the file.
- Run `/sync-db` only if a new entity or column was added as part of this change.
