feat: implement specialty hierarchy handling in doctor and representation APIs, add backfill command and tests

This commit is contained in:
hamed
2026-07-19 17:30:46 +03:30
parent 21b67ec075
commit 6496ebf336
12 changed files with 539 additions and 12 deletions
@@ -9,6 +9,9 @@ use Doctrine\Persistence\ManagerRegistry;
class SpecialtyRepository extends ServiceEntityRepository
{
/** @var array<int,?int>|null */
private ?array $parentMap = null;
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Specialty::class);
@@ -62,6 +65,52 @@ class SpecialtyRepository extends ServiceEntityRepository
return $this->findOneBy(['slug' => $slug]);
}
/**
* Specialty ids plus every ancestor up to the root, unique and sorted.
* Unknown ids are dropped; a cyclic parent chain stops at the repeated id.
*
* @param int[] $ids
* @return int[]
*/
public function expandWithAncestors(array $ids): array
{
$map = $this->parentMap();
$out = [];
foreach ($ids as $id) {
$cur = (int) $id;
$seen = [];
while (array_key_exists($cur, $map) && !isset($seen[$cur])) {
$seen[$cur] = true;
$out[$cur] = true;
$cur = $map[$cur] ?? 0;
}
}
$out = array_keys($out);
sort($out);
return $out;
}
/** @return array<int,?int> id => parentId for every specialty */
private function parentMap(): array
{
if ($this->parentMap === null) {
$rows = $this->createQueryBuilder('s')
->select('s.id AS id', 'IDENTITY(s.parent) AS parent')
->getQuery()
->getArrayResult();
$this->parentMap = [];
foreach ($rows as $row) {
$this->parentMap[(int) $row['id']] = $row['parent'] !== null ? (int) $row['parent'] : null;
}
}
return $this->parentMap;
}
public function save(Specialty $specialty, bool $flush = true): void
{
$this->getEntityManager()->persist($specialty);