From 36ecb09b73555ffbebc3340373120f52c159941c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 16 Nov 2025 07:23:38 +0000 Subject: [PATCH 1/3] Initial plan From 5482cbd60b8048a14b29a0e15f1f19bfd747c43e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 16 Nov 2025 07:34:30 +0000 Subject: [PATCH 2/3] Add comprehensive French developer documentation with examples and use cases Co-authored-by: gilles-g <377875+gilles-g@users.noreply.github.com> --- README.md | 12 + docs/GUIDE_DEVELOPPEUR.md | 1261 +++++++++++++++++++++++++++++++++++++ 2 files changed, 1273 insertions(+) create mode 100644 docs/GUIDE_DEVELOPPEUR.md diff --git a/README.md b/README.md index 6778185..0f53375 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,18 @@ to your ``composer.json`` file: $ php composer.phar require "spiriitlabs/poliris-bundle" ``` +Documentation +------------- + +📚 **[Guide DĂ©veloppeur Complet (Français)](docs/GUIDE_DEVELOPPEUR.md)** + +Le guide dĂ©veloppeur offre : +- 🚀 Un tutoriel "Getting Started" dĂ©taillĂ© +- 🎯 Des cas d'usage illustrĂ©s (SeLoger, photos, automatisation) +- 📊 Des tableaux de rĂ©fĂ©rence des modĂšles +- 🔧 Des solutions aux erreurs courantes +- 💡 Des exemples de code complets et testĂ©s + How to use ---------- diff --git a/docs/GUIDE_DEVELOPPEUR.md b/docs/GUIDE_DEVELOPPEUR.md new file mode 100644 index 0000000..bdad7d1 --- /dev/null +++ b/docs/GUIDE_DEVELOPPEUR.md @@ -0,0 +1,1261 @@ +# Guide DĂ©veloppeur - PolirisBundle 📚 + +> **Bundle Symfony pour la gĂ©nĂ©ration de flux CSV Poliris (V4.1.17)** +> Export de donnĂ©es immobiliĂšres vers SeLoger, LeBonCoin et autres portails + +--- + +## 📋 Table des matiĂšres + +1. [Getting Started](#-getting-started) +2. [Cas d'usage illustrĂ©s](#-cas-dusage-illustrĂ©s) +3. [RĂ©fĂ©rence des modĂšles](#-rĂ©fĂ©rence-des-modĂšles) +4. [DĂ©pannage](#-dĂ©pannage) + +--- + +## 🚀 Getting Started + +### Qu'est-ce que PolirisBundle ? + +**PolirisBundle** est un bundle Symfony qui facilite la gĂ©nĂ©ration de fichiers CSV au format **Poliris** (V4.1.17), le standard utilisĂ© par les principaux portails immobiliers français (SeLoger, LeBonCoin, etc.). + +#### Pourquoi ce bundle ? + +- ✅ **SimplicitĂ©** : Pas besoin de gĂ©rer manuellement 333+ colonnes CSV +- ✅ **Type-safe** : Utilise le pattern Builder avec des classes typĂ©es +- ✅ **Performance** : OptimisĂ© pour traiter des milliers d'annonces +- ✅ **Maintenance** : Évolution facile grĂące Ă  l'architecture modulaire + +#### Que puis-je en faire ? + +- Exporter vos biens immobiliers vers SeLoger, LeBonCoin +- Automatiser la mise Ă  jour quotidienne de vos annonces +- GĂ©rer des flux multi-portails avec un seul code source +- IntĂ©grer facilement des photos et mĂ©tadonnĂ©es + +--- + +### Installation + +#### PrĂ©requis + +- PHP >= 8.2 +- Symfony >= 5.4 + +#### Installation via Composer + +```bash +composer require spiriitlabs/poliris-bundle +``` + +Le bundle sera automatiquement enregistrĂ© grĂące au Flex de Symfony. + +--- + +### Configuration minimale + +Le bundle ne nĂ©cessite **aucune configuration** pour dĂ©marrer. Il fonctionne out-of-the-box ! + +Si vous souhaitez personnaliser le comportement, crĂ©ez un fichier `config/packages/poliris.yaml` : + +```yaml +# config/packages/poliris.yaml (optionnel) +poliris: + # Configuration future (actuellement aucune configuration nĂ©cessaire) +``` + +--- + +### Premier export : Exemple complet + +CrĂ©ons notre premier fichier CSV avec 2 annonces immobiliĂšres. + +#### Code PHP + +```php +startLine() + ->withIdentifiant( + agenceId: 'AGENCE001', + agencePropertyRef: 'BIEN-12345', + annonceType: Annonce::ANNONCE_TYPE_VENTE, + annonceIdTechnique: 'TECH-001' + ) + ->withType( + type: 1, // 1 = Appartement + sousType: null + ) + ->withLocalisation( + cp: '75001', + ville: 'Paris', + pays: 'France', + adresse: '10 rue de Rivoli', + quartierProximite: 'Proche mĂ©tro Louvre', + situation: null, + procheLac: null, + procheTennis: null, + procheSki: null, + cpReel: '75001', + villeReelle: 'Paris', + idQuartier: null, + transportLigne: '1,14', + transportStation: 'Louvre-Rivoli', + latitude: 48.860611, + longitude: 2.342499, + precisionGps: null, + localisation: null + ) + ->withSurface( + surface: 65, + surfaceTerrain: null, + nbPieces: 3, + nbChambres: 2, + nbSdb: 1, + nbSalleEau: 0, + nbWc: 1, + nbBalcons: 1, + surfaceBalcon: 5, + nbParkings: 0, + nbBoxes: 0, + terrasse: false, + longueurFacade: null, + placesEnSalle: null, + nbBureaux: null, + surfaceSejour: 25, + nbTerrasses: 0, + surfaceCave: 0, + surfaceSalleManger: null, + surfaceMin: null, + surfaceMax: null, + nbPiecesMin: null, + nbPiecesMax: null, + nbChambresMin: null, + nbChambresMax: null, + comblesAmenageables: null, + surfaceTerrainNecessaire: null, + surfaceTerrasse: null + ) + ->withPrix( + prix: 450000, + loyerMoisMur: null, + loyerCC: null, + loyerHT: null, + depotGarantie: null, + prixMasque: false, + prixHT: null, + copropriete: true, + nbLots: 25, + syndicatCopro: null, + syndicatCoproDetails: 'Charges annuelles : 2400€', + prixTerrain: null, + prixModeleMaison: null, + prixMin: null, + prixMax: null + ) + ->withDetail( + activitesCommerciales: null, + label: 'Appartement T3 lumineux proche Louvre', + description: 'Magnifique appartement de 65mÂČ avec balcon, situĂ© au cƓur de Paris. Vue dĂ©gagĂ©e, calme, proche de toutes commoditĂ©s.', + datesDispo: new \DateTimeImmutable('2025-01-01'), + amenagementHandicapes: false, + animauxAcceptes: false, + duplex: false, + commPrives: null, + logementADisposition: null, + nomModele: null + ) + ->withDiagnostic( + recent: true, + travaux: false, + consoEnergie: 'C', + bilanConsoEnergie: '150', + ges: 'B', + bilanGes: '20', + dpeAt: new \DateTimeImmutable('2024-06-15'), + dpeVersion: '2021', + dpeMin: null, + dpeMax: null, + dpeAnneeRef: null, + dpeCoutConsoAnnuelle: 1200 + ) + ->withContact( + tel: '0142000000', + fullName: 'Jean Dupont', + email: 'contact@agence.fr', + interCabinet: false, + interCabinetPrive: null, + codeNego: 'NEG001', + agenceTerrain: null + ); + + // Annonce 2 : Maison Ă  Lyon + $builder + ->startLine() + ->withIdentifiant( + agenceId: 'AGENCE001', + agencePropertyRef: 'BIEN-67890', + annonceType: Annonce::ANNONCE_TYPE_VENTE, + annonceIdTechnique: 'TECH-002' + ) + ->withType( + type: 2, // 2 = Maison + sousType: null + ) + ->withLocalisation( + cp: '69003', + ville: 'Lyon', + pays: 'France', + adresse: '25 avenue du GĂ©nĂ©ral Leclerc', + quartierProximite: 'Quartier rĂ©sidentiel', + situation: null, + procheLac: null, + procheTennis: null, + procheSki: null, + cpReel: '69003', + villeReelle: 'Lyon', + idQuartier: null, + transportLigne: 'B', + transportStation: 'Place Guichard', + latitude: 45.754610, + longitude: 4.856880, + precisionGps: null, + localisation: null + ) + ->withSurface( + surface: 120, + surfaceTerrain: 250, + nbPieces: 5, + nbChambres: 4, + nbSdb: 2, + nbSalleEau: 1, + nbWc: 2, + nbBalcons: 0, + surfaceBalcon: null, + nbParkings: 2, + nbBoxes: 0, + terrasse: true, + longueurFacade: null, + placesEnSalle: null, + nbBureaux: null, + surfaceSejour: 40, + nbTerrasses: 1, + surfaceCave: 15, + surfaceSalleManger: 18, + surfaceMin: null, + surfaceMax: null, + nbPiecesMin: null, + nbPiecesMax: null, + nbChambresMin: null, + nbChambresMax: null, + comblesAmenageables: null, + surfaceTerrainNecessaire: null, + surfaceTerrasse: 30 + ) + ->withPrix( + prix: 650000, + loyerMoisMur: null, + loyerCC: null, + loyerHT: null, + depotGarantie: null, + prixMasque: false, + prixHT: null, + copropriete: false, + nbLots: null, + syndicatCopro: null, + syndicatCoproDetails: null, + prixTerrain: null, + prixModeleMaison: null, + prixMin: null, + prixMax: null + ) + ->withDetail( + activitesCommerciales: null, + label: 'Belle maison familiale avec jardin', + description: 'Maison de 120mÂČ avec jardin de 250mÂČ, 4 chambres, terrasse. IdĂ©ale famille.', + datesDispo: new \DateTimeImmutable('2025-02-01'), + amenagementHandicapes: false, + animauxAcceptes: true, + duplex: false, + commPrives: null, + logementADisposition: null, + nomModele: null + ) + ->withDiagnostic( + recent: true, + travaux: false, + consoEnergie: 'D', + bilanConsoEnergie: '220', + ges: 'D', + bilanGes: '35', + dpeAt: new \DateTimeImmutable('2024-09-10'), + dpeVersion: '2021', + dpeMin: null, + dpeMax: null, + dpeAnneeRef: null, + dpeCoutConsoAnnuelle: 1800 + ) + ->withContact( + tel: '0478000000', + fullName: 'Marie Martin', + email: 'lyon@agence.fr', + interCabinet: false, + interCabinetPrive: null, + codeNego: 'NEG002', + agenceTerrain: null + ); + + // GĂ©nĂ©ration du CSV + $export = $builder->build(); + + return $export->toCSV(); // Retourne le contenu CSV + } +} +``` + +#### Utilisation dans un contrĂŽleur + +```php +generateExport(); + + $response = new Response($csvContent); + $response->headers->set('Content-Type', 'text/csv; charset=utf-8'); + $response->headers->set('Content-Disposition', 'attachment; filename="export-poliris.csv"'); + + return $response; + } +} +``` + +--- + +### À quoi ressemble le rĂ©sultat ? + +Le CSV gĂ©nĂ©rĂ© contiendra 333+ colonnes. Voici un **extrait simplifiĂ©** des premiĂšres colonnes : + +```csv +AGENCE001,BIEN-12345,vente,1,,,,75001,Paris,...,65,3,2,...,450000,... +AGENCE001,BIEN-67890,vente,2,,,,69003,Lyon,...,120,5,4,...,650000,... +``` + +**Structure du fichier :** + +| Col 1-4 | Col 5-10 | Col 11-40 | Col 41-100 | Col 101-200 | Col 201-333 | +|---------|----------|-----------|------------|-------------|-------------| +| Identifiant | Type | Photos | Localisation | Surface/Prix | Divers | + +#### Correspondance des colonnes principales + +| Colonne | Nom | Exemple | +|---------|-----|---------| +| 1 | ID Agence | `AGENCE001` | +| 2 | RĂ©fĂ©rence bien | `BIEN-12345` | +| 3 | Type annonce | `vente` ou `location` | +| 4 | Type de bien | `1` (Appt), `2` (Maison) | +| 5-10 | Photos | URLs des images | +| 11 | Code postal | `75001` | +| 12 | Ville | `Paris` | +| 20 | Surface | `65` (mÂČ) | +| 21 | Nb piĂšces | `3` | +| 22 | Nb chambres | `2` | +| 30 | Prix | `450000` | +| ... | ... | ... | + +--- + +## 🎯 Cas d'usage illustrĂ©s + +### Cas #1 : Exporter pour SeLoger avec mapping + +SeLoger impose certaines **contraintes spĂ©cifiques** sur les donnĂ©es Poliris. + +#### Mapping des types de biens + +```php + 1, + 'maison' => 2, + 'parking' => 3, + 'terrain' => 4, + 'boutique' => 5, + 'bureau' => 6, + 'batiment' => 7, + 'chateau' => 8, + 'immeuble' => 9, + 'loft' => 10, + 'local_commercial' => 11, + 'programme_neuf' => 13, + ]; + + /** + * Sous-types pour appartement + */ + public const SOUS_TYPE_APPARTEMENT = [ + 'duplex' => 1, + 'triplex' => 2, + 'loft' => 3, + 'penthouse' => 4, + ]; + + /** + * Sous-types pour maison + */ + public const SOUS_TYPE_MAISON = [ + 'villa' => 1, + 'mas' => 2, + 'chalet' => 3, + 'ferme' => 4, + 'longere' => 5, + ]; + + public function getTypePoliris(string $typeBien): int + { + return self::TYPE_MAPPING[$typeBien] ?? 1; + } + + public function getSousType(string $typeBien, ?string $sousType): ?int + { + if ($typeBien === 'appartement' && $sousType) { + return self::SOUS_TYPE_APPARTEMENT[$sousType] ?? null; + } + + if ($typeBien === 'maison' && $sousType) { + return self::SOUS_TYPE_MAISON[$sousType] ?? null; + } + + return null; + } +} +``` + +#### Utilisation du mapping + +```php +startLine() + ->withIdentifiant('AGENCE001', 'REF-001', 'vente', 'TECH-001') + ->withType( + type: $mapping->getTypePoliris('appartement'), // 1 + sousType: $mapping->getSousType('appartement', 'duplex') // 1 + ) + ->withDetail( + activitesCommerciales: null, + label: 'Duplex 4 piĂšces', + description: 'Magnifique duplex...', + datesDispo: new \DateTimeImmutable(), + amenagementHandicapes: false, + animauxAcceptes: false, + duplex: true, // Important pour SeLoger + commPrives: null, + logementADisposition: null, + nomModele: null + ); +``` + +#### Publication spĂ©cifique SeLoger + +```php +$builder + ->startLine() + ->withIdentifiant(/* ... */) + ->withPublication( + publications: 'seloger', // Portails cibles : seloger, leboncoin, etc. + coupDeCoeur: true, // Mise en avant + versionFormat: '4.1.17' // Version Poliris + ); +``` + +--- + +### Cas #2 : Inclure des photos dans l'export + +Les portails immobiliers nĂ©cessitent des photos de qualitĂ©. Le bundle supporte jusqu'Ă  **30 photos par annonce**. + +#### Exemple avec photos + +```php +startLine() + ->withIdentifiant('AGENCE001', 'REF-001', 'vente', 'TECH-001') + ->withType(1, null) + ->withPhoto( + // URLs des photos (30 max) + photo1: 'https://www.example.com/photos/bien-001/salon.jpg', + photo2: 'https://www.example.com/photos/bien-001/cuisine.jpg', + photo3: 'https://www.example.com/photos/bien-001/chambre1.jpg', + photo4: 'https://www.example.com/photos/bien-001/chambre2.jpg', + photo5: 'https://www.example.com/photos/bien-001/sdb.jpg', + photo6: 'https://www.example.com/photos/bien-001/exterieur.jpg', + photo7: null, + photo8: null, + photo9: null, + photo10: null, + photo11: null, + photo12: null, + photo13: null, + photo14: null, + photo15: null, + photo16: null, + photo17: null, + photo18: null, + photo19: null, + photo20: null, + photo21: null, + photo22: null, + photo23: null, + photo24: null, + photo25: null, + photo26: null, + photo27: null, + photo28: null, + photo29: null, + photo30: null, + + // Titres des photos (optionnels) + titre1: 'Salon lumineux', + titre2: 'Cuisine Ă©quipĂ©e', + titre3: 'Chambre 1', + titre4: 'Chambre 2', + titre5: 'Salle de bain', + titre6: 'Vue extĂ©rieure', + titre7: null, + titre8: null, + titre9: null, + titre10: null, + titre11: null, + titre12: null, + titre13: null, + titre14: null, + titre15: null, + titre16: null, + titre17: null, + titre18: null, + titre19: null, + titre20: null, + titre21: null, + titre22: null, + titre23: null, + titre24: null, + titre25: null, + titre26: null, + titre27: null, + titre28: null, + titre29: null, + titre30: null, + + // Photo panoramique et visite virtuelle + photoPanoramique: 'https://www.example.com/photos/bien-001/panorama.jpg', + urlVisiteVirtuelle: 'https://www.example.com/visite-virtuelle/bien-001' + ); +``` + +#### Bonnes pratiques pour les photos + +✅ **Ordre des photos** : Commencez par les piĂšces principales (salon, cuisine) +✅ **QualitĂ©** : Minimum 1024x768, idĂ©al 1920x1080 +✅ **Format** : JPG ou PNG +✅ **Poids** : < 2 Mo par photo +✅ **Titres** : Descriptifs et informatifs + +❌ **À Ă©viter** : Photos floues, trop sombres, avec filigrane + +--- + +### Cas #3 : Automatiser un export programmĂ© + +#### Option A : Avec une commande Symfony + +CrĂ©ez une commande pour gĂ©nĂ©rer l'export : + +```php +title('GĂ©nĂ©ration du fichier Poliris'); + + try { + // GĂ©nĂ©ration du CSV + $csvContent = $this->exportService->generateExport(); + + // Sauvegarde du fichier + $filename = sprintf( + 'poliris-export-%s.csv', + (new \DateTime())->format('Y-m-d-His') + ); + $filepath = $this->exportDir . '/' . $filename; + + file_put_contents($filepath, $csvContent); + + $io->success(sprintf('Export gĂ©nĂ©rĂ© : %s', $filename)); + + return Command::SUCCESS; + } catch (\Exception $e) { + $io->error('Erreur lors de la gĂ©nĂ©ration : ' . $e->getMessage()); + return Command::FAILURE; + } + } +} +``` + +**Configuration** (`config/services.yaml`) : + +```yaml +# config/services.yaml +services: + App\Command\ExportPolirisCommand: + arguments: + $exportDir: '%kernel.project_dir%/var/exports' +``` + +**ExĂ©cution** : + +```bash +php bin/console app:export:poliris +``` + +#### Option B : Avec un cron + +Ajoutez une tĂąche cron pour exĂ©cuter l'export quotidiennement : + +```bash +# Crontab : Export tous les jours Ă  2h du matin +0 2 * * * cd /var/www/myapp && php bin/console app:export:poliris >> /var/log/poliris-export.log 2>&1 +``` + +#### Option C : Avec un Event Symfony + +DĂ©clenchez l'export automatiquement lors de certains Ă©vĂ©nements : + +```php + 'onPropertyUpdated', + ]; + } + + public function onPropertyUpdated(PropertyUpdatedEvent $event): void + { + // RegĂ©nĂ©rer l'export aprĂšs mise Ă  jour d'un bien + $csvContent = $this->exportService->generateExport(); + + // Enregistrer ou envoyer le fichier + // ... + } +} +``` + +#### Option D : Via Messenger (asynchrone) + +Pour des exports lourds, utilisez Symfony Messenger : + +```php +exportService->generateExport(); + + // Sauvegarder le fichier + file_put_contents( + '/var/exports/poliris-' . $message->requestedAt->format('YmdHis') . '.csv', + $csvContent + ); + } +} +``` + +**DĂ©clencher l'export** : + +```php +$messageBus->dispatch(new GeneratePolirisExportMessage(new \DateTimeImmutable())); +``` + +--- + +### Cas #4 : Erreur courante et solution + +#### ❌ Erreur : "Invalid CSV format" + +**SymptĂŽme** : +Le portail refuse le CSV avec le message "Format invalide". + +**Causes possibles** : + +1. **Encodage incorrect** : Le CSV doit ĂȘtre en UTF-8 +2. **SĂ©parateur incorrect** : Poliris utilise des virgules (`,`) +3. **Nombre de colonnes incorrect** : 333 colonnes attendues +4. **Valeurs manquantes** : Certaines colonnes obligatoires sont vides + +**Solution 1 : VĂ©rifier l'encodage** + +```php +$csvContent = $builder->build()->toCSV(); + +// Forcer l'encodage UTF-8 +$csvContent = mb_convert_encoding($csvContent, 'UTF-8', 'auto'); + +// Ajouter le BOM UTF-8 (parfois nĂ©cessaire) +$csvContent = "\xEF\xBB\xBF" . $csvContent; +``` + +**Solution 2 : VĂ©rifier les colonnes obligatoires** + +Assurez-vous de remplir **au minimum** : + +```php +$builder + ->startLine() + // OBLIGATOIRES + ->withIdentifiant( + agenceId: 'AGENCE001', // ✅ Requis + agencePropertyRef: 'REF-001', // ✅ Requis + annonceType: 'vente', // ✅ Requis + annonceIdTechnique: 'TECH-001' // ✅ Requis + ) + ->withType( + type: 1, // ✅ Requis + sousType: null + ) + ->withLocalisation( + cp: '75001', // ✅ Requis + ville: 'Paris', // ✅ Requis + pays: 'France', // ✅ Requis + adresse: null, // Optionnel + // ... autres paramĂštres optionnels + ) + ->withSurface( + surface: 65, // ✅ Requis + surfaceTerrain: null, + nbPieces: 3, // ✅ Requis + nbChambres: 2, // ✅ Requis (selon type) + // ... autres paramĂštres + ) + ->withPrix( + prix: 450000, // ✅ Requis + // ... autres paramĂštres optionnels + ) + ->withDetail( + activitesCommerciales: null, + label: 'Mon annonce', // ✅ Requis + description: 'Description...', // ✅ Requis + // ... autres paramĂštres + ); +``` + +**Solution 3 : Activer les logs de debug** + +```php +try { + $export = $builder->build(); + $csvContent = $export->toCSV(); +} catch (\Throwable $e) { + // Logger l'erreur + error_log('Erreur Poliris : ' . $e->getMessage()); + error_log('Trace : ' . $e->getTraceAsString()); + + throw $e; +} +``` + +#### ❌ Erreur : "Memory exhausted" + +**SymptĂŽme** : +`PHP Fatal error: Allowed memory size exhausted` + +**Cause** : +Trop d'annonces en mĂ©moire (plusieurs milliers). + +**Solution : Traiter par lots** + +```php +addProperty($builder, $property); + $count++; + + // GĂ©nĂ©rer un CSV toutes les 500 annonces + if ($count % self::BATCH_SIZE === 0) { + yield $builder->build()->toCSV(); + + // RĂ©initialiser le builder + $builder = new AnnonceExportBuilder(); + + // LibĂ©rer la mĂ©moire + gc_collect_cycles(); + } + } + + // DerniĂšres annonces + if ($count % self::BATCH_SIZE !== 0) { + yield $builder->build()->toCSV(); + } + } + + private function addProperty(AnnonceExportBuilder $builder, $property): void + { + $builder + ->startLine() + ->withIdentifiant(/* ... */); + // etc. + } +} +``` + +**Utilisation** : + +```php +// Dans un contrĂŽleur ou une commande +$properties = $propertyRepository->findAllAsIterator(); + +foreach ($exportService->generateExportInBatches($properties) as $batch) { + // Écrire chaque batch dans un fichier + file_put_contents('/tmp/export-batch-' . time() . '.csv', $batch, FILE_APPEND); +} +``` + +--- + +## 📚 RĂ©fĂ©rence des modĂšles + +Le bundle fournit **31 modĂšles** pour reprĂ©senter tous les aspects d'un bien immobilier. + +### Vue d'ensemble des modĂšles + +| ModĂšle | Description | Colonnes CSV | +|--------|-------------|--------------| +| **Identifiant** | ID agence, rĂ©fĂ©rence bien | 1-3, 175 | +| **Type** | Type de bien (appt, maison, etc.) | 4, 181 | +| **Photo** | URLs photos (30 max) + visite virtuelle | 5-10, 96-125 | +| **Localisation** | Adresse, CP, ville, GPS | 11-19, 158-165 | +| **Surface** | Surface, piĂšces, chambres | 20-29, 182-188 | +| **Prix** | Prix, loyer, charges | 30-34, 126-137 | +| **Detail** | Titre, description, disponibilitĂ© | 35-40, 194-196 | +| **Diagnostic** | DPE, GES, diagnostics | 41-50, 198-205 | +| **Contact** | TĂ©lĂ©phone, email, nĂ©gociateur | 51-57, 300-305 | +| **Exterieur** | Ascenseur, piscine, vue | 58-64 | +| **Interieur** | MeublĂ©, annĂ©e construction | 65-85 | +| **ChauffageClim** | Type chauffage, climatisation | 86-87 | +| **PartieJour** | Cuisine, sĂ©jour, Ă©quipements | 88-95 | +| **Etage** | Étage, nombre d'Ă©tages | 138-140 | +| **Securite** | Digicode, interphone, alarme | 141-144 | +| **Garage** | Garage, type | 145-146 | +| **Parking** | Nombre vĂ©hicules, type parking | 147-149 | +| **Bureau** | Infos bureaux professionnels | 150-167 | +| **Boutique** | Infos commerce | 168-169 | +| **Terrain** | Terrain constructible, agricole | 170-176 | +| **Location** | DurĂ©e bail, nature bail | 177-180, 206-209 | +| **Viager** | Bouquet, rente mensuelle | 210-214 | +| **Mandat** | Mandat exclusif, numĂ©ro | 215-225 | +| **HonoraireCharge** | Honoraires, charges | 226-235 | +| **Diagnostic** | Diagnostics immobiliers | 236-250 | +| **FondsCommerce** | CA, rĂ©sultats (commerce) | 251-258 | +| **ProduitInvestissement** | Valeur achat, rapport | 259-260 | +| **LocationVacances** | Prix saison, disponibilitĂ©s | 261-272 | +| **Langue** | Multilangue (3 langues max) | 273-284 | +| **Publication** | Portails de diffusion | 285-287 | +| **ChampCustom** | Champs personnalisĂ©s (26 max) | 288-313 | + +### ModĂšles les plus utilisĂ©s + +#### 1. Identifiant (colonnes 1-3, 175) + +```php +->withIdentifiant( + agenceId: 'AGENCE001', // Col 1 : ID agence + agencePropertyRef: 'REF-001', // Col 2 : RĂ©fĂ©rence bien + annonceType: 'vente', // Col 3 : vente/location + annonceIdTechnique: 'TECH-001' // Col 175 : ID technique +) +``` + +**Types d'annonce** : +- `Annonce::ANNONCE_TYPE_VENTE` = `'vente'` +- `Annonce::ANNONCE_TYPE_LOCATION` = `'location'` +- `Annonce::ANNONCE_TYPE_MODELE_MAISON` = `'modĂšle de maison'` + +#### 2. Type (colonnes 4, 181) + +```php +->withType( + type: 1, // Col 4 : Type principal + sousType: null // Col 181 : Sous-type +) +``` + +**Codes types** : +- `1` = Appartement +- `2` = Maison +- `3` = Parking/Box +- `4` = Terrain +- `5` = Boutique +- `6` = Bureau +- `7` = BĂątiment +- `8` = ChĂąteau +- `9` = Immeuble +- `10` = Loft +- `11` = Local commercial +- `13` = Programme neuf + +#### 3. Surface (colonnes 20-29, 182-188) + +```php +->withSurface( + surface: 65, // Col 20 : Surface habitable (mÂČ) + surfaceTerrain: 200, // Col 21 : Surface terrain (mÂČ) + nbPieces: 3, // Col 22 : Nombre de piĂšces + nbChambres: 2, // Col 23 : Nombre de chambres + nbSdb: 1, // Col 24 : Salles de bain + nbSalleEau: 1, // Col 25 : Salles d'eau + nbWc: 1, // Col 26 : WC + nbBalcons: 1, // Col 27 : Nombre de balcons + surfaceBalcon: 5, // Col 28 : Surface balcon (mÂČ) + nbParkings: 1, // Col 29 : Parkings + // ... 18 autres paramĂštres +) +``` + +#### 4. Prix (colonnes 30-34, 126-137) + +```php +->withPrix( + prix: 450000, // Col 30 : Prix vente ou loyer + loyerMoisMur: null, // Col 31 : Loyer mur + loyerCC: 500, // Col 32 : Loyer charges comprises + loyerHT: null, // Col 33 : Loyer HT (pro) + depotGarantie: 1000, // Col 34 : DĂ©pĂŽt de garantie + prixMasque: false, // Col 126 : Masquer le prix + prixHT: null, // Col 127 : Prix HT (pro) + copropriete: true, // Col 128 : En copropriĂ©tĂ© + nbLots: 25, // Col 129 : Nombre de lots + syndicatCopro: null, // Col 130 : Syndic + syndicatCoproDetails: 'Charges annuelles : 2400€', // Col 131 + // ... autres paramĂštres +) +``` + +#### 5. Detail (colonnes 35-40, 194-196) + +```php +->withDetail( + activitesCommerciales: null, // Col 35 + label: 'Appartement T3 lumineux', // Col 36 : Titre + description: 'Belle description...', // Col 37 : Descriptif + datesDispo: new \DateTimeImmutable(), // Col 38 : Date dispo + amenagementHandicapes: false, // Col 39 + animauxAcceptes: true, // Col 40 + duplex: false, // Col 194 + commPrives: null, // Col 195 + logementADisposition: null, // Col 196 + nomModele: null +) +``` + +--- + +## 🔧 DĂ©pannage + +### FAQ + +**Q : Puis-je valider les donnĂ©es avant l'export ?** +R : Non, le bundle ne propose pas de validation intĂ©grĂ©e pour des raisons de performance. Validez vos donnĂ©es en amont avec le Validator de Symfony. + +**Q : Comment gĂ©rer les champs multilangues ?** +R : Utilisez le modĂšle `Langue` avec jusqu'Ă  3 langues : + +```php +->withLangue( + code1: 'FR', + code2: 'EN', + code3: 'DE', + proximite1: 'Proche mĂ©tro', + proximite2: 'Near metro', + proximite3: 'Nahe U-Bahn', + label1: 'Appartement 3 piĂšces', + label2: '3-room apartment', + label3: '3-Zimmer-Wohnung', + descriptif1: 'Belle description en français', + descriptif2: 'Beautiful description in English', + descriptif3: 'Schöne Beschreibung auf Deutsch' +) +``` + +**Q : Comment exporter uniquement certaines annonces ?** +R : Filtrez vos donnĂ©es en amont dans votre repository : + +```php +// Dans votre service +public function generateExportForSeLoger(): string +{ + $properties = $this->propertyRepository->findBy([ + 'status' => 'published', + 'exportToSeLoger' => true + ]); + + $builder = new AnnonceExportBuilder(); + + foreach ($properties as $property) { + $this->addProperty($builder, $property); + } + + return $builder->build()->toCSV(); +} +``` + +**Q : Le CSV est-il valide pour tous les portails ?** +R : Le format Poliris V4.1.17 est supportĂ© par la majoritĂ© des portails français (SeLoger, LeBonCoin, PAP, etc.). VĂ©rifiez la documentation de chaque portail pour les spĂ©cificitĂ©s. + +**Q : Comment gĂ©rer les erreurs de gĂ©nĂ©ration ?** +R : Utilisez un try-catch et loggez les erreurs : + +```php +use Psr\Log\LoggerInterface; + +public function generateExport(LoggerInterface $logger): ?string +{ + try { + $builder = new AnnonceExportBuilder(); + + foreach ($this->properties as $property) { + try { + $this->addProperty($builder, $property); + } catch (\Throwable $e) { + // Logger mais continuer + $logger->error('Erreur annonce ' . $property->getId(), [ + 'exception' => $e->getMessage() + ]); + } + } + + return $builder->build()->toCSV(); + + } catch (\Throwable $e) { + $logger->critical('Erreur gĂ©nĂ©ration CSV Poliris', [ + 'exception' => $e->getMessage(), + 'trace' => $e->getTraceAsString() + ]); + + return null; + } +} +``` + +--- + +### Checklist avant mise en production + +- [ ] Les champs obligatoires sont remplis (Identifiant, Type, Localisation, Surface, Prix, Detail) +- [ ] Les photos sont accessibles via HTTPS +- [ ] L'encodage du CSV est UTF-8 +- [ ] Les dates sont au format `DateTimeImmutable` ou `DateTime` +- [ ] Les boolĂ©ens sont correctement convertis (`true` → `'OUI'`, `false` → `'NON'`) +- [ ] Le fichier CSV est testĂ© sur un portail en environnement de test +- [ ] Les logs sont activĂ©s pour tracer les erreurs +- [ ] Un systĂšme de monitoring est en place (export quotidien rĂ©ussi/Ă©chouĂ©) +- [ ] La performance est acceptable (< 30s pour 1000 annonces) + +--- + +### Ressources supplĂ©mentaires + +- **Documentation officielle Poliris** : [https://www.poliris.com/documentation](https://www.poliris.com/documentation) +- **Repository GitHub** : [https://github.com/SpiriitLabs/poliris-bundle](https://github.com/SpiriitLabs/poliris-bundle) +- **Packagist** : [https://packagist.org/packages/spiriitlabs/poliris-bundle](https://packagist.org/packages/spiriitlabs/poliris-bundle) + +--- + +## 📝 SchĂ©ma d'architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Votre Application │ +│ │ +│ ┌────────────────┐ ┌─────────────────┐ │ +│ │ Controller │─────▶│ ExportService │ │ +│ └────────────────┘ └────────┬────────┘ │ +│ │ │ +│ â–Œ │ +│ ┌─────────────────────────────────┐ │ +│ │ AnnonceExportBuilder (Bundle) │ │ +│ └──────────────┬──────────────────┘ │ +│ │ │ +│ â–Œ │ +│ ┌─────────────────────────────┐ │ +│ │ AnnonceBuilder (Bundle) │ │ +│ └──────────────┬──────────────┘ │ +│ │ │ +│ Utilise 31 modĂšles │ +│ │ │ +│ ┌──────────────┌──────────────┐ │ +│ â–Œ â–Œ â–Œ │ +│ Identifiant Surface Photo │ +│ Type Prix Detail │ +│ Localisation Contact etc. │ +│ │ +└───────────────────────────┬───────────────────────────────┘ + │ + â–Œ + ┌────────────────┐ + │ Fichier CSV │ + │ (333 colonnes) │ + └────────┬───────┘ + │ + ┌────────────────┌────────────────┐ + â–Œ â–Œ â–Œ + SeLoger LeBonCoin PAP +``` + +--- + +## 📊 Tableau rĂ©capitulatif des cas d'usage + +| Cas d'usage | DifficultĂ© | Temps estimĂ© | ModĂšles requis | +|-------------|------------|--------------|----------------| +| Export basique | ⭐ Facile | 15 min | 5-7 modĂšles | +| Export avec photos | ⭐⭐ Moyen | 30 min | 8-10 modĂšles | +| Export SeLoger complet | ⭐⭐⭐ AvancĂ© | 1-2h | 15-20 modĂšles | +| Automatisation cron | ⭐⭐ Moyen | 45 min | DĂ©pend du cas | +| Export asynchrone | ⭐⭐⭐ AvancĂ© | 2-3h | DĂ©pend du cas | +| Multi-portails | ⭐⭐⭐ AvancĂ© | 3-4h | 20-25 modĂšles | + +--- + +**VoilĂ  ! Vous ĂȘtes maintenant prĂȘt Ă  utiliser PolirisBundle comme un pro ! 🚀** + +Si vous avez des questions, n'hĂ©sitez pas Ă  ouvrir une issue sur GitHub ou Ă  consulter les exemples dans le dossier `tests/`. + +Bon export ! 🏠✹ From 570e733f1edb5a4b2e88070ca72011aaf98990d1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 16 Nov 2025 08:16:59 +0000 Subject: [PATCH 3/3] Replace French documentation with professional English version Co-authored-by: gilles-g <377875+gilles-g@users.noreply.github.com> --- README.md | 16 +- ...{GUIDE_DEVELOPPEUR.md => DOCUMENTATION.md} | 682 +++++++++--------- 2 files changed, 349 insertions(+), 349 deletions(-) rename docs/{GUIDE_DEVELOPPEUR.md => DOCUMENTATION.md} (55%) diff --git a/README.md b/README.md index 0f53375..e4f8dbb 100644 --- a/README.md +++ b/README.md @@ -23,14 +23,14 @@ $ php composer.phar require "spiriitlabs/poliris-bundle" Documentation ------------- -📚 **[Guide DĂ©veloppeur Complet (Français)](docs/GUIDE_DEVELOPPEUR.md)** - -Le guide dĂ©veloppeur offre : -- 🚀 Un tutoriel "Getting Started" dĂ©taillĂ© -- 🎯 Des cas d'usage illustrĂ©s (SeLoger, photos, automatisation) -- 📊 Des tableaux de rĂ©fĂ©rence des modĂšles -- 🔧 Des solutions aux erreurs courantes -- 💡 Des exemples de code complets et testĂ©s +📚 **[Complete Documentation](docs/DOCUMENTATION.md)** + +The documentation provides: +- 🚀 Detailed getting started tutorial +- 🎯 Illustrated use cases (SeLoger, photos, automation) +- 📊 Model reference tables +- 🔧 Common error solutions +- 💡 Complete and tested code examples How to use ---------- diff --git a/docs/GUIDE_DEVELOPPEUR.md b/docs/DOCUMENTATION.md similarity index 55% rename from docs/GUIDE_DEVELOPPEUR.md rename to docs/DOCUMENTATION.md index bdad7d1..6364c33 100644 --- a/docs/GUIDE_DEVELOPPEUR.md +++ b/docs/DOCUMENTATION.md @@ -1,44 +1,44 @@ -# Guide DĂ©veloppeur - PolirisBundle 📚 +# PolirisBundle Documentation -> **Bundle Symfony pour la gĂ©nĂ©ration de flux CSV Poliris (V4.1.17)** -> Export de donnĂ©es immobiliĂšres vers SeLoger, LeBonCoin et autres portails +> **Symfony Bundle for Poliris CSV Generation (V4.1.17)** +> Export real estate data to SeLoger, LeBonCoin and other French property portals --- -## 📋 Table des matiĂšres +## Table of Contents -1. [Getting Started](#-getting-started) -2. [Cas d'usage illustrĂ©s](#-cas-dusage-illustrĂ©s) -3. [RĂ©fĂ©rence des modĂšles](#-rĂ©fĂ©rence-des-modĂšles) -4. [DĂ©pannage](#-dĂ©pannage) +1. [Getting Started](#getting-started) +2. [Use Cases](#use-cases) +3. [Model Reference](#model-reference) +4. [Troubleshooting](#troubleshooting) --- -## 🚀 Getting Started +## Getting Started -### Qu'est-ce que PolirisBundle ? +### What is PolirisBundle? -**PolirisBundle** est un bundle Symfony qui facilite la gĂ©nĂ©ration de fichiers CSV au format **Poliris** (V4.1.17), le standard utilisĂ© par les principaux portails immobiliers français (SeLoger, LeBonCoin, etc.). +**PolirisBundle** is a Symfony bundle that simplifies the generation of CSV files in the **Poliris** format (V4.1.17), the standard used by major French real estate portals (SeLoger, LeBonCoin, etc.). -#### Pourquoi ce bundle ? +#### Why use this bundle? -- ✅ **SimplicitĂ©** : Pas besoin de gĂ©rer manuellement 333+ colonnes CSV -- ✅ **Type-safe** : Utilise le pattern Builder avec des classes typĂ©es -- ✅ **Performance** : OptimisĂ© pour traiter des milliers d'annonces -- ✅ **Maintenance** : Évolution facile grĂące Ă  l'architecture modulaire +- ✅ **Simplicity**: No need to manually manage 333+ CSV columns +- ✅ **Type-safe**: Uses the Builder pattern with typed classes +- ✅ **Performance**: Optimized to process thousands of listings +- ✅ **Maintainability**: Easy evolution thanks to modular architecture -#### Que puis-je en faire ? +#### What can you do with it? -- Exporter vos biens immobiliers vers SeLoger, LeBonCoin -- Automatiser la mise Ă  jour quotidienne de vos annonces -- GĂ©rer des flux multi-portails avec un seul code source -- IntĂ©grer facilement des photos et mĂ©tadonnĂ©es +- Export your real estate properties to SeLoger, LeBonCoin +- Automate daily listing updates +- Manage multi-portal feeds with a single codebase +- Easily integrate photos and metadata --- ### Installation -#### PrĂ©requis +#### Requirements - PHP >= 8.2 - Symfony >= 5.4 @@ -49,29 +49,29 @@ composer require spiriitlabs/poliris-bundle ``` -Le bundle sera automatiquement enregistrĂ© grĂące au Flex de Symfony. +The bundle will be automatically registered thanks to Symfony Flex. --- -### Configuration minimale +### Minimal Configuration -Le bundle ne nĂ©cessite **aucune configuration** pour dĂ©marrer. Il fonctionne out-of-the-box ! +The bundle requires **no configuration** to get started. It works out-of-the-box! -Si vous souhaitez personnaliser le comportement, crĂ©ez un fichier `config/packages/poliris.yaml` : +If you wish to customize behavior, create a `config/packages/poliris.yaml` file: ```yaml -# config/packages/poliris.yaml (optionnel) +# config/packages/poliris.yaml (optional) poliris: - # Configuration future (actuellement aucune configuration nĂ©cessaire) + # Future configuration (currently no configuration needed) ``` --- -### Premier export : Exemple complet +### First Export: Complete Example -CrĂ©ons notre premier fichier CSV avec 2 annonces immobiliĂšres. +Let's create our first CSV file with 2 real estate listings. -#### Code PHP +#### PHP Code ```php startLine() ->withIdentifiant( agenceId: 'AGENCE001', - agencePropertyRef: 'BIEN-12345', + agencePropertyRef: 'PROP-12345', annonceType: Annonce::ANNONCE_TYPE_VENTE, annonceIdTechnique: 'TECH-001' ) ->withType( - type: 1, // 1 = Appartement + type: 1, // 1 = Apartment sousType: null ) ->withLocalisation( @@ -105,7 +105,7 @@ class PolirisExportService ville: 'Paris', pays: 'France', adresse: '10 rue de Rivoli', - quartierProximite: 'Proche mĂ©tro Louvre', + quartierProximite: 'Near Louvre metro', situation: null, procheLac: null, procheTennis: null, @@ -161,7 +161,7 @@ class PolirisExportService copropriete: true, nbLots: 25, syndicatCopro: null, - syndicatCoproDetails: 'Charges annuelles : 2400€', + syndicatCoproDetails: 'Annual charges: €2400', prixTerrain: null, prixModeleMaison: null, prixMin: null, @@ -169,8 +169,8 @@ class PolirisExportService ) ->withDetail( activitesCommerciales: null, - label: 'Appartement T3 lumineux proche Louvre', - description: 'Magnifique appartement de 65mÂČ avec balcon, situĂ© au cƓur de Paris. Vue dĂ©gagĂ©e, calme, proche de toutes commoditĂ©s.', + label: 'Bright 3-room apartment near Louvre', + description: 'Beautiful 65mÂČ apartment with balcony, located in the heart of Paris. Unobstructed view, quiet, close to all amenities.', datesDispo: new \DateTimeImmutable('2025-01-01'), amenagementHandicapes: false, animauxAcceptes: false, @@ -195,25 +195,25 @@ class PolirisExportService ) ->withContact( tel: '0142000000', - fullName: 'Jean Dupont', - email: 'contact@agence.fr', + fullName: 'John Smith', + email: 'contact@agency.com', interCabinet: false, interCabinetPrive: null, codeNego: 'NEG001', agenceTerrain: null ); - // Annonce 2 : Maison Ă  Lyon + // Listing 2: House in Lyon $builder ->startLine() ->withIdentifiant( agenceId: 'AGENCE001', - agencePropertyRef: 'BIEN-67890', + agencePropertyRef: 'PROP-67890', annonceType: Annonce::ANNONCE_TYPE_VENTE, annonceIdTechnique: 'TECH-002' ) ->withType( - type: 2, // 2 = Maison + type: 2, // 2 = House sousType: null ) ->withLocalisation( @@ -221,7 +221,7 @@ class PolirisExportService ville: 'Lyon', pays: 'France', adresse: '25 avenue du GĂ©nĂ©ral Leclerc', - quartierProximite: 'Quartier rĂ©sidentiel', + quartierProximite: 'Residential area', situation: null, procheLac: null, procheTennis: null, @@ -285,8 +285,8 @@ class PolirisExportService ) ->withDetail( activitesCommerciales: null, - label: 'Belle maison familiale avec jardin', - description: 'Maison de 120mÂČ avec jardin de 250mÂČ, 4 chambres, terrasse. IdĂ©ale famille.', + label: 'Beautiful family house with garden', + description: '120mÂČ house with 250mÂČ garden, 4 bedrooms, terrace. Ideal for families.', datesDispo: new \DateTimeImmutable('2025-02-01'), amenagementHandicapes: false, animauxAcceptes: true, @@ -311,23 +311,23 @@ class PolirisExportService ) ->withContact( tel: '0478000000', - fullName: 'Marie Martin', - email: 'lyon@agence.fr', + fullName: 'Mary Johnson', + email: 'lyon@agency.com', interCabinet: false, interCabinetPrive: null, codeNego: 'NEG002', agenceTerrain: null ); - // GĂ©nĂ©ration du CSV + // Generate CSV $export = $builder->build(); - return $export->toCSV(); // Retourne le contenu CSV + return $export->toCSV(); } } ``` -#### Utilisation dans un contrĂŽleur +#### Usage in a Controller ```php 1, - 'maison' => 2, + 'apartment' => 1, + 'house' => 2, 'parking' => 3, - 'terrain' => 4, - 'boutique' => 5, - 'bureau' => 6, - 'batiment' => 7, - 'chateau' => 8, - 'immeuble' => 9, + 'land' => 4, + 'shop' => 5, + 'office' => 6, + 'building' => 7, + 'castle' => 8, + 'block' => 9, 'loft' => 10, - 'local_commercial' => 11, - 'programme_neuf' => 13, + 'commercial_premises' => 11, + 'new_development' => 13, ]; /** - * Sous-types pour appartement + * Subtypes for apartments */ public const SOUS_TYPE_APPARTEMENT = [ 'duplex' => 1, @@ -435,29 +435,29 @@ class SeLogerMappingService ]; /** - * Sous-types pour maison + * Subtypes for houses */ public const SOUS_TYPE_MAISON = [ 'villa' => 1, - 'mas' => 2, + 'farmhouse' => 2, 'chalet' => 3, - 'ferme' => 4, - 'longere' => 5, + 'farm' => 4, + 'longhouse' => 5, ]; - public function getTypePoliris(string $typeBien): int + public function getTypePoliris(string $propertyType): int { - return self::TYPE_MAPPING[$typeBien] ?? 1; + return self::TYPE_MAPPING[$propertyType] ?? 1; } - public function getSousType(string $typeBien, ?string $sousType): ?int + public function getSubType(string $propertyType, ?string $subType): ?int { - if ($typeBien === 'appartement' && $sousType) { - return self::SOUS_TYPE_APPARTEMENT[$sousType] ?? null; + if ($propertyType === 'apartment' && $subType) { + return self::SOUS_TYPE_APPARTEMENT[$subType] ?? null; } - if ($typeBien === 'maison' && $sousType) { - return self::SOUS_TYPE_MAISON[$sousType] ?? null; + if ($propertyType === 'house' && $subType) { + return self::SOUS_TYPE_MAISON[$subType] ?? null; } return null; @@ -465,7 +465,7 @@ class SeLogerMappingService } ``` -#### Utilisation du mapping +#### Using the Mapping ```php startLine() ->withIdentifiant('AGENCE001', 'REF-001', 'vente', 'TECH-001') ->withType( - type: $mapping->getTypePoliris('appartement'), // 1 - sousType: $mapping->getSousType('appartement', 'duplex') // 1 + type: $mapping->getTypePoliris('apartment'), // 1 + sousType: $mapping->getSubType('apartment', 'duplex') // 1 ) ->withDetail( activitesCommerciales: null, - label: 'Duplex 4 piĂšces', - description: 'Magnifique duplex...', + label: '4-room duplex', + description: 'Magnificent duplex...', datesDispo: new \DateTimeImmutable(), amenagementHandicapes: false, animauxAcceptes: false, - duplex: true, // Important pour SeLoger + duplex: true, // Important for SeLoger commPrives: null, logementADisposition: null, nomModele: null ); ``` -#### Publication spĂ©cifique SeLoger +#### SeLoger-specific Publication ```php $builder ->startLine() ->withIdentifiant(/* ... */) ->withPublication( - publications: 'seloger', // Portails cibles : seloger, leboncoin, etc. - coupDeCoeur: true, // Mise en avant - versionFormat: '4.1.17' // Version Poliris + publications: 'seloger', // Target portals: seloger, leboncoin, etc. + coupDeCoeur: true, // Featured listing + versionFormat: '4.1.17' // Poliris version ); ``` --- -### Cas #2 : Inclure des photos dans l'export +### Case #2: Including Photos in Export -Les portails immobiliers nĂ©cessitent des photos de qualitĂ©. Le bundle supporte jusqu'Ă  **30 photos par annonce**. +Real estate portals require quality photos. The bundle supports up to **30 photos per listing**. -#### Exemple avec photos +#### Example with Photos ```php withIdentifiant('AGENCE001', 'REF-001', 'vente', 'TECH-001') ->withType(1, null) ->withPhoto( - // URLs des photos (30 max) - photo1: 'https://www.example.com/photos/bien-001/salon.jpg', - photo2: 'https://www.example.com/photos/bien-001/cuisine.jpg', - photo3: 'https://www.example.com/photos/bien-001/chambre1.jpg', - photo4: 'https://www.example.com/photos/bien-001/chambre2.jpg', - photo5: 'https://www.example.com/photos/bien-001/sdb.jpg', - photo6: 'https://www.example.com/photos/bien-001/exterieur.jpg', + // Photo URLs (30 max) + photo1: 'https://www.example.com/photos/property-001/living-room.jpg', + photo2: 'https://www.example.com/photos/property-001/kitchen.jpg', + photo3: 'https://www.example.com/photos/property-001/bedroom1.jpg', + photo4: 'https://www.example.com/photos/property-001/bedroom2.jpg', + photo5: 'https://www.example.com/photos/property-001/bathroom.jpg', + photo6: 'https://www.example.com/photos/property-001/exterior.jpg', photo7: null, photo8: null, photo9: null, @@ -563,13 +563,13 @@ $builder photo29: null, photo30: null, - // Titres des photos (optionnels) - titre1: 'Salon lumineux', - titre2: 'Cuisine Ă©quipĂ©e', - titre3: 'Chambre 1', - titre4: 'Chambre 2', - titre5: 'Salle de bain', - titre6: 'Vue extĂ©rieure', + // Photo titles (optional) + titre1: 'Bright living room', + titre2: 'Equipped kitchen', + titre3: 'Bedroom 1', + titre4: 'Bedroom 2', + titre5: 'Bathroom', + titre6: 'Exterior view', titre7: null, titre8: null, titre9: null, @@ -595,29 +595,29 @@ $builder titre29: null, titre30: null, - // Photo panoramique et visite virtuelle - photoPanoramique: 'https://www.example.com/photos/bien-001/panorama.jpg', - urlVisiteVirtuelle: 'https://www.example.com/visite-virtuelle/bien-001' + // Panoramic photo and virtual tour + photoPanoramique: 'https://www.example.com/photos/property-001/panorama.jpg', + urlVisiteVirtuelle: 'https://www.example.com/virtual-tour/property-001' ); ``` -#### Bonnes pratiques pour les photos +#### Best Practices for Photos -✅ **Ordre des photos** : Commencez par les piĂšces principales (salon, cuisine) -✅ **QualitĂ©** : Minimum 1024x768, idĂ©al 1920x1080 -✅ **Format** : JPG ou PNG -✅ **Poids** : < 2 Mo par photo -✅ **Titres** : Descriptifs et informatifs +✅ **Photo order**: Start with main rooms (living room, kitchen) +✅ **Quality**: Minimum 1024x768, ideal 1920x1080 +✅ **Format**: JPG or PNG +✅ **Size**: < 2 MB per photo +✅ **Titles**: Descriptive and informative -❌ **À Ă©viter** : Photos floues, trop sombres, avec filigrane +❌ **Avoid**: Blurry photos, too dark, with watermarks --- -### Cas #3 : Automatiser un export programmĂ© +### Case #3: Automated Scheduled Export -#### Option A : Avec une commande Symfony +#### Option A: Symfony Command -CrĂ©ez une commande pour gĂ©nĂ©rer l'export : +Create a command to generate the export: ```php title('GĂ©nĂ©ration du fichier Poliris'); + $io->title('Poliris File Generation'); try { - // GĂ©nĂ©ration du CSV + // Generate CSV $csvContent = $this->exportService->generateExport(); - // Sauvegarde du fichier + // Save file $filename = sprintf( 'poliris-export-%s.csv', (new \DateTime())->format('Y-m-d-His') @@ -663,18 +663,18 @@ class ExportPolirisCommand extends Command file_put_contents($filepath, $csvContent); - $io->success(sprintf('Export gĂ©nĂ©rĂ© : %s', $filename)); + $io->success(sprintf('Export generated: %s', $filename)); return Command::SUCCESS; } catch (\Exception $e) { - $io->error('Erreur lors de la gĂ©nĂ©ration : ' . $e->getMessage()); + $io->error('Error during generation: ' . $e->getMessage()); return Command::FAILURE; } } } ``` -**Configuration** (`config/services.yaml`) : +**Configuration** (`config/services.yaml`): ```yaml # config/services.yaml @@ -684,24 +684,24 @@ services: $exportDir: '%kernel.project_dir%/var/exports' ``` -**ExĂ©cution** : +**Execution**: ```bash php bin/console app:export:poliris ``` -#### Option B : Avec un cron +#### Option B: Cron Job -Ajoutez une tĂąche cron pour exĂ©cuter l'export quotidiennement : +Add a cron task to run the export daily: ```bash -# Crontab : Export tous les jours Ă  2h du matin +# Crontab: Export every day at 2am 0 2 * * * cd /var/www/myapp && php bin/console app:export:poliris >> /var/log/poliris-export.log 2>&1 ``` -#### Option C : Avec un Event Symfony +#### Option C: Symfony Event -DĂ©clenchez l'export automatiquement lors de certains Ă©vĂ©nements : +Trigger export automatically on certain events: ```php exportService->generateExport(); - // Enregistrer ou envoyer le fichier + // Save or send the file // ... } } ``` -#### Option D : Via Messenger (asynchrone) +#### Option D: Symfony Messenger (Asynchronous) -Pour des exports lourds, utilisez Symfony Messenger : +For heavy exports, use Symfony Messenger: ```php exportService->generateExport(); - // Sauvegarder le fichier + // Save the file file_put_contents( '/var/exports/poliris-' . $message->requestedAt->format('YmdHis') . '.csv', $csvContent @@ -785,7 +785,7 @@ class GeneratePolirisExportHandler } ``` -**DĂ©clencher l'export** : +**Trigger the export**: ```php $messageBus->dispatch(new GeneratePolirisExportMessage(new \DateTimeImmutable())); @@ -793,100 +793,100 @@ $messageBus->dispatch(new GeneratePolirisExportMessage(new \DateTimeImmutable()) --- -### Cas #4 : Erreur courante et solution +### Case #4: Common Error and Solution -#### ❌ Erreur : "Invalid CSV format" +#### ❌ Error: "Invalid CSV format" -**SymptĂŽme** : -Le portail refuse le CSV avec le message "Format invalide". +**Symptom**: +Portal rejects CSV with "Invalid format" message. -**Causes possibles** : +**Possible causes**: -1. **Encodage incorrect** : Le CSV doit ĂȘtre en UTF-8 -2. **SĂ©parateur incorrect** : Poliris utilise des virgules (`,`) -3. **Nombre de colonnes incorrect** : 333 colonnes attendues -4. **Valeurs manquantes** : Certaines colonnes obligatoires sont vides +1. **Incorrect encoding**: CSV must be UTF-8 +2. **Wrong separator**: Poliris uses commas (`,`) +3. **Incorrect column count**: 333 columns expected +4. **Missing values**: Some required columns are empty -**Solution 1 : VĂ©rifier l'encodage** +**Solution 1: Check encoding** ```php $csvContent = $builder->build()->toCSV(); -// Forcer l'encodage UTF-8 +// Force UTF-8 encoding $csvContent = mb_convert_encoding($csvContent, 'UTF-8', 'auto'); -// Ajouter le BOM UTF-8 (parfois nĂ©cessaire) +// Add UTF-8 BOM (sometimes required) $csvContent = "\xEF\xBB\xBF" . $csvContent; ``` -**Solution 2 : VĂ©rifier les colonnes obligatoires** +**Solution 2: Check required columns** -Assurez-vous de remplir **au minimum** : +Ensure you fill in **at minimum**: ```php $builder ->startLine() - // OBLIGATOIRES + // REQUIRED ->withIdentifiant( - agenceId: 'AGENCE001', // ✅ Requis - agencePropertyRef: 'REF-001', // ✅ Requis - annonceType: 'vente', // ✅ Requis - annonceIdTechnique: 'TECH-001' // ✅ Requis + agenceId: 'AGENCE001', // ✅ Required + agencePropertyRef: 'REF-001', // ✅ Required + annonceType: 'vente', // ✅ Required + annonceIdTechnique: 'TECH-001' // ✅ Required ) ->withType( - type: 1, // ✅ Requis + type: 1, // ✅ Required sousType: null ) ->withLocalisation( - cp: '75001', // ✅ Requis - ville: 'Paris', // ✅ Requis - pays: 'France', // ✅ Requis - adresse: null, // Optionnel - // ... autres paramĂštres optionnels + cp: '75001', // ✅ Required + ville: 'Paris', // ✅ Required + pays: 'France', // ✅ Required + adresse: null, // Optional + // ... other optional parameters ) ->withSurface( - surface: 65, // ✅ Requis + surface: 65, // ✅ Required surfaceTerrain: null, - nbPieces: 3, // ✅ Requis - nbChambres: 2, // ✅ Requis (selon type) - // ... autres paramĂštres + nbPieces: 3, // ✅ Required + nbChambres: 2, // ✅ Required (depending on type) + // ... other parameters ) ->withPrix( - prix: 450000, // ✅ Requis - // ... autres paramĂštres optionnels + prix: 450000, // ✅ Required + // ... other optional parameters ) ->withDetail( activitesCommerciales: null, - label: 'Mon annonce', // ✅ Requis - description: 'Description...', // ✅ Requis - // ... autres paramĂštres + label: 'My listing', // ✅ Required + description: 'Description...', // ✅ Required + // ... other parameters ); ``` -**Solution 3 : Activer les logs de debug** +**Solution 3: Enable debug logging** ```php try { $export = $builder->build(); $csvContent = $export->toCSV(); } catch (\Throwable $e) { - // Logger l'erreur - error_log('Erreur Poliris : ' . $e->getMessage()); - error_log('Trace : ' . $e->getTraceAsString()); + // Log the error + error_log('Poliris error: ' . $e->getMessage()); + error_log('Trace: ' . $e->getTraceAsString()); throw $e; } ``` -#### ❌ Erreur : "Memory exhausted" +#### ❌ Error: "Memory exhausted" -**SymptĂŽme** : +**Symptom**: `PHP Fatal error: Allowed memory size exhausted` -**Cause** : -Trop d'annonces en mĂ©moire (plusieurs milliers). +**Cause**: +Too many listings in memory (several thousand). -**Solution : Traiter par lots** +**Solution: Process in batches** ```php addProperty($builder, $property); $count++; - // GĂ©nĂ©rer un CSV toutes les 500 annonces + // Generate CSV every 500 listings if ($count % self::BATCH_SIZE === 0) { yield $builder->build()->toCSV(); - // RĂ©initialiser le builder + // Reset builder $builder = new AnnonceExportBuilder(); - // LibĂ©rer la mĂ©moire + // Free memory gc_collect_cycles(); } } - // DerniĂšres annonces + // Last listings if ($count % self::BATCH_SIZE !== 0) { yield $builder->build()->toCSV(); } @@ -936,146 +936,146 @@ class PolirisExportService } ``` -**Utilisation** : +**Usage**: ```php -// Dans un contrĂŽleur ou une commande +// In a controller or command $properties = $propertyRepository->findAllAsIterator(); foreach ($exportService->generateExportInBatches($properties) as $batch) { - // Écrire chaque batch dans un fichier + // Write each batch to a file file_put_contents('/tmp/export-batch-' . time() . '.csv', $batch, FILE_APPEND); } ``` --- -## 📚 RĂ©fĂ©rence des modĂšles +## Model Reference -Le bundle fournit **31 modĂšles** pour reprĂ©senter tous les aspects d'un bien immobilier. +The bundle provides **31 models** to represent all aspects of a real estate property. -### Vue d'ensemble des modĂšles +### Model Overview -| ModĂšle | Description | Colonnes CSV | +| Model | Description | CSV Columns | |--------|-------------|--------------| -| **Identifiant** | ID agence, rĂ©fĂ©rence bien | 1-3, 175 | -| **Type** | Type de bien (appt, maison, etc.) | 4, 181 | -| **Photo** | URLs photos (30 max) + visite virtuelle | 5-10, 96-125 | -| **Localisation** | Adresse, CP, ville, GPS | 11-19, 158-165 | -| **Surface** | Surface, piĂšces, chambres | 20-29, 182-188 | -| **Prix** | Prix, loyer, charges | 30-34, 126-137 | -| **Detail** | Titre, description, disponibilitĂ© | 35-40, 194-196 | +| **Identifiant** | Agency ID, property reference | 1-3, 175 | +| **Type** | Property type (apt, house, etc.) | 4, 181 | +| **Photo** | Photo URLs (30 max) + virtual tour | 5-10, 96-125 | +| **Localisation** | Address, postal code, city, GPS | 11-19, 158-165 | +| **Surface** | Surface, rooms, bedrooms | 20-29, 182-188 | +| **Prix** | Price, rent, charges | 30-34, 126-137 | +| **Detail** | Title, description, availability | 35-40, 194-196 | | **Diagnostic** | DPE, GES, diagnostics | 41-50, 198-205 | -| **Contact** | TĂ©lĂ©phone, email, nĂ©gociateur | 51-57, 300-305 | -| **Exterieur** | Ascenseur, piscine, vue | 58-64 | -| **Interieur** | MeublĂ©, annĂ©e construction | 65-85 | -| **ChauffageClim** | Type chauffage, climatisation | 86-87 | -| **PartieJour** | Cuisine, sĂ©jour, Ă©quipements | 88-95 | -| **Etage** | Étage, nombre d'Ă©tages | 138-140 | -| **Securite** | Digicode, interphone, alarme | 141-144 | +| **Contact** | Phone, email, agent | 51-57, 300-305 | +| **Exterieur** | Elevator, pool, view | 58-64 | +| **Interieur** | Furnished, construction year | 65-85 | +| **ChauffageClim** | Heating type, air conditioning | 86-87 | +| **PartieJour** | Kitchen, living room, equipment | 88-95 | +| **Etage** | Floor, number of floors | 138-140 | +| **Securite** | Digicode, intercom, alarm | 141-144 | | **Garage** | Garage, type | 145-146 | -| **Parking** | Nombre vĂ©hicules, type parking | 147-149 | -| **Bureau** | Infos bureaux professionnels | 150-167 | -| **Boutique** | Infos commerce | 168-169 | -| **Terrain** | Terrain constructible, agricole | 170-176 | -| **Location** | DurĂ©e bail, nature bail | 177-180, 206-209 | -| **Viager** | Bouquet, rente mensuelle | 210-214 | -| **Mandat** | Mandat exclusif, numĂ©ro | 215-225 | -| **HonoraireCharge** | Honoraires, charges | 226-235 | -| **Diagnostic** | Diagnostics immobiliers | 236-250 | -| **FondsCommerce** | CA, rĂ©sultats (commerce) | 251-258 | -| **ProduitInvestissement** | Valeur achat, rapport | 259-260 | -| **LocationVacances** | Prix saison, disponibilitĂ©s | 261-272 | -| **Langue** | Multilangue (3 langues max) | 273-284 | -| **Publication** | Portails de diffusion | 285-287 | -| **ChampCustom** | Champs personnalisĂ©s (26 max) | 288-313 | - -### ModĂšles les plus utilisĂ©s - -#### 1. Identifiant (colonnes 1-3, 175) +| **Parking** | Number of vehicles, parking type | 147-149 | +| **Bureau** | Office property information | 150-167 | +| **Boutique** | Shop information | 168-169 | +| **Terrain** | Buildable land, agricultural | 170-176 | +| **Location** | Lease duration, lease type | 177-180, 206-209 | +| **Viager** | Lump sum, monthly annuity | 210-214 | +| **Mandat** | Exclusive mandate, number | 215-225 | +| **HonoraireCharge** | Fees, charges | 226-235 | +| **Diagnostic** | Property diagnostics | 236-250 | +| **FondsCommerce** | Revenue, results (business) | 251-258 | +| **ProduitInvestissement** | Purchase value, return | 259-260 | +| **LocationVacances** | Season prices, availability | 261-272 | +| **Langue** | Multilingual (3 languages max) | 273-284 | +| **Publication** | Distribution portals | 285-287 | +| **ChampCustom** | Custom fields (26 max) | 288-313 | + +### Most Used Models + +#### 1. Identifiant (columns 1-3, 175) ```php ->withIdentifiant( - agenceId: 'AGENCE001', // Col 1 : ID agence - agencePropertyRef: 'REF-001', // Col 2 : RĂ©fĂ©rence bien - annonceType: 'vente', // Col 3 : vente/location - annonceIdTechnique: 'TECH-001' // Col 175 : ID technique + agenceId: 'AGENCE001', // Col 1: Agency ID + agencePropertyRef: 'REF-001', // Col 2: Property reference + annonceType: 'vente', // Col 3: vente/location + annonceIdTechnique: 'TECH-001' // Col 175: Technical ID ) ``` -**Types d'annonce** : +**Listing types**: - `Annonce::ANNONCE_TYPE_VENTE` = `'vente'` - `Annonce::ANNONCE_TYPE_LOCATION` = `'location'` - `Annonce::ANNONCE_TYPE_MODELE_MAISON` = `'modĂšle de maison'` -#### 2. Type (colonnes 4, 181) +#### 2. Type (columns 4, 181) ```php ->withType( - type: 1, // Col 4 : Type principal - sousType: null // Col 181 : Sous-type + type: 1, // Col 4: Main type + sousType: null // Col 181: Subtype ) ``` -**Codes types** : -- `1` = Appartement -- `2` = Maison +**Type codes**: +- `1` = Apartment +- `2` = House - `3` = Parking/Box -- `4` = Terrain -- `5` = Boutique -- `6` = Bureau -- `7` = BĂątiment -- `8` = ChĂąteau -- `9` = Immeuble +- `4` = Land +- `5` = Shop +- `6` = Office +- `7` = Building +- `8` = Castle +- `9` = Block - `10` = Loft -- `11` = Local commercial -- `13` = Programme neuf +- `11` = Commercial premises +- `13` = New development -#### 3. Surface (colonnes 20-29, 182-188) +#### 3. Surface (columns 20-29, 182-188) ```php ->withSurface( - surface: 65, // Col 20 : Surface habitable (mÂČ) - surfaceTerrain: 200, // Col 21 : Surface terrain (mÂČ) - nbPieces: 3, // Col 22 : Nombre de piĂšces - nbChambres: 2, // Col 23 : Nombre de chambres - nbSdb: 1, // Col 24 : Salles de bain - nbSalleEau: 1, // Col 25 : Salles d'eau - nbWc: 1, // Col 26 : WC - nbBalcons: 1, // Col 27 : Nombre de balcons - surfaceBalcon: 5, // Col 28 : Surface balcon (mÂČ) - nbParkings: 1, // Col 29 : Parkings - // ... 18 autres paramĂštres + surface: 65, // Col 20: Living area (mÂČ) + surfaceTerrain: 200, // Col 21: Land area (mÂČ) + nbPieces: 3, // Col 22: Number of rooms + nbChambres: 2, // Col 23: Number of bedrooms + nbSdb: 1, // Col 24: Bathrooms + nbSalleEau: 1, // Col 25: Shower rooms + nbWc: 1, // Col 26: WC + nbBalcons: 1, // Col 27: Number of balconies + surfaceBalcon: 5, // Col 28: Balcony surface (mÂČ) + nbParkings: 1, // Col 29: Parkings + // ... 18 other parameters ) ``` -#### 4. Prix (colonnes 30-34, 126-137) +#### 4. Prix (columns 30-34, 126-137) ```php ->withPrix( - prix: 450000, // Col 30 : Prix vente ou loyer - loyerMoisMur: null, // Col 31 : Loyer mur - loyerCC: 500, // Col 32 : Loyer charges comprises - loyerHT: null, // Col 33 : Loyer HT (pro) - depotGarantie: 1000, // Col 34 : DĂ©pĂŽt de garantie - prixMasque: false, // Col 126 : Masquer le prix - prixHT: null, // Col 127 : Prix HT (pro) - copropriete: true, // Col 128 : En copropriĂ©tĂ© - nbLots: 25, // Col 129 : Nombre de lots - syndicatCopro: null, // Col 130 : Syndic - syndicatCoproDetails: 'Charges annuelles : 2400€', // Col 131 - // ... autres paramĂštres + prix: 450000, // Col 30: Sale price or rent + loyerMoisMur: null, // Col 31: Wall rent + loyerCC: 500, // Col 32: Rent including charges + loyerHT: null, // Col 33: Rent excl. tax (professional) + depotGarantie: 1000, // Col 34: Security deposit + prixMasque: false, // Col 126: Hide price + prixHT: null, // Col 127: Price excl. tax (professional) + copropriete: true, // Col 128: In co-ownership + nbLots: 25, // Col 129: Number of lots + syndicatCopro: null, // Col 130: Syndic + syndicatCoproDetails: 'Annual charges: €2400', // Col 131 + // ... other parameters ) ``` -#### 5. Detail (colonnes 35-40, 194-196) +#### 5. Detail (columns 35-40, 194-196) ```php ->withDetail( activitesCommerciales: null, // Col 35 - label: 'Appartement T3 lumineux', // Col 36 : Titre - description: 'Belle description...', // Col 37 : Descriptif - datesDispo: new \DateTimeImmutable(), // Col 38 : Date dispo + label: 'Bright 3-room apartment', // Col 36: Title + description: 'Beautiful description...', // Col 37: Description + datesDispo: new \DateTimeImmutable(), // Col 38: Availability date amenagementHandicapes: false, // Col 39 animauxAcceptes: true, // Col 40 duplex: false, // Col 194 @@ -1087,38 +1087,38 @@ Le bundle fournit **31 modĂšles** pour reprĂ©senter tous les aspects d'un bien i --- -## 🔧 DĂ©pannage +## Troubleshooting ### FAQ -**Q : Puis-je valider les donnĂ©es avant l'export ?** -R : Non, le bundle ne propose pas de validation intĂ©grĂ©e pour des raisons de performance. Validez vos donnĂ©es en amont avec le Validator de Symfony. +**Q: Can I validate data before export?** +A: No, the bundle does not offer built-in validation for performance reasons. Validate your data upstream with Symfony's Validator. -**Q : Comment gĂ©rer les champs multilangues ?** -R : Utilisez le modĂšle `Langue` avec jusqu'Ă  3 langues : +**Q: How to handle multilingual fields?** +A: Use the `Langue` model with up to 3 languages: ```php ->withLangue( code1: 'FR', code2: 'EN', code3: 'DE', - proximite1: 'Proche mĂ©tro', + proximite1: 'Near metro', proximite2: 'Near metro', - proximite3: 'Nahe U-Bahn', - label1: 'Appartement 3 piĂšces', + proximite3: 'Near U-Bahn', + label1: '3-room apartment', label2: '3-room apartment', label3: '3-Zimmer-Wohnung', - descriptif1: 'Belle description en français', + descriptif1: 'Beautiful description in French', descriptif2: 'Beautiful description in English', - descriptif3: 'Schöne Beschreibung auf Deutsch' + descriptif3: 'Beautiful description in German' ) ``` -**Q : Comment exporter uniquement certaines annonces ?** -R : Filtrez vos donnĂ©es en amont dans votre repository : +**Q: How to export only certain listings?** +A: Filter your data upstream in your repository: ```php -// Dans votre service +// In your service public function generateExportForSeLoger(): string { $properties = $this->propertyRepository->findBy([ @@ -1136,11 +1136,11 @@ public function generateExportForSeLoger(): string } ``` -**Q : Le CSV est-il valide pour tous les portails ?** -R : Le format Poliris V4.1.17 est supportĂ© par la majoritĂ© des portails français (SeLoger, LeBonCoin, PAP, etc.). VĂ©rifiez la documentation de chaque portail pour les spĂ©cificitĂ©s. +**Q: Is the CSV valid for all portals?** +A: Poliris V4.1.17 format is supported by most French portals (SeLoger, LeBonCoin, PAP, etc.). Check each portal's documentation for specifics. -**Q : Comment gĂ©rer les erreurs de gĂ©nĂ©ration ?** -R : Utilisez un try-catch et loggez les erreurs : +**Q: How to handle generation errors?** +A: Use try-catch and log errors: ```php use Psr\Log\LoggerInterface; @@ -1154,8 +1154,8 @@ public function generateExport(LoggerInterface $logger): ?string try { $this->addProperty($builder, $property); } catch (\Throwable $e) { - // Logger mais continuer - $logger->error('Erreur annonce ' . $property->getId(), [ + // Log but continue + $logger->error('Listing error ' . $property->getId(), [ 'exception' => $e->getMessage() ]); } @@ -1164,7 +1164,7 @@ public function generateExport(LoggerInterface $logger): ?string return $builder->build()->toCSV(); } catch (\Throwable $e) { - $logger->critical('Erreur gĂ©nĂ©ration CSV Poliris', [ + $logger->critical('Poliris CSV generation error', [ 'exception' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); @@ -1176,33 +1176,33 @@ public function generateExport(LoggerInterface $logger): ?string --- -### Checklist avant mise en production +### Pre-production Checklist -- [ ] Les champs obligatoires sont remplis (Identifiant, Type, Localisation, Surface, Prix, Detail) -- [ ] Les photos sont accessibles via HTTPS -- [ ] L'encodage du CSV est UTF-8 -- [ ] Les dates sont au format `DateTimeImmutable` ou `DateTime` -- [ ] Les boolĂ©ens sont correctement convertis (`true` → `'OUI'`, `false` → `'NON'`) -- [ ] Le fichier CSV est testĂ© sur un portail en environnement de test -- [ ] Les logs sont activĂ©s pour tracer les erreurs -- [ ] Un systĂšme de monitoring est en place (export quotidien rĂ©ussi/Ă©chouĂ©) -- [ ] La performance est acceptable (< 30s pour 1000 annonces) +- [ ] Required fields are filled (Identifiant, Type, Localisation, Surface, Prix, Detail) +- [ ] Photos are accessible via HTTPS +- [ ] CSV encoding is UTF-8 +- [ ] Dates use `DateTimeImmutable` or `DateTime` +- [ ] Booleans are correctly converted (`true` → `'OUI'`, `false` → `'NON'`) +- [ ] CSV file is tested on a portal in test environment +- [ ] Logging is enabled to trace errors +- [ ] Monitoring system is in place (daily export success/failure) +- [ ] Performance is acceptable (< 30s for 1000 listings) --- -### Ressources supplĂ©mentaires +### Additional Resources -- **Documentation officielle Poliris** : [https://www.poliris.com/documentation](https://www.poliris.com/documentation) -- **Repository GitHub** : [https://github.com/SpiriitLabs/poliris-bundle](https://github.com/SpiriitLabs/poliris-bundle) -- **Packagist** : [https://packagist.org/packages/spiriitlabs/poliris-bundle](https://packagist.org/packages/spiriitlabs/poliris-bundle) +- **Official Poliris documentation**: [https://www.poliris.com/documentation](https://www.poliris.com/documentation) +- **GitHub repository**: [https://github.com/SpiriitLabs/poliris-bundle](https://github.com/SpiriitLabs/poliris-bundle) +- **Packagist**: [https://packagist.org/packages/spiriitlabs/poliris-bundle](https://packagist.org/packages/spiriitlabs/poliris-bundle) --- -## 📝 SchĂ©ma d'architecture +## Architecture Diagram ``` ┌─────────────────────────────────────────────────────────────┐ -│ Votre Application │ +│ Your Application │ │ │ │ ┌────────────────┐ ┌─────────────────┐ │ │ │ Controller │─────▶│ ExportService │ │ @@ -1218,7 +1218,7 @@ public function generateExport(LoggerInterface $logger): ?string │ │ AnnonceBuilder (Bundle) │ │ │ └──────────────┬──────────────┘ │ │ │ │ -│ Utilise 31 modĂšles │ +│ Uses 31 models │ │ │ │ │ ┌──────────────┌──────────────┐ │ │ â–Œ â–Œ â–Œ │ @@ -1230,8 +1230,8 @@ public function generateExport(LoggerInterface $logger): ?string │ â–Œ ┌────────────────┐ - │ Fichier CSV │ - │ (333 colonnes) │ + │ CSV File │ + │ (333 columns) │ └────────┬───────┘ │ ┌────────────────┌────────────────┐ @@ -1241,21 +1241,21 @@ public function generateExport(LoggerInterface $logger): ?string --- -## 📊 Tableau rĂ©capitulatif des cas d'usage +## Use Case Summary Table -| Cas d'usage | DifficultĂ© | Temps estimĂ© | ModĂšles requis | +| Use Case | Difficulty | Estimated Time | Required Models | |-------------|------------|--------------|----------------| -| Export basique | ⭐ Facile | 15 min | 5-7 modĂšles | -| Export avec photos | ⭐⭐ Moyen | 30 min | 8-10 modĂšles | -| Export SeLoger complet | ⭐⭐⭐ AvancĂ© | 1-2h | 15-20 modĂšles | -| Automatisation cron | ⭐⭐ Moyen | 45 min | DĂ©pend du cas | -| Export asynchrone | ⭐⭐⭐ AvancĂ© | 2-3h | DĂ©pend du cas | -| Multi-portails | ⭐⭐⭐ AvancĂ© | 3-4h | 20-25 modĂšles | +| Basic export | ⭐ Easy | 15 min | 5-7 models | +| Export with photos | ⭐⭐ Medium | 30 min | 8-10 models | +| Complete SeLoger export | ⭐⭐⭐ Advanced | 1-2h | 15-20 models | +| Cron automation | ⭐⭐ Medium | 45 min | Depends on case | +| Asynchronous export | ⭐⭐⭐ Advanced | 2-3h | Depends on case | +| Multi-portal | ⭐⭐⭐ Advanced | 3-4h | 20-25 models | --- -**VoilĂ  ! Vous ĂȘtes maintenant prĂȘt Ă  utiliser PolirisBundle comme un pro ! 🚀** +**You're now ready to use PolirisBundle like a pro! 🚀** -Si vous avez des questions, n'hĂ©sitez pas Ă  ouvrir une issue sur GitHub ou Ă  consulter les exemples dans le dossier `tests/`. +If you have questions, feel free to open an issue on GitHub or check the examples in the `tests/` folder. -Bon export ! 🏠✹ +Happy exporting! 🏠✹