Ajout des vérifications des types dans les formulaires (adresses, email, telephone)
This commit is contained in:
78
app/Controllers/AddressController.php
Normal file
78
app/Controllers/AddressController.php
Normal file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\Access;
|
||||
use App\Core\JsonResponse;
|
||||
use App\Services\AddressApiService;
|
||||
use PDO;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Recherche d'adresses postales (V7.25), relais vers la Base Adresse Nationale.
|
||||
*
|
||||
* Sert l'autocomplétion du champ « Adresse personnelle » de la page Agents. Le
|
||||
* relais est indispensable : la CSP interdit au navigateur d'appeler un domaine
|
||||
* tiers (`connect-src 'self'`, voir App\Core\Security).
|
||||
*
|
||||
* LECTURE PURE : aucune écriture en base, donc ni jeton CSRF ni journalisation
|
||||
* (CLAUDE.md — « les lectures ne sont jamais journalisées »). Le garde reste
|
||||
* celui de l'écriture des fiches agent, `requireService()` : c'est le seul rôle
|
||||
* qui saisit une adresse.
|
||||
*
|
||||
* Un échec du service distant ressort en 502 et non en 500 : c'est la BAN qui
|
||||
* n'a pas répondu, pas PTA qui a échoué. Côté interface, cet échec n'est jamais
|
||||
* bloquant — l'adresse reste saisissable à la main.
|
||||
*/
|
||||
final class AddressController
|
||||
{
|
||||
private Access $access;
|
||||
|
||||
public function __construct(private PDO $pdo)
|
||||
{
|
||||
$this->access = new Access($pdo);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET api/search_address.php — propositions d'adresses.
|
||||
* Paramètres GET : q (saisie en cours), limit (facultatif, 1 à 8).
|
||||
*/
|
||||
public function search(): void
|
||||
{
|
||||
$this->access->requireService();
|
||||
|
||||
$query = trim((string) ($_GET['q'] ?? ''));
|
||||
$length = function_exists('mb_strlen') ? mb_strlen($query, 'UTF-8') : strlen($query);
|
||||
if ($length < AddressApiService::MIN_QUERY_LENGTH) {
|
||||
JsonResponse::send(422, [
|
||||
'error' => sprintf('Saisissez au moins %d caractères.', AddressApiService::MIN_QUERY_LENGTH),
|
||||
]);
|
||||
}
|
||||
// Plafond de saisie : la BAN n'exploite pas au-delà, et cela borne le
|
||||
// coût d'une requête forgée.
|
||||
if ($length > 120) {
|
||||
JsonResponse::send(422, ['error' => 'Saisie trop longue pour une recherche d’adresse.']);
|
||||
}
|
||||
|
||||
$limit = filter_var($_GET['limit'] ?? 5, FILTER_VALIDATE_INT);
|
||||
if ($limit === false) {
|
||||
$limit = 5;
|
||||
}
|
||||
|
||||
try {
|
||||
$suggestions = (new AddressApiService())->search($query, $limit);
|
||||
} catch (Throwable $e) {
|
||||
JsonResponse::send(502, [
|
||||
'error' => 'Le service national des adresses est momentanément indisponible. Vous pouvez saisir l’adresse manuellement.',
|
||||
'details' => (getenv('PTA_DEBUG') ?: '0') !== '0' ? $e->getMessage() : null,
|
||||
]);
|
||||
}
|
||||
|
||||
JsonResponse::send(200, [
|
||||
'query' => $query,
|
||||
'suggestions' => $suggestions,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace App\Controllers;
|
||||
|
||||
use App\Core\Access;
|
||||
use App\Core\ContactFormat;
|
||||
use App\Core\Csrf;
|
||||
use App\Core\Journal;
|
||||
use App\Core\JsonResponse;
|
||||
@@ -484,8 +485,15 @@ final class AgentController
|
||||
if ($rate === false || $rate <= 0 || $rate > 100) {
|
||||
JsonResponse::send(422, ['error' => 'La quotité de travail doit être comprise entre 1 et 100 %.']);
|
||||
}
|
||||
if ($email !== '' && filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
|
||||
JsonResponse::send(422, ['error' => 'L’adresse e-mail saisie n’est pas valide.']);
|
||||
// Coordonnées FACULTATIVES : seule une valeur renseignée est contrôlée.
|
||||
// Les règles de forme vivent dans ContactFormat, communes à create() et
|
||||
// update(), et recopiées dans assets/js/field-format.js pour le retour
|
||||
// immédiat à la saisie.
|
||||
if ($email !== '' && !ContactFormat::isEmail($email)) {
|
||||
JsonResponse::send(422, ['error' => 'L’adresse e-mail saisie n’est pas valide. Format attendu : prenom.nom@exemple.fr']);
|
||||
}
|
||||
if ($telephone !== '' && !ContactFormat::isPhone($telephone)) {
|
||||
JsonResponse::send(422, ['error' => 'Le numéro de téléphone saisi n’est pas valide. Format attendu : 06 12 34 56 78 ou +33 6 12 34 56 78.']);
|
||||
}
|
||||
if (strlen($matricule) > 50 || strlen($nom) > 100 || strlen($prenom) > 100 || strlen($email) > 255
|
||||
|| strlen($telephone) > 30 || strlen($adresse) > 255 || strlen($diplomeLibelle) > 150 || strlen($ptaComment) > 1000) {
|
||||
@@ -624,8 +632,15 @@ final class AgentController
|
||||
|| ($contractEnd !== '' && (!$contractEndDate || $contractEndDate->format('Y-m-d') !== $contractEnd || $contractEndDate < $contractStartDate))) {
|
||||
JsonResponse::send(422, ['error' => 'Le poste, le type de contrat et les dates du contrat doivent être valides.']);
|
||||
}
|
||||
if ($email !== '' && filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
|
||||
JsonResponse::send(422, ['error' => 'L’adresse e-mail saisie n’est pas valide.']);
|
||||
// Coordonnées FACULTATIVES : seule une valeur renseignée est contrôlée.
|
||||
// Les règles de forme vivent dans ContactFormat, communes à create() et
|
||||
// update(), et recopiées dans assets/js/field-format.js pour le retour
|
||||
// immédiat à la saisie.
|
||||
if ($email !== '' && !ContactFormat::isEmail($email)) {
|
||||
JsonResponse::send(422, ['error' => 'L’adresse e-mail saisie n’est pas valide. Format attendu : prenom.nom@exemple.fr']);
|
||||
}
|
||||
if ($telephone !== '' && !ContactFormat::isPhone($telephone)) {
|
||||
JsonResponse::send(422, ['error' => 'Le numéro de téléphone saisi n’est pas valide. Format attendu : 06 12 34 56 78 ou +33 6 12 34 56 78.']);
|
||||
}
|
||||
if (strlen($nom) > 100 || strlen($prenom) > 100 || strlen($email) > 255 || strlen($telephone) > 30
|
||||
|| strlen($adresse) > 255 || strlen($diplomeLibelle) > 150 || strlen($ptaComment) > 1000) {
|
||||
|
||||
@@ -289,8 +289,8 @@ final class PageController extends Controller
|
||||
'agents' => $agentModel->allActive(),
|
||||
'structures' => (new StructureModel($this->pdo))->allActive(),
|
||||
'posts' => $agentModel->allPosts(),
|
||||
'scripts' => ['administration.js'],
|
||||
'modules' => ['administration'],
|
||||
'scripts' => ['administration.js', 'address-autocomplete.js', 'field-format.js'],
|
||||
'modules' => ['administration', 'addressAutocomplete', 'fieldFormat'],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user