79 lines
2.7 KiB
PHP
79 lines
2.7 KiB
PHP
<?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,
|
||
]);
|
||
}
|
||
}
|