Files
clinicpro/src/Treatment/Repository/TreatmentCaseRepository.php
T
hamedandClaude Opus 5 250e0b0813 feat(treatment): filter treatment cases by patient record
The list could be narrowed by status, search and open-date, but not by patient
— so a patient's own file had no way to ask which courses belong to them.
`?record=` adds that bound.

patientRecord is joined once and shared with the search branch; joining it twice
under the same alias is a DQL error, and search already needed it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 16:05:35 +03:30

125 lines
5.0 KiB
PHP

<?php
namespace App\Treatment\Repository;
use App\ClinicService\Entity\ServiceItem;
use App\Patient\Entity\PatientRecord;
use App\Treatment\Entity\TreatmentCase;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<TreatmentCase>
*/
class TreatmentCaseRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, TreatmentCase::class);
}
public function findByUuid(string $uuid): ?TreatmentCase
{
return $this->findOneBy(['uuid' => $uuid]);
}
/**
* پروندهٔ بازِ همین بیمار برای همین سرویس.
*
* نقطهٔ تصمیمِ «پروندهٔ دوم نساز»: بیماری که وسط دورهٔ لیزرش نوبت دیگری از همان
* سرویس می‌گیرد، باید جلسهٔ همان دوره را بگیرد، نه یک دورهٔ موازی.
*/
public function findOpenFor(PatientRecord $record, ServiceItem $service): ?TreatmentCase
{
return $this->findOneBy([
'patientRecord' => $record,
'serviceItem' => $service,
'status' => TreatmentCase::STATUS_ACTIVE,
]);
}
/** @return TreatmentCase[] */
/**
* @param ?string $q جستجو روی نام بیمار، موبایل، کد ملی، شمارهٔ پرونده و نام سرویس.
* منشی همان کلیدی را می‌زند که در فرم نوبت می‌زند، پس هر چهار
* شناسهٔ بیمار باید بگیرد نه فقط نام.
* @param ?string $recordUuid فقط دوره‌های همین بیمار — نمای «پروندهٔ بیمار».
*/
public function findForTenant(
string $entityType,
int $entityId,
?string $status = null,
?string $q = null,
?int $openedFrom = null,
?int $openedTo = null,
?string $recordUuid = null,
): array {
$qb = $this->createQueryBuilder('c')
->where('c.entityType = :type')
->andWhere('c.entityId = :id')
->setParameter('type', $entityType)
->setParameter('id', $entityId)
->orderBy('c.openedAt', 'DESC');
$hasSearch = $q !== null && $q !== '';
// `patientRecord` را یک بار join می‌کنیم؛ هم فیلتر بیمار و هم جستجو لازمش
// دارند و دو join با یک alias خطای DQL است.
if ($hasSearch || ($recordUuid !== null && $recordUuid !== '')) {
$qb->join('c.patientRecord', 'pr');
}
if ($recordUuid !== null && $recordUuid !== '') {
$qb->andWhere('pr.uuid = :record')->setParameter('record', $recordUuid);
}
if ($status !== null) {
$qb->andWhere('c.status = :status')->setParameter('status', $status);
}
// بازه روی تاریخِ باز شدن پرونده است، نه سررسید جلسه: سؤالِ این فهرست
// «چه پرونده‌هایی در این بازه شروع شدند» است.
if ($openedFrom !== null) {
$qb->andWhere('c.openedAt >= :from')->setParameter('from', $openedFrom);
}
if ($openedTo !== null) {
$qb->andWhere('c.openedAt <= :to')->setParameter('to', $openedTo);
}
if ($hasSearch) {
/**
* پرسنل از دو راه می‌آید: کسی که به پرونده اختصاص یافته، و کسی که واقعاً
* جلسه‌ای از آن را انجام داده. مدیر که نام اپراتور را می‌زند هر دو را
* می‌خواهد — «کارهای این نفر» شامل کارِ انجام‌شده هم هست.
*/
$assignedStaff = <<<'DQL'
EXISTS (
SELECT 1 FROM App\Treatment\Entity\TreatmentCaseStaff cs
JOIN cs.staff cst
WHERE cs.treatmentCase = c AND cst.fullName LIKE :q
)
DQL;
$performingStaff = <<<'DQL'
EXISTS (
SELECT 1 FROM App\Treatment\Entity\TreatmentSession ts
JOIN ts.performedBy tsp
WHERE ts.treatmentCase = c AND tsp.fullName LIKE :q
)
DQL;
$qb->join('pr.user', 'u')
->join('c.serviceItem', 'si')
->andWhere(
'u.realName LIKE :q OR u.mobileNumber LIKE :q OR u.nationalCode LIKE :q'
. ' OR pr.recordNumber LIKE :q OR si.name LIKE :q'
. ' OR ' . $assignedStaff . ' OR ' . $performingStaff,
)
->setParameter('q', '%' . $q . '%');
}
return $qb->getQuery()->getResult();
}
}