yoanbernabeu/edilabo-bundle

A Symfony bundle to parse/export Edilabo XML files.

Installs: 91

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

Type:symfony-bundle

pkg:composer/yoanbernabeu/edilabo-bundle

v0.1.3 2025-10-14 10:33 UTC

This package is auto-updated.

Last update: 2025-10-14 11:46:11 UTC


README

Un bundle Symfony pour importer et exporter les fichiers XML EDILABO (format SANDRE) utilisés pour les échanges entre laboratoires et commanditaires dans le domaine de l'environnement.

📋 Description

Ce bundle permet de parser et générer des fichiers XML conformes au scénario d'échange SANDRE COM_LABO version 1.1, utilisé pour les demandes de prestations (prélèvements et analyses) entre commanditaires et laboratoires.

Le bundle couvre 100% du format SANDRE COM_LABO v1.1, incluant tous les champs optionnels et obligatoires définis dans la spécification.

Fonctionnalités

  • Import XML : Parser des fichiers XML EDILABO vers des objets PHP
  • Export XML : Générer des fichiers XML EDILABO à partir d'objets PHP
  • Validation XML : Validation basique et stricte (XSD) des fichiers
  • Couverture complète : Tous les éléments du format SANDRE COM_LABO v1.1
  • Type-safety : 11 Enums PHP pour 314 valeurs de nomenclatures SANDRE

Warning

Ce bundle est en cours de développement actif :

  • Aucune garantie de rétrocompatibilité entre les versions
  • ⚠️ L'API peut changer radicalement sans préavis
  • 💥 Des breaking changes sont attendus dans les futures versions

N'utilisez pas en production avant la sortie d'une version stable.

🚀 Installation

composer require yoanbernabeu/edilabo-bundle

📖 Utilisation

Configuration dans Symfony

Le bundle s'enregistre automatiquement dans votre application Symfony. Le service principal EdilaboInterface est disponible via l'injection de dépendances.

Exemples d'utilisation

Injection dans un contrôleur

<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use YoanBernabeu\EdilaboBundle\EdilaboInterface;

class LabController extends AbstractController
{
    public function __construct(
        private readonly EdilaboInterface $edilabo
    ) {
    }

    public function parse(): Response
    {
        // Parser un fichier
        $comLabo = $this->edilabo->parseFile('/path/to/commande.xml');

        // Accéder aux données
        $scenario = $comLabo->getScenario();
        $codeScenario = $scenario->getCodeScenario(); // "COM_LABO"
        $version = $scenario->getVersionScenario(); // "1"

        // Accéder aux intervenants
        foreach ($comLabo->getIntervenants() as $intervenant) {
            $nom = $intervenant->getNomIntervenant();
            $code = $intervenant->getCdIntervenant();
        }

        // Accéder aux demandes
        foreach ($comLabo->getDemandes() as $demande) {
            $reference = $demande->getCdDemandeCommanditaire();
            
            // Les champs nomenclature SANDRE sont des Enums type-safe
            $typeDemande = $demande->getTypeDemande(); // TypeDemande Enum
            $contexte = $demande->getContexteCodification(); // ContexteCodification Enum
            
            // Une demande peut avoir plusieurs prélèvements
            foreach ($demande->getPrelevements() as $prelevement) {
                $datePrel = $prelevement->getDatePrel();
                $support = $prelevement->getSupport();
                
                // Les finalités sont des Enums
                foreach ($prelevement->getFinalitesPrel() as $finalite) {
                    $code = $finalite->getCode(); // '1', '2', 'AS', 'CS', etc.
                    $libelle = $finalite->getLibelle(); // 'Demande de prélèvement(s)', etc.
                }
                
                foreach ($prelevement->getEchantillons() as $echantillon) {
                    $ref = $echantillon->getRefEchantillonCommanditaire();
                }
            }
        }

        return new Response('Parsing terminé');
    }
}

Parser depuis une chaîne XML

use YoanBernabeu\EdilaboBundle\EdilaboInterface;

$xmlContent = file_get_contents('/path/to/file.xml');
$comLabo = $edilabo->parse($xmlContent);

Parser depuis un fichier

use YoanBernabeu\EdilaboBundle\EdilaboInterface;

$comLabo = $edilabo->parseFile('/path/to/commande.xml');

Valider un XML

use YoanBernabeu\EdilaboBundle\EdilaboInterface;
use YoanBernabeu\EdilaboBundle\Exception\E1_MalformedXmlException;
use YoanBernabeu\EdilaboBundle\Exception\E2_SchemaValidationException;

try {
    $isValid = $edilabo->validate($xmlContent);
    echo "XML valide !";
} catch (E1_MalformedXmlException $e) {
    echo "[E1] XML mal formaté : " . $e->getMessage();
} catch (E2_SchemaValidationException $e) {
    echo "[E2] Structure invalide : " . $e->getMessage();
}

Exporter vers XML

Le bundle permet également de créer et exporter des fichiers XML EDILABO à partir d'objets PHP.

Export vers une chaîne XML :

use YoanBernabeu\EdilaboBundle\Builder\ComLaboBuilder;
use YoanBernabeu\EdilaboBundle\Builder\ScenarioBuilder;
use YoanBernabeu\EdilaboBundle\Model\Emetteur;
use YoanBernabeu\EdilaboBundle\Model\Destinataire;

// Créer un ComLabo avec les builders fluides
$emetteur = new Emetteur(
    cdIntervenant: '12345678900001',
    nomIntervenant: 'Mon Entreprise',
    service: null,
    contact: null
);

$destinataire = new Destinataire(
    cdIntervenant: '98765432100002',
    nomIntervenant: 'Laboratoire XYZ',
    service: null,
    contact: null
);

$scenario = ScenarioBuilder::create()
    ->setReferenceFichierEnvoi('CMD_2025_001.xml')
    ->setDateCreationFichier('2025-01-15')
    ->setEmetteur($emetteur)
    ->setDestinataire($destinataire)
    ->build();

$comLabo = ComLaboBuilder::create()
    ->setScenario($scenario)
    ->build();

// Export vers XML
$xml = $edilabo->export($comLabo);

Export vers un fichier :

$edilabo->exportToFile($comLabo, '/path/to/export.xml');

Exemple complet avec demande et prélèvement :

use YoanBernabeu\EdilaboBundle\Builder\DemandeBuilder;
use YoanBernabeu\EdilaboBundle\Builder\PrelevementBuilder;
use YoanBernabeu\EdilaboBundle\Model\Commanditaire;
use YoanBernabeu\EdilaboBundle\Model\Prestataire;
use YoanBernabeu\EdilaboBundle\Model\Echantillon;
use YoanBernabeu\EdilaboBundle\Model\GroupeParametresRef;
use YoanBernabeu\EdilaboBundle\Model\Laboratoire;
use YoanBernabeu\EdilaboBundle\Model\StationPrelevementRef;
use YoanBernabeu\EdilaboBundle\Model\Support;
use YoanBernabeu\EdilaboBundle\Model\Preleveur;
use YoanBernabeu\EdilaboBundle\Model\Enum\ContexteCodification;
use YoanBernabeu\EdilaboBundle\Model\Enum\TypeDemande;
use YoanBernabeu\EdilaboBundle\Model\Enum\FinalitePrelevement;
use YoanBernabeu\EdilaboBundle\Model\Enum\NatureProduit;
use YoanBernabeu\EdilaboBundle\Model\Enum\UsageProduit;

// Créer un échantillon
$echantillon = new Echantillon(
    refEchantillonCommanditaire: 'ECH_2025_001',
    refEchantillonPrel: null,
    refEchantillonLabo: null,
    groupesParametres: [new GroupeParametresRef('GP_SOL')],
    laboratoire: new Laboratoire('98765432100002'),
    payeur: null,
    methodeTransport: null,
    analyses: [],
    commemoratifs: []
);

// Créer un prélèvement avec le builder et des Enums type-safe
$prelevement = PrelevementBuilder::create()
    ->setCdPrelevement('PREL_2025_001')
    ->setDatePrel('2025-01-10')
    ->setDelaiPrel('5')
    ->setStationPrelevement(new StationPrelevementRef('STATION_001'))
    ->setSupport(new Support('25', 'Sol'))
    ->setPreleveur(new Preleveur('11122233344455'))
    ->addFinalitePrel(FinalitePrelevement::CONTROLE_SANITAIRE_REGLEMENTAIRE)
    ->setNatureProduit(NatureProduit::EAU_SURFACE)
    ->setUsageProduit(UsageProduit::CONSOMMATION_HUMAINE)
    ->addEchantillon($echantillon)
    ->build();

// Créer une demande avec le builder et des Enums type-safe
$demande = DemandeBuilder::create()
    ->setCdDemandeCommanditaire('CMD_2025_001')
    ->setCommanditaire(new Commanditaire('12345678900001'))
    ->setPrestataire(new Prestataire('98765432100002'))
    ->setTypeDemande(TypeDemande::ANALYSE)
    ->setContexteCodification(ContexteCodification::DEMANDE_ET_RESULTATS)
    ->setReferenceMarche('MARCHE_2025')
    ->setCommentairesCommanditaire('Analyse de sol')
    ->addPrelevement($prelevement)
    ->build();

// Construire le ComLabo complet
$comLabo = ComLaboBuilder::create()
    ->setScenario($scenario)
    ->addDemande($demande)
    ->build();

// Export
$xml = $edilabo->export($comLabo);

Validation XSD stricte

Le bundle permet également de valider les fichiers XML contre le schéma XSD officiel SANDRE. Cette validation est beaucoup plus stricte que la validation basique :

use YoanBernabeu\EdilaboBundle\EdilaboInterface;
use YoanBernabeu\EdilaboBundle\Exception\E1_MalformedXmlException;
use YoanBernabeu\EdilaboBundle\Exception\E2_SchemaValidationException;
use YoanBernabeu\EdilaboBundle\Validator\XsdValidator;

try {
    // Validation avec le schéma XSD officiel SANDRE (connexion internet requise)
    $edilabo->validateWithXsd($xmlContent);
    echo "XML conforme au schéma SANDRE !";
} catch (E1_MalformedXmlException $e) {
    echo "[E1] XML mal formaté : " . $e->getMessage();
} catch (E2_SchemaValidationException $e) {
    // Affiche les erreurs de conformité détaillées
    echo "[E2] XML non conforme au schéma : " . $e->getMessage();
}

Vous pouvez également spécifier un chemin de schéma personnalisé :

// Utiliser le schéma officiel SANDRE (par défaut - constante)
$edilabo->validateWithXsd($xmlContent);
// Équivalent à :
$edilabo->validateWithXsd($xmlContent, XsdValidator::SANDRE_SCHEMA_URL);

// Utiliser un schéma local personnalisé
$edilabo->validateWithXsd($xmlContent, '/path/to/schema.xsd');

⚠️ Performance : La validation XSD est stricte mais lente (~50s par fichier car elle télécharge le schéma distant et résout les imports). Utilisez-la uniquement pour des cas où la validation basique validate() ne suffit pas (doute sur la conformité du XML...).

🎯 Enums de nomenclature SANDRE

Le bundle fournit 11 Enums PHP couvrant 314 valeurs des nomenclatures SANDRE pour garantir la sécurité des types et faciliter le développement avec autocomplétion IDE.

Enums disponibles

Enum Valeurs Utilisation
ContexteCodification 2 Contexte d'échange EDILABO
TypeDemande 3 Type de demande (prélèvement, analyse, mixte)
FinalitePrelevement 37 Finalité du prélèvement (contrôle sanitaire, auto-surveillance, etc.)
NatureProduit 123 Nature du produit prélevé (eaux, boues, effluents agricoles, composts, engrais)
UsageProduit 7 Usage du produit (thermalisme, baignade, consommation, etc.)
ZoneVerticaleProspectee 16 Zone verticale du plan d'eau (thermocline, hypolimnion, etc.)
OrigineCodeStation 11 Origine du code de la station de prélèvement
OrigineCodeLocal 11 Origine du code du local de prélèvement
SystemeProjection 68 Système de projection géographique (Lambert, etc.)
SystemeAltimetrique 33 Système altimétrique (NGF, IGN, etc.)
LocalisationAnalyse 3 Localisation de l'analyse (in situ, laboratoire)

Utilisation des Enums

use YoanBernabeu\EdilaboBundle\Model\Enum\TypeDemande;
use YoanBernabeu\EdilaboBundle\Model\Enum\FinalitePrelevement;
use YoanBernabeu\EdilaboBundle\Model\Enum\NatureProduit;

// Créer une demande avec type-safety
$demande = DemandeBuilder::create()
    ->setTypeDemande(TypeDemande::ANALYSE) // Enum au lieu de string
    ->build();

// Accéder aux valeurs de l'Enum
$typeDemande = $demande->getTypeDemande();
echo $typeDemande->getCode();    // '2'
echo $typeDemande->getLibelle(); // 'Demande d\'analyse(s)'

// Créer un prélèvement avec des Enums
$prelevement = PrelevementBuilder::create()
    ->addFinalitePrel(FinalitePrelevement::CONTROLE_SANITAIRE_REGLEMENTAIRE)
    ->setNatureProduit(NatureProduit::EAU_SURFACE)
    ->build();

// Accéder aux finalités (array d'Enums)
foreach ($prelevement->getFinalitesPrel() as $finalite) {
    echo $finalite->getMnemonique(); // 'CS'
    echo $finalite->getLibelle();    // 'Contrôle sanitaire réglementaire'
    echo $finalite->getCode();       // 'CS'
}

// Parser depuis XML : les valeurs sont automatiquement converties en Enums
$comLabo = $edilabo->parseFile('commande.xml');
$demande = $comLabo->getDemandes()[0];
$type = $demande->getTypeDemande(); // TypeDemande Enum, pas string

Avantages des Enums

  • Type-safety : Impossible d'assigner une valeur invalide
  • Autocomplétion IDE : Toutes les valeurs possibles sont suggérées
  • Documentation : Libellés et mnémoniques disponibles via méthodes
  • Validation : Erreur à la compilation si valeur incorrecte
  • Refactoring : Renommage sûr dans toute la codebase

🏗️ Structure des objets

ComLabo (racine)

L'objet principal qui contient toutes les données du fichier EDILABO.

$comLabo->getScenario();              // Scenario
$comLabo->getIntervenants();          // Intervenant[]
$comLabo->getStationsPrelevement();   // StationPrelevement[]
$comLabo->getGroupesParametres();     // GroupeParametres[]
$comLabo->getDemandes();              // Demande[]

Scenario

Informations sur le scénario d'échange.

$scenario->getCodeScenario();          // "COM_LABO"
$scenario->getVersionScenario();       // "1"
$scenario->getNomScenario();           // Nom du scénario
$scenario->getDateCreationFichier();   // Date de création
$scenario->getReferenceFichierEnvoi(); // Référence du fichier
$scenario->getEmetteur();              // Emetteur (avec Service et Contact)
$scenario->getDestinataire();          // Destinataire (avec Service et Contact)
$scenario->getReferentiels();          // Referentiel[] - Référentiels SANDRE utilisés

Intervenant

Représente un acteur (commanditaire, prestataire, laboratoire).

$intervenant->getCdIntervenant();      // Code SIRET
$intervenant->getNomIntervenant();     // Nom de l'intervenant
$intervenant->getMnIntervenant();      // Mention
$intervenant->getRueIntervenant();     // Rue
$intervenant->getVilleIntervenant();   // Ville
$intervenant->getCPIntervenant();      // Code postal
// ... et autres champs d'adresse (BP, Immeuble, Lieu-dit, Département)

StationPrelevement

Informations sur le lieu de prélèvement.

$station->getCdStationPrelevement();           // Code de la station
$station->getLbStationPrelevement();           // Libellé
$station->getOrigineCode();                    // OrigineCodeStation Enum
$station->getTypeStationPrelevement();         // Type
$station->getAdresseStationPrelevement();      // Adresse
$station->getZoneVerticaleProspectee();        // ZoneVerticaleProspectee Enum (16 valeurs)
$station->getCoordXStationPrelevement();       // Coordonnée X
$station->getCoordYStationPrelevement();       // Coordonnée Y
$station->getProjectStationPrelevement();      // Projection
$station->getAltitudeStationPrelevement();     // SystemeProjection Enum (68 valeurs)
$station->getProjectAltiStationPrelevement();  // SystemeAltimetrique Enum (33 valeurs)
$station->getCommune();                        // Commune
$station->getLocauxPrelevement();              // LocalPrelevement[] - Locaux détaillés

GroupeParametres

Ensemble d'analyses regroupées.

$groupe->getCdGroupeParametres();     // Code du groupe
$groupe->getLbGroupeParametres();     // Libellé
$groupe->getAnalyses();               // Analyse[]

Analyse

Détails d'une analyse de laboratoire.

$analyse->getInsituAna();             // LocalisationAnalyse Enum (IN_SITU, LABORATOIRE)
$analyse->getParametre();             // Parametre
$analyse->getFractionAnalysee();      // FractionAnalysee
$analyse->getUniteReference();        // UniteReference
$analyse->getMethode();               // Methode (optionnel)
$analyse->getCommentairesAna();       // Commentaires
$analyse->getMethFractionnement();    // Méthode de fractionnement
$analyse->getMethExtraction();        // Méthode d'extraction
$analyse->getSolvant();               // Solvant utilisé
$analyse->getPayeur();                // Payeur de l'analyse
$analyse->getCommemoratifs();         // Commemoratif[]

Demande

Une demande de prestations.

$demande->getCdDemandeCommanditaire();      // Code de la demande
$demande->getCommanditaire();               // Commanditaire
$demande->getPrestataire();                 // Prestataire
$demande->getTypeDemande();                 // TypeDemande Enum (PRELEVEMENT, ANALYSE, MIXTE)
$demande->getContexteCodification();        // ContexteCodification Enum
$demande->getCdDemandePrestataire();        // Code demande prestataire
$demande->getDateDemande();                 // Date de la demande
$demande->getLbDemande();                   // Libellé
$demande->getDateDebutApplicationDemande(); // Date début application
$demande->getDateFinApplicationDemande();   // Date fin application
$demande->getReferenceMarche();             // Référence marché
$demande->getPayeur();                      // Payeur
$demande->getDestinatairesRsAna();          // DestinataireRsAna[] - Destinataires des résultats
$demande->getPrelevements();                // Prelevement[] - Prélèvements
$demande->getCommemoratifs();               // Commemoratif[]

Prelevement

Informations sur un prélèvement.

$prelevement->getCdPrelevement();            // Code du prélèvement
$prelevement->getDatePrel();                 // Date du prélèvement
$prelevement->getHeurePrel();                // Heure du prélèvement
$prelevement->getDelaiPrel();                // Délai de prélèvement
$prelevement->getDureePrel();                // Durée du prélèvement
$prelevement->getReferencePrel();            // Référence
$prelevement->getFinalitesPrel();            // FinalitePrelevement[] - Finalités (Enums)
$prelevement->getCommentairesPrel();         // Commentaires
$prelevement->getRisqueProduit();            // Risque produit
$prelevement->getStationPrelevement();       // StationPrelevementRef
$prelevement->getLocalPrelevement();         // LocalPrelevementRef
$prelevement->getLocalExactePrel();          // Localisation exacte
$prelevement->getSupport();                  // Support (Sol, Eau, etc.)
$prelevement->getMethodePrel();              // Méthode de prélèvement
$prelevement->getNatureProduit();            // NatureProduit Enum (123 valeurs)
$prelevement->getUsageProduit();             // UsageProduit Enum (7 valeurs)
$prelevement->getNormeProduit();             // Norme du produit
$prelevement->getPreleveur();                // Preleveur
$prelevement->getPayeur();                   // Payeur
$prelevement->getMesuresEnvironnementales(); // MesureEnvironnementale[]
$prelevement->getEchantillons();             // Echantillon[]
$prelevement->getCommemoratifs();            // Commemoratif[]

Echantillon

Représente un échantillon prélevé.

$echantillon->getRefEchantillonCommanditaire(); // Référence commanditaire
$echantillon->getRefEchantillonPrel();          // Référence préleveur
$echantillon->getRefEchantillonLabo();          // Référence laboratoire
$echantillon->getGroupesParametres();           // GroupeParametresRef[] - Groupes de paramètres
$echantillon->getLaboratoire();                 // Laboratoire
$echantillon->getPayeur();                      // Payeur
$echantillon->getMethodeTransport();            // Méthode de transport
$echantillon->getAnalyses();                    // Analyse[] - Analyses spécifiques
$echantillon->getCommemoratifs();               // Commemoratif[]

🔧 Gestion des erreurs

Le bundle implémente une hiérarchie d'exceptions conforme aux préconisations SANDRE pour la gestion des erreurs dans les fichiers d'échange. Chaque exception correspond à un code d'erreur SANDRE (E0 à E4) avec sa définition officielle.

Hiérarchie des exceptions SANDRE

Toutes les exceptions héritent de SandreException qui fournit :

  • getErrorCode() : Retourne le code SANDRE (E0, E1, E2, E3, E4)
  • getErrorDefinition() : Retourne la définition complète selon SANDRE
  • getMessage() : Message préfixé automatiquement avec [E0], [E1], etc.

E0_FileCorruptedException - Fichier endommagé

Code E0 : Fichier XML endommagé, non lisible (lors de sa génération ou de son transport).

Le fichier XML en tant que tel est endommagé. L'application ne peut ouvrir ou lire le contenu du fichier.

use YoanBernabeu\EdilaboBundle\Exception\E0_FileCorruptedException;

try {
    $comLabo = $edilabo->parseFile('/path/to/file.xml');
} catch (E0_FileCorruptedException $e) {
    // Fichier introuvable, non lisible ou corrompu
    echo $e->getErrorCode(); // "E0"
    echo $e->getMessage();   // "[E0] Fichier XML introuvable : /path/to/file.xml"
}

Méthodes disponibles :

  • fileNotFound($path) - Fichier introuvable
  • fileNotReadable($path) - Fichier non lisible
  • corruptedFile($path, $reason) - Fichier corrompu
  • systemError($message) - Erreur système

E1_MalformedXmlException - XML mal formaté

Code E1 : Fichier XML mal formaté (well-formed).

La structure du fichier XML ne respecte pas les spécifications XML sur sa structuration (balise de fermeture manquante, etc.).

use YoanBernabeu\EdilaboBundle\Exception\E1_MalformedXmlException;

try {
    $comLabo = $edilabo->parse($xmlContent);
} catch (E1_MalformedXmlException $e) {
    // XML mal formé (balises non fermées, structure incorrecte, etc.)
    echo $e->getErrorCode(); // "E1"
    echo $e->getMessage();   // "[E1] XML mal formaté : ..."
}

Méthodes disponibles :

  • malformed($reason) - XML mal formaté
  • invalidStructure($details) - Structure XML invalide
  • emptyContent() - Contenu XML vide
  • missingTag($tagName) - Balise manquante
  • fromLibXmlErrors($errors) - À partir d'erreurs libxml

E2_SchemaValidationException - Non conforme au scénario

Code E2 : Fichier XML non validé au regard d'un scénario (valid).

Le fichier n'est pas valide au regard du scénario d'échanges SANDRE auquel il se réfère (erreurs de structure, non-respect des codes de valeurs possibles pour les nomenclatures).

use YoanBernabeu\EdilaboBundle\Exception\E2_SchemaValidationException;

try {
    $edilabo->validateWithXsd($xmlContent);
} catch (E2_SchemaValidationException $e) {
    // Non conforme au schéma XSD SANDRE
    echo $e->getErrorCode(); // "E2"
    echo $e->getMessage();   // "[E2] Validation XSD échouée : ..."
}

Méthodes disponibles :

  • invalidScenario($validationErrors) - Scénario invalide
  • invalidCodeValue($element, $value, $allowedValues) - Valeur de code invalide
  • missingRequiredElement($parent, $element) - Élément requis manquant
  • xsdValidationFailed($errors) - Validation XSD échouée
  • invalidNamespace($expected, $actual) - Namespace invalide

E3_UnknownReferenceException - Référence inconnue

Code E3 : Code/Identifiant non reconnu au niveau du référentiel commun.

Le fichier contient une valeur d'un code ou d'un identifiant non reconnu au niveau du référentiel commun SANDRE auquel il se rapporte.

use YoanBernabeu\EdilaboBundle\Exception\E3_UnknownReferenceException;

try {
    // Si votre logique métier valide les codes SANDRE
    if (!$this->referentielSandre->exists($codeParametre)) {
        throw E3_UnknownReferenceException::unknownParameter($codeParametre);
    }
} catch (E3_UnknownReferenceException $e) {
    // Code ou identifiant non reconnu dans le référentiel SANDRE
    echo $e->getErrorCode(); // "E3"
    echo $e->getMessage();   // "[E3] Paramètre SANDRE non reconnu : 1234"
}

Méthodes disponibles :

  • unknownCode($element, $code, $referentiel) - Code non reconnu
  • unknownIdentifier($element, $identifier, $referentiel) - Identifiant non reconnu
  • unknownParameter($code) - Paramètre SANDRE inconnu
  • unknownMethod($code) - Méthode SANDRE inconnue
  • unknownUnit($code) - Unité SANDRE inconnue
  • unknownIntervenant($id) - Intervenant introuvable
  • unknownStation($code) - Station introuvable

E4_UnsupportedContentException - Contenu non supporté

Code E4 : Contenu d'un élément ou attribut non supporté.

En raison des règles de gestion d'intégration (contraintes métiers, règles d'intégrité), l'information d'un élément ou attribut n'a pas de sens, l'erreur pouvant survenir au regard des autres informations contenues dans le fichier (inconsistent), ou au niveau de l'interface d'intégration.

use YoanBernabeu\EdilaboBundle\Exception\E4_UnsupportedContentException;

try {
    // Si vos règles métier détectent une incohérence
    if ($prelevement->getDatePrel() > date('Y-m-d')) {
        throw E4_UnsupportedContentException::businessRuleViolation(
            'DatePrelevement',
            $prelevement->getDatePrel(),
            'La date de prélèvement ne peut être dans le futur'
        );
    }
} catch (E4_UnsupportedContentException $e) {
    // Violation de règle métier ou données incohérentes
    echo $e->getErrorCode(); // "E4"
    echo $e->getMessage();   // "[E4] Violation de règle métier pour ..."
}

Méthodes disponibles :

  • businessRuleViolation($element, $value, $rule) - Violation de règle métier
  • inconsistentData($context, $reason) - Données incohérentes
  • integrationError($element, $reason) - Erreur d'intégration
  • unsupportedValue($element, $value, $reason) - Valeur non supportée
  • integrityConstraintViolation($constraint, $details) - Violation de contrainte d'intégrité
  • conflictingData($element1, $element2, $reason) - Données en conflit

Exemple complet de gestion des erreurs

use YoanBernabeu\EdilaboBundle\EdilaboInterface;
use YoanBernabeu\EdilaboBundle\Exception\E0_FileCorruptedException;
use YoanBernabeu\EdilaboBundle\Exception\E1_MalformedXmlException;
use YoanBernabeu\EdilaboBundle\Exception\E2_SchemaValidationException;
use YoanBernabeu\EdilaboBundle\Exception\SandreException;

try {
    $comLabo = $edilabo->parseFile('/path/to/commande.xml');
    
    // Traitement des données...
    
} catch (E0_FileCorruptedException $e) {
    // E0 : Fichier endommagé ou introuvable
    $this->logger->error($e->getMessage(), [
        'code' => $e->getErrorCode(),
        'definition' => $e->getErrorDefinition()
    ]);
    
} catch (E1_MalformedXmlException $e) {
    // E1 : XML mal formaté
    $this->logger->error($e->getMessage());
    
} catch (E2_SchemaValidationException $e) {
    // E2 : Non conforme au schéma SANDRE
    $this->logger->warning($e->getMessage());
    
} catch (SandreException $e) {
    // Capture toutes les autres exceptions SANDRE (E3, E4)
    $this->logger->error("Erreur SANDRE {$e->getErrorCode()}: {$e->getMessage()}");
}

Conformité SANDRE

Cette hiérarchie d'exceptions est conforme aux préconisations du scénario d'échange SANDRE Laboratoires-Commanditaires (page 69/79) qui définit les 5 types d'erreurs (E0 à E4) pouvant survenir lors du traitement d'un fichier d'échange.

🧪 Tests

Le bundle inclut des tests unitaires et d'intégration.

# Tests rapides (sans validation XSD - recommandé pour le développement)
composer test:fast

# Tous les tests (inclut validation XSD - plus lent)
composer test:all

# Tests unitaires seulement
composer test:phpunit

# Tests rapides (exclut les tests XSD lents)
composer test:phpunit:fast

# Tests de validation XSD uniquement
composer test:phpunit:xsd

# Analyse statique
composer test:phpstan

# Vérification du style de code
composer test:cs

💡 Conseil : Utilisez composer test:fast pendant le développement (17ms) et composer test:all avant de commiter (~1min avec validation XSD).

📦 Dépendances

  • PHP 8.1 ou supérieur
  • Symfony 7.3 ou supérieur
  • symfony/serializer-pack

📝 Format supporté

Ce bundle supporte 100% du format SANDRE COM_LABO version 1.1 (Novembre 2015) défini dans le scénario d'échange "Echanges Laboratoires-Commanditaires - Message Demande de prestations".

Couverture complète

Le bundle implémente tous les éléments du schéma XSD, incluant :

  • ✅ Tous les champs obligatoires et optionnels
  • ✅ Toutes les cardinalités (éléments simples et tableaux)
  • ✅ Tous les objets imbriqués (Service, LocalPrelevement, MesureEnvironnementale, etc.)
  • ✅ Tous les champs spécialisés (méthodes de fractionnement, extraction, solvants, etc.)
  • ✅ Validation XSD stricte contre le schéma officiel SANDRE
  • 11 Enums PHP couvrant 314 valeurs de nomenclatures SANDRE pour la sécurité des types

Namespaces XML supportés

  • http://xml.sandre.eaufrance.fr/scenario/com_labo/1

Schéma XSD officiel

Le bundle utilise le schéma XSD officiel SANDRE :

📄 Licence

MIT

👤 Auteur

Yoan Bernabeu

🔗 Liens utiles