L'admin doit pouvoir voir en un coup d'œil quelles matières sont actives (notes saisies) pour décider lesquelles peuvent être supprimées sans perte de données. Auparavant, la suppression d'une matière était silencieuse : elle cascade-deletait évaluations et notes sans avertir. La liste des matières affiche désormais les compteurs d'enseignants, classes, évaluations et notes. La suppression déclenche une confirmation explicite quand la matière contient des notes, avec récapitulatif des volumes impactés, pour rendre l'action irréversible consciente. Côté tests, un endpoint de seeding HTTP remplace les appels docker exec dans les E2E (gain ~30-60s → 5-10s par test), et un trait partagé factorise le SQL de seeding entre les deux suites fonctionnelles.
119 lines
4.4 KiB
PHP
119 lines
4.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Administration\Infrastructure\ReadModel;
|
|
|
|
use App\Administration\Application\Dto\PaginatedResult;
|
|
use App\Administration\Application\Port\PaginatedSubjectsReader;
|
|
use App\Administration\Application\Query\GetSubjects\SubjectDto;
|
|
use DateTimeImmutable;
|
|
use Doctrine\DBAL\Connection;
|
|
|
|
final readonly class DbalPaginatedSubjectsReader implements PaginatedSubjectsReader
|
|
{
|
|
public function __construct(
|
|
private Connection $connection,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* @return PaginatedResult<SubjectDto>
|
|
*/
|
|
public function findPaginated(
|
|
string $tenantId,
|
|
string $schoolId,
|
|
?string $search,
|
|
int $page,
|
|
int $limit,
|
|
): PaginatedResult {
|
|
$params = [
|
|
'tenant_id' => $tenantId,
|
|
'school_id' => $schoolId,
|
|
'status' => 'active',
|
|
];
|
|
$whereClause = 's.tenant_id = :tenant_id AND s.school_id = :school_id AND s.status = :status AND s.deleted_at IS NULL';
|
|
|
|
if ($search !== null && $search !== '') {
|
|
$whereClause .= ' AND (s.name ILIKE :search OR s.code ILIKE :search)';
|
|
$params['search'] = '%' . $search . '%';
|
|
}
|
|
|
|
$countSql = "SELECT COUNT(*) FROM subjects s WHERE {$whereClause}";
|
|
|
|
/** @var int|string|false $totalRaw */
|
|
$totalRaw = $this->connection->fetchOne($countSql, $params);
|
|
$total = (int) $totalRaw;
|
|
|
|
$offset = ($page - 1) * $limit;
|
|
|
|
$selectSql = <<<SQL
|
|
SELECT
|
|
s.id, s.name, s.code, s.color, s.description, s.status,
|
|
s.created_at, s.updated_at,
|
|
(SELECT COUNT(*) FROM teacher_assignments ta WHERE ta.subject_id = s.id AND ta.status = 'active') AS teacher_count,
|
|
(SELECT COUNT(DISTINCT ta.school_class_id) FROM teacher_assignments ta WHERE ta.subject_id = s.id AND ta.status = 'active') AS class_count,
|
|
(SELECT COUNT(*) FROM evaluations e WHERE e.subject_id = s.id AND e.tenant_id = s.tenant_id) AS evaluation_count,
|
|
(SELECT COUNT(g.id) FROM grades g INNER JOIN evaluations e ON e.id = g.evaluation_id WHERE e.subject_id = s.id AND e.tenant_id = s.tenant_id AND g.tenant_id = s.tenant_id) AS grade_count
|
|
FROM subjects s
|
|
WHERE {$whereClause}
|
|
ORDER BY s.name ASC
|
|
LIMIT :limit OFFSET :offset
|
|
SQL;
|
|
|
|
$params['limit'] = $limit;
|
|
$params['offset'] = $offset;
|
|
|
|
$rows = $this->connection->fetchAllAssociative($selectSql, $params);
|
|
|
|
$items = array_map(static function (array $row): SubjectDto {
|
|
/** @var string $id */
|
|
$id = $row['id'];
|
|
/** @var string $name */
|
|
$name = $row['name'];
|
|
/** @var string $code */
|
|
$code = $row['code'];
|
|
/** @var string|null $color */
|
|
$color = $row['color'];
|
|
/** @var string|null $description */
|
|
$description = $row['description'];
|
|
/** @var string $status */
|
|
$status = $row['status'];
|
|
/** @var string $createdAt */
|
|
$createdAt = $row['created_at'];
|
|
/** @var string $updatedAt */
|
|
$updatedAt = $row['updated_at'];
|
|
/** @var int|string $teacherCountRaw */
|
|
$teacherCountRaw = $row['teacher_count'] ?? 0;
|
|
/** @var int|string $classCountRaw */
|
|
$classCountRaw = $row['class_count'] ?? 0;
|
|
/** @var int|string $evaluationCountRaw */
|
|
$evaluationCountRaw = $row['evaluation_count'] ?? 0;
|
|
/** @var int|string $gradeCountRaw */
|
|
$gradeCountRaw = $row['grade_count'] ?? 0;
|
|
|
|
return new SubjectDto(
|
|
id: $id,
|
|
name: $name,
|
|
code: $code,
|
|
color: $color,
|
|
description: $description,
|
|
status: $status,
|
|
createdAt: new DateTimeImmutable($createdAt),
|
|
updatedAt: new DateTimeImmutable($updatedAt),
|
|
teacherCount: (int) $teacherCountRaw,
|
|
classCount: (int) $classCountRaw,
|
|
evaluationCount: (int) $evaluationCountRaw,
|
|
gradeCount: (int) $gradeCountRaw,
|
|
);
|
|
}, $rows);
|
|
|
|
return new PaginatedResult(
|
|
items: $items,
|
|
total: $total,
|
|
page: $page,
|
|
limit: $limit,
|
|
);
|
|
}
|
|
}
|