Refactor doctor name handling across the application

- Removed the "دکتر" prefix from doctor names in various components and API responses to ensure consistency and clarity.
- Updated the AppointmentDetailPage, CommentsPage, DashboardPage, RatingsPage, SecretariesPage, and other relevant files to reflect the changes in doctor name formatting.
- Adjusted API documentation to align with the new naming conventions.
- Implemented validation to prevent the creation of clinics without a name and restricted users to a single clinic.
- Added tests to verify that doctor names are stored without titles and that clinic creation adheres to the new validation rules.
This commit is contained in:
hamed
2026-07-19 16:09:55 +03:30
parent 7761c37a3e
commit b05aeaf58b
30 changed files with 260 additions and 71 deletions
+20 -9
View File
@@ -13,11 +13,14 @@ use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
/**
* اصلاح یک‌بارمصرف نامِ پزشکانِ ایمپورت‌شدهٔ IRIMC که با پیشوند «دکتر» ذخیره شده‌اند.
* فقط source='irimc' را دست می‌زند؛ پزشکان manual/seed را تغییر نمی‌دهد.
* حذف پیشوند «دکتر» از نام پزشکانی که با عنوان ذخیره شده‌اند. نام پزشک هرگز نباید
* عنوان داشته باشد؛ لایهٔ نمایش خودش تصمیم می‌گیرد چطور نشانش دهد.
*
* پیش‌فرض فقط source='irimc' است. `--all` هر منبعی (seed/manual) را هم پاک می‌کند —
* لازم است چون مسیرهای ثبت‌نام تا پیش از این عنوان را حذف نمی‌کردند.
*
* php bin/console app:doctors:fix-irimc-names --dry-run
* php bin/console app:doctors:fix-irimc-names
* php bin/console app:doctors:fix-irimc-names --all
*/
#[AsCommand(
name: 'app:doctors:fix-irimc-names',
@@ -33,19 +36,21 @@ class FixIrimcNamesCommand extends Command
protected function configure(): void
{
$this->addOption('dry-run', null, InputOption::VALUE_NONE, 'Report only, change nothing');
$this->addOption('all', null, InputOption::VALUE_NONE, 'Every doctor, not just source=irimc');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$dryRun = (bool) $input->getOption('dry-run');
$all = (bool) $input->getOption('all');
$qb = $this->em->getRepository(Doctor::class)->createQueryBuilder('d');
if (!$all) {
$qb->where('d.source = :src')->setParameter('src', 'irimc');
}
/** @var Doctor[] $doctors */
$doctors = $this->em->getRepository(Doctor::class)->createQueryBuilder('d')
->where('d.source = :src')
->setParameter('src', 'irimc')
->getQuery()
->getResult();
$doctors = $qb->getQuery()->getResult();
$fixed = 0;
foreach ($doctors as $doctor) {
@@ -63,7 +68,13 @@ class FixIrimcNamesCommand extends Command
$this->em->flush();
}
$io->success(sprintf('%d نام %s (از %d پزشک IRIMC).', $fixed, $dryRun ? 'قابل اصلاح' : 'اصلاح شد', count($doctors)));
$io->success(sprintf(
'%d نام %s (از %d پزشک %s).',
$fixed,
$dryRun ? 'قابل اصلاح' : 'اصلاح شد',
count($doctors),
$all ? 'بررسی‌شده' : 'IRIMC',
));
return Command::SUCCESS;
}
+3 -2
View File
@@ -26,6 +26,7 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
use Symfony\Component\Security\Http\Attribute\IsGranted;
use App\Shared\Util\PersianText;
#[OA\Tag(name: 'Doctors')]
class DoctorController extends BaseController
@@ -105,7 +106,7 @@ class DoctorController extends BaseController
}
$data = json_decode($request->getContent(), true) ?? [];
$name = trim($data['title'] ?? $data['name'] ?? '');
$name = PersianText::stripDoctorTitle($data['title'] ?? $data['name'] ?? '');
if ($name === '') {
return $this->error(ErrorCodes::ERR_VALIDATION_001, 'نام دکتر الزامی است', 422, 'title');
@@ -350,7 +351,7 @@ class DoctorController extends BaseController
}
$data = json_decode($request->getContent(), true) ?? [];
if (!empty($data['title'])) $doctor->setName($data['title']);
if (!empty($data['title'])) $doctor->setName(PersianText::stripDoctorTitle($data['title']));
$this->hydrateDoctor($doctor, $data);
$this->doctorRepo->save($doctor);