feat: Version 3.5.2 - Configuration Stripe et gestion des immeubles
- Configuration complète Stripe pour les 3 environnements (DEV/REC/PROD) * DEV: Clés TEST Pierre (mode test) * REC: Clés TEST Client (mode test) * PROD: Clés LIVE Client (mode live) - Ajout de la gestion des bases de données immeubles/bâtiments * Configuration buildings_database pour DEV/REC/PROD * Service BuildingService pour enrichissement des adresses - Optimisations pages et améliorations ergonomie - Mises à jour des dépendances Composer - Nettoyage des fichiers obsolètes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,17 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/LogService.php';
|
||||
namespace App\Services;
|
||||
|
||||
class AddressService {
|
||||
use Database;
|
||||
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Service de gestion des adresses
|
||||
*
|
||||
* Ce service interroge la base de données externe 'adresses' pour récupérer
|
||||
* les adresses géographiques dans des secteurs définis.
|
||||
*/
|
||||
class AddressService
|
||||
{
|
||||
private ?PDO $addressesDb = null;
|
||||
private PDO $mainDb;
|
||||
private LogService $logService;
|
||||
|
||||
public function __construct() {
|
||||
private $logService;
|
||||
private $buildingService;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->logService = new LogService();
|
||||
|
||||
try {
|
||||
$this->addressesDb = AddressesDatabase::getInstance();
|
||||
$this->addressesDb = \AddressesDatabase::getInstance();
|
||||
$this->logService->info('[AddressService] Connexion à la base d\'adresses réussie');
|
||||
} catch (\Exception $e) {
|
||||
// Si la connexion échoue, on continue sans la base d'adresses
|
||||
@@ -21,53 +39,59 @@ class AddressService {
|
||||
]);
|
||||
$this->addressesDb = null;
|
||||
}
|
||||
$this->mainDb = Database::getInstance();
|
||||
|
||||
$this->mainDb = \Database::getInstance();
|
||||
$this->buildingService = new BuildingService();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Vérifie si la connexion à la base d'adresses est active
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isConnected(): bool {
|
||||
public function isConnected(): bool
|
||||
{
|
||||
return $this->addressesDb !== null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Détermine le département de l'entité courante
|
||||
*
|
||||
*
|
||||
* @param int|null $entityId ID de l'entité
|
||||
* @return string|null Code département (ex: "22", "23")
|
||||
*/
|
||||
private function getDepartmentForEntity(?int $entityId = null): ?string {
|
||||
private function getDepartmentForEntity(?int $entityId = null): ?string
|
||||
{
|
||||
if (!$entityId) {
|
||||
$entityId = $_SESSION['entity_id'] ?? null;
|
||||
}
|
||||
|
||||
|
||||
if (!$entityId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
$query = "SELECT departement FROM entites WHERE id = :entity_id";
|
||||
$stmt = $this->mainDb->prepare($query);
|
||||
$stmt->execute(['entity_id' => $entityId]);
|
||||
$result = $stmt->fetch();
|
||||
|
||||
|
||||
return $result ? $result['departement'] : null;
|
||||
} catch (\Exception $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Récupère toutes les adresses contenues dans un polygone défini par des coordonnées
|
||||
* Gère automatiquement les secteurs multi-départements
|
||||
*
|
||||
*
|
||||
* @param array $coordinates Array de coordonnées [[lat, lng], [lat, lng], ...]
|
||||
* @param int|null $entityId ID de l'entité (pour déterminer le département principal)
|
||||
* @return array Array des adresses trouvées
|
||||
*/
|
||||
public function getAddressesInPolygon(array $coordinates, ?int $entityId = null): array {
|
||||
public function getAddressesInPolygon(array $coordinates, ?int $entityId = null): array
|
||||
{
|
||||
// Si pas de connexion à la base d'adresses, retourner un tableau vide
|
||||
if (!$this->addressesDb) {
|
||||
$this->logService->error('[AddressService] Pas de connexion à la base d\'adresses externe', [
|
||||
@@ -75,21 +99,20 @@ class AddressService {
|
||||
]);
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
$this->logService->info('[AddressService] Début recherche adresses', [
|
||||
'entity_id' => $entityId,
|
||||
'nb_coordinates' => count($coordinates)
|
||||
]);
|
||||
|
||||
|
||||
if (count($coordinates) < 3) {
|
||||
throw new InvalidArgumentException("Un polygone doit avoir au moins 3 points");
|
||||
}
|
||||
|
||||
|
||||
// D'abord, déterminer tous les départements touchés par ce secteur
|
||||
require_once __DIR__ . '/DepartmentBoundaryService.php';
|
||||
$boundaryService = new \DepartmentBoundaryService();
|
||||
$boundaryService = new DepartmentBoundaryService();
|
||||
$departmentsTouched = $boundaryService->getDepartmentsForSector($coordinates);
|
||||
|
||||
|
||||
if (empty($departmentsTouched)) {
|
||||
// Si aucun département n'est trouvé par analyse spatiale,
|
||||
// chercher d'abord dans le département de l'entité et ses limitrophes
|
||||
@@ -103,22 +126,22 @@ class AddressService {
|
||||
]);
|
||||
throw new RuntimeException("Impossible de déterminer le département");
|
||||
}
|
||||
|
||||
|
||||
// Obtenir les départements prioritaires (entité + limitrophes)
|
||||
$priorityDepts = $boundaryService->getPriorityDepartments($entityDept);
|
||||
|
||||
|
||||
// Log pour debug
|
||||
$this->logService->warning('[AddressService] Aucun département trouvé par analyse spatiale', [
|
||||
'departements_prioritaires' => implode(', ', $priorityDepts)
|
||||
]);
|
||||
|
||||
|
||||
// Utiliser les départements prioritaires pour la recherche
|
||||
$departmentsTouched = [];
|
||||
foreach ($priorityDepts as $deptCode) {
|
||||
$departmentsTouched[] = ['code_dept' => $deptCode];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Créer le polygone SQL à partir des coordonnées
|
||||
$polygonPoints = [];
|
||||
foreach ($coordinates as $coord) {
|
||||
@@ -127,22 +150,22 @@ class AddressService {
|
||||
}
|
||||
$polygonPoints[] = $coord[1] . ' ' . $coord[0]; // MySQL attend longitude latitude
|
||||
}
|
||||
|
||||
|
||||
// Fermer le polygone
|
||||
$polygonPoints[] = $polygonPoints[0];
|
||||
|
||||
|
||||
$polygonString = 'POLYGON((' . implode(',', $polygonPoints) . '))';
|
||||
|
||||
|
||||
// Collecter les adresses de tous les départements touchés
|
||||
$allAddresses = [];
|
||||
|
||||
|
||||
foreach ($departmentsTouched as $dept) {
|
||||
$deptCode = $dept['code_dept'];
|
||||
$tableName = "cp" . $deptCode;
|
||||
|
||||
|
||||
try {
|
||||
// Requête pour récupérer les adresses dans le polygone pour ce département
|
||||
$sql = "SELECT
|
||||
$sql = "SELECT
|
||||
id,
|
||||
numero,
|
||||
rue as voie,
|
||||
@@ -161,32 +184,32 @@ class AddressService {
|
||||
:dept_code as departement
|
||||
FROM `$tableName`
|
||||
WHERE ST_Contains(
|
||||
ST_GeomFromText(:polygon, 4326),
|
||||
ST_GeomFromText(:polygon, 4326),
|
||||
POINT(CAST(gps_lng AS DECIMAL(10,8)), CAST(gps_lat AS DECIMAL(10,8)))
|
||||
)
|
||||
AND gps_lat != ''
|
||||
AND gps_lng != ''";
|
||||
|
||||
|
||||
$stmt = $this->addressesDb->prepare($sql);
|
||||
$stmt->execute([
|
||||
'polygon' => $polygonString,
|
||||
'dept_code' => $deptCode
|
||||
]);
|
||||
|
||||
|
||||
$addresses = $stmt->fetchAll();
|
||||
|
||||
|
||||
// Ajouter les adresses à la collection globale
|
||||
foreach ($addresses as $address) {
|
||||
$allAddresses[] = $address;
|
||||
}
|
||||
|
||||
|
||||
// Log pour debug
|
||||
$this->logService->info('[AddressService] Recherche dans table', [
|
||||
'table' => $tableName,
|
||||
'departement' => $deptCode,
|
||||
'nb_adresses' => count($addresses)
|
||||
]);
|
||||
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Log l'erreur mais continue avec les autres départements
|
||||
$this->logService->error('[AddressService] Erreur SQL', [
|
||||
@@ -197,35 +220,90 @@ class AddressService {
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$this->logService->info('[AddressService] Fin recherche adresses', [
|
||||
'total_adresses' => count($allAddresses)
|
||||
]);
|
||||
return $allAddresses;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enrichit les adresses avec les données bâtiments depuis la base 'batiments'
|
||||
*
|
||||
* Pour chaque adresse trouvée, cette méthode cherche si un bâtiment existe
|
||||
* et ajoute les métadonnées (nb_log, residence, fk_habitat, etc.)
|
||||
*
|
||||
* @param array $addresses Liste d'adresses depuis getAddressesInPolygon()
|
||||
* @param int|null $entityId ID de l'entité (pour logs)
|
||||
* @return array Adresses enrichies avec données bâtiment
|
||||
*/
|
||||
public function enrichAddressesWithBuildings(array $addresses, ?int $entityId = null): array
|
||||
{
|
||||
if (empty($addresses)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$this->logService->info('[AddressService] Début enrichissement avec bâtiments', [
|
||||
'entity_id' => $entityId,
|
||||
'nb_addresses' => count($addresses)
|
||||
]);
|
||||
|
||||
try {
|
||||
$enrichedAddresses = $this->buildingService->enrichAddresses($addresses);
|
||||
|
||||
// Compter les immeubles vs maisons
|
||||
$nbImmeubles = 0;
|
||||
$nbMaisons = 0;
|
||||
foreach ($enrichedAddresses as $addr) {
|
||||
if (isset($addr['fk_habitat']) && $addr['fk_habitat'] == 2) {
|
||||
$nbImmeubles++;
|
||||
} else {
|
||||
$nbMaisons++;
|
||||
}
|
||||
}
|
||||
|
||||
$this->logService->info('[AddressService] Fin enrichissement avec bâtiments', [
|
||||
'total_adresses' => count($enrichedAddresses),
|
||||
'nb_immeubles' => $nbImmeubles,
|
||||
'nb_maisons' => $nbMaisons
|
||||
]);
|
||||
|
||||
return $enrichedAddresses;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$this->logService->error('[AddressService] Erreur lors de l\'enrichissement', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
|
||||
// En cas d'erreur, retourner les adresses sans enrichissement
|
||||
return $addresses;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les adresses dans un rayon autour d'un point
|
||||
*
|
||||
*
|
||||
* @param float $latitude Latitude du centre
|
||||
* @param float $longitude Longitude du centre
|
||||
* @param float $radiusMeters Rayon en mètres
|
||||
* @param int|null $entityId ID de l'entité (pour déterminer le département)
|
||||
* @return array Array des adresses trouvées
|
||||
*/
|
||||
public function getAddressesInRadius(float $latitude, float $longitude, float $radiusMeters, ?int $entityId = null): array {
|
||||
public function getAddressesInRadius(float $latitude, float $longitude, float $radiusMeters, ?int $entityId = null): array
|
||||
{
|
||||
// Déterminer le département
|
||||
$dept = $this->getDepartmentForEntity($entityId);
|
||||
if (!$dept) {
|
||||
throw new RuntimeException("Impossible de déterminer le département de l'entité");
|
||||
}
|
||||
|
||||
|
||||
// Nom de la table selon le département
|
||||
$tableName = "cp" . $dept;
|
||||
|
||||
|
||||
try {
|
||||
// Utiliser ST_Distance_Sphere pour calculer la distance en mètres
|
||||
$sql = "SELECT
|
||||
$sql = "SELECT
|
||||
id,
|
||||
numero,
|
||||
rue as voie,
|
||||
@@ -245,45 +323,44 @@ class AddressService {
|
||||
AND gps_lat != ''
|
||||
AND gps_lng != ''
|
||||
ORDER BY distance";
|
||||
|
||||
|
||||
$point = "POINT($longitude $latitude)";
|
||||
|
||||
|
||||
$stmt = $this->addressesDb->prepare($sql);
|
||||
$stmt->execute([
|
||||
'point' => $point,
|
||||
'radius' => $radiusMeters
|
||||
]);
|
||||
|
||||
|
||||
return $stmt->fetchAll();
|
||||
} catch (PDOException $e) {
|
||||
throw new RuntimeException("Erreur lors de la récupération des adresses dans la table $tableName : " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compte le nombre d'adresses dans un polygone
|
||||
* Gère automatiquement les secteurs multi-départements
|
||||
*
|
||||
*
|
||||
* @param array $coordinates Array de coordonnées [[lat, lng], [lat, lng], ...]
|
||||
* @param int|null $entityId ID de l'entité (pour déterminer le département principal)
|
||||
* @return int Nombre d'adresses
|
||||
*/
|
||||
public function countAddressesInPolygon(array $coordinates, ?int $entityId = null): int {
|
||||
public function countAddressesInPolygon(array $coordinates, ?int $entityId = null): int
|
||||
{
|
||||
// Si pas de connexion à la base d'adresses, retourner 0
|
||||
if (!$this->addressesDb) {
|
||||
error_log("AddressService: Pas de connexion à la base d'adresses, retour de 0 adresses");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (count($coordinates) < 3) {
|
||||
throw new InvalidArgumentException("Un polygone doit avoir au moins 3 points");
|
||||
}
|
||||
|
||||
|
||||
// D'abord, déterminer tous les départements touchés par ce secteur
|
||||
require_once __DIR__ . '/DepartmentBoundaryService.php';
|
||||
$boundaryService = new \DepartmentBoundaryService();
|
||||
$boundaryService = new DepartmentBoundaryService();
|
||||
$departmentsTouched = $boundaryService->getDepartmentsForSector($coordinates);
|
||||
|
||||
|
||||
if (empty($departmentsTouched)) {
|
||||
// Si aucun département n'est trouvé, utiliser le département de l'entité
|
||||
$dept = $this->getDepartmentForEntity($entityId);
|
||||
@@ -292,7 +369,7 @@ class AddressService {
|
||||
}
|
||||
$departmentsTouched = [['code_dept' => $dept]];
|
||||
}
|
||||
|
||||
|
||||
// Créer le polygone SQL à partir des coordonnées
|
||||
$polygonPoints = [];
|
||||
foreach ($coordinates as $coord) {
|
||||
@@ -301,19 +378,19 @@ class AddressService {
|
||||
}
|
||||
$polygonPoints[] = $coord[1] . ' ' . $coord[0]; // MySQL attend longitude latitude
|
||||
}
|
||||
|
||||
|
||||
// Fermer le polygone
|
||||
$polygonPoints[] = $polygonPoints[0];
|
||||
|
||||
|
||||
$polygonString = 'POLYGON((' . implode(',', $polygonPoints) . '))';
|
||||
|
||||
|
||||
// Compter les adresses dans tous les départements touchés
|
||||
$totalCount = 0;
|
||||
|
||||
|
||||
foreach ($departmentsTouched as $dept) {
|
||||
$deptCode = $dept['code_dept'];
|
||||
$tableName = "cp" . $deptCode;
|
||||
|
||||
|
||||
try {
|
||||
$sql = "SELECT COUNT(*) as count
|
||||
FROM `$tableName`
|
||||
@@ -323,23 +400,20 @@ class AddressService {
|
||||
)
|
||||
AND gps_lat != ''
|
||||
AND gps_lng != ''";
|
||||
|
||||
|
||||
$stmt = $this->addressesDb->prepare($sql);
|
||||
$stmt->execute(['polygon' => $polygonString]);
|
||||
|
||||
|
||||
$result = $stmt->fetch();
|
||||
$deptCount = (int)$result['count'];
|
||||
$totalCount += $deptCount;
|
||||
|
||||
// Log pour debug
|
||||
error_log("Département $deptCode : $deptCount adresses comptées");
|
||||
|
||||
|
||||
} catch (PDOException $e) {
|
||||
// Log l'erreur mais continue avec les autres départements
|
||||
error_log("Erreur de comptage pour le département $deptCode : " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $totalCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user