Update documentation for EspoCRM Best Practices and Validator Tool
This commit is contained in:
@@ -1,11 +1,29 @@
|
||||
# EspoCRM Best Practices & Entwicklungsrichtlinien
|
||||
|
||||
**Version:** 2.1
|
||||
**Datum:** 9. März 2026
|
||||
**Version:** 2.2
|
||||
**Datum:** 10. März 2026
|
||||
**Zielgruppe:** AI Code Agents & Entwickler
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Letzte Änderungen (v2.2 - 10. März 2026)
|
||||
|
||||
**Kritische Erkenntnisse:**
|
||||
- ✅ **Service-Klassen sind PFLICHT**: Neue Section über erforderliche Service-Klassen
|
||||
- ✅ **InjectableFactory-Fehler**: Detaillierter Troubleshooting-Guide hinzugefügt
|
||||
- ✅ **validate_and_rebuild.py v2.0**: Erweiterte Log-Prüfung und CRUD-Tests
|
||||
- ✅ **Real-World-Beispiel**: CAICollection/CAdvowareAkten Fehlerfall dokumentiert
|
||||
|
||||
**Tools:**
|
||||
- 🆕 Minimierter/Verbose Output-Modus
|
||||
- 🆕 Log-Prüfung nach jedem API-Request
|
||||
- 🆕 CRUD-Tests für alle Entities
|
||||
- 🆕 KI-freundliches JSON-Feedback
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## 📋 Inhaltsverzeichnis
|
||||
|
||||
1. [Projekt-Übersicht](#projekt-übersicht)
|
||||
@@ -273,6 +291,90 @@ client/custom/ # Frontend-Code
|
||||
- `stream: true` - Stream/Activity Feed
|
||||
- `calendar: true` - Für Entities mit Datum-Feldern
|
||||
|
||||
### Service-Klassen (KRITISCH!)
|
||||
|
||||
**⚠️ PFLICHT:** Jede Custom Entity MUSS eine Service-Klasse haben!
|
||||
|
||||
**Problem:** Ohne Service-Klasse gibt EspoCRM beim Zugriff folgenden Fehler:
|
||||
```
|
||||
CRITICAL: InjectableFactory: Class 'Espo\Custom\Services\{EntityName}' does not exist.
|
||||
```
|
||||
|
||||
**Lösung:** Erstelle für jede Entity eine Service-Klasse:
|
||||
|
||||
**Datei:** `custom/Espo/Custom/Services/{EntityName}.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace Espo\Custom\Services;
|
||||
|
||||
use Espo\Services\Record;
|
||||
|
||||
/**
|
||||
* Service für {EntityName} Entity
|
||||
*/
|
||||
class {EntityName} extends Record
|
||||
{
|
||||
// Basis-Service-Funktionalität wird von Record geerbt
|
||||
// Custom Business Logic kann hier hinzugefügt werden
|
||||
}
|
||||
```
|
||||
|
||||
**Minimale Service-Klasse:**
|
||||
- Erbt von `\Espo\Services\Record`
|
||||
- Muss im Namespace `Espo\Custom\Services` sein
|
||||
- Klassenname muss exakt dem Entity-Namen entsprechen
|
||||
- Mindestens leerer Body erforderlich
|
||||
|
||||
**Erweiterte Service mit Custom Logic:**
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace Espo\Custom\Services;
|
||||
|
||||
use Espo\Services\Record;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
|
||||
class {EntityName} extends Record
|
||||
{
|
||||
/**
|
||||
* Custom Business Logic Methode
|
||||
*/
|
||||
public function customAction(string $id, \stdClass $data): array
|
||||
{
|
||||
// ACL Check
|
||||
if (!$this->getAcl()->checkEntityEdit($this->entityType)) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
// Load Entity
|
||||
$entity = $this->getEntityManager()->getEntity($this->entityType, $id);
|
||||
if (!$entity) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
// Business Logic hier
|
||||
$entity->set('status', 'Processed');
|
||||
$this->getEntityManager()->saveEntity($entity);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'id' => $entity->getId()
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Best Practice:**
|
||||
1. ✅ Erstelle Service-Klasse SOFORT bei Entity-Erstellung
|
||||
2. ✅ Auch wenn initial leer, erstelle sie trotzdem
|
||||
3. ✅ Nutze Service für Business Logic statt Hooks
|
||||
4. ✅ Verwende Type Hints für bessere IDE-Unterstützung
|
||||
5. ✅ Dokumentiere Custom-Methoden mit DocBlocks
|
||||
|
||||
---
|
||||
|
||||
### i18n (Internationalisierung)
|
||||
|
||||
**KRITISCH:** Immer BEIDE Sprachen pflegen!
|
||||
@@ -1282,35 +1384,113 @@ php custom/scripts/workflow_manager.php list
|
||||
|
||||
## Testing & Validierung
|
||||
|
||||
### Validierungs-Tool
|
||||
### Validierungs-Tool v2.0 (Erweitert)
|
||||
|
||||
**Haupt-Tool:** `custom/scripts/validate_and_rebuild.py`
|
||||
|
||||
#### Verwendung
|
||||
|
||||
```bash
|
||||
# Vollständige Validierung + Rebuild
|
||||
# Standard: Minimaler Output, vollständige Tests
|
||||
python3 custom/scripts/validate_and_rebuild.py
|
||||
|
||||
# Verbose: Detaillierte Ausgabe für Debugging
|
||||
python3 custom/scripts/validate_and_rebuild.py -v
|
||||
|
||||
# Nur Validierung (kein Rebuild)
|
||||
python3 custom/scripts/validate_and_rebuild.py --dry-run
|
||||
|
||||
# Mit E2E Tests überspringen
|
||||
python3 custom/scripts/validate_and_rebuild.py --skip-e2e
|
||||
# Ohne Entity CRUD-Tests
|
||||
python3 custom/scripts/validate_and_rebuild.py --skip-tests
|
||||
|
||||
# Mit Custom-Credentials
|
||||
python3 custom/scripts/validate_and_rebuild.py --username admin --password secret
|
||||
|
||||
# Mit Custom Base-URL
|
||||
python3 custom/scripts/validate_and_rebuild.py --base-url http://my-espo.local
|
||||
```
|
||||
|
||||
**Das Tool prüft:**
|
||||
#### Was das Tool prüft
|
||||
|
||||
**Phase 1: Statische Validierung**
|
||||
1. ✅ JSON-Syntax aller Custom-Dateien
|
||||
2. ✅ Relationship-Konsistenz (bidirektionale Links)
|
||||
3. ✅ Formula-Script Platzierung
|
||||
4. ✅ i18n-Vollständigkeit (de_DE + en_US)
|
||||
5. ✅ Layout-Struktur (bottomPanelsDetail, detail.json)
|
||||
6. ✅ Dateirechte (www-data:www-data)
|
||||
7. ✅ CSS-Validierung (csslint)
|
||||
8. ✅ JavaScript-Validierung (jshint)
|
||||
9. ✅ PHP-Syntax (php -l)
|
||||
10. ✅ EspoCRM Rebuild
|
||||
11. ✅ E2E-Tests (CRUD-Operationen)
|
||||
3. ✅ Erforderliche Dateien (scopes, i18n)
|
||||
4. ✅ Dateirechte (www-data:www-data) + Auto-Fix
|
||||
5. ✅ PHP-Syntax (php -l)
|
||||
|
||||
**Bei Fehlern:** Automatische Fehlerlog-Analyse der letzten 50 Log-Zeilen!
|
||||
**Phase 2: Rebuild mit Log-Prüfung**
|
||||
6. ✅ Cache-Clearing
|
||||
7. ✅ EspoCRM Rebuild
|
||||
8. ✅ **Log-Prüfung direkt nach Rebuild** (mit präzisen Zeitstempeln)
|
||||
|
||||
**Phase 3: Live Entity-Tests** (wenn nicht übersprungen)
|
||||
9. ✅ **CREATE** - Eintrag erstellen + Log-Check
|
||||
10. ✅ **READ** - Eintrag lesen + Log-Check
|
||||
11. ✅ **UPDATE** - Eintrag aktualisieren + Log-Check
|
||||
12. ✅ **LIST** - Liste abrufen + Log-Check
|
||||
13. ✅ **DELETE** - Eintrag löschen + Log-Check
|
||||
14. ✅ Automatisches Cleanup aller Test-Records
|
||||
|
||||
#### Neue Features (v2.0)
|
||||
|
||||
**1. Log-Integration:**
|
||||
- Nach **jedem** API-Request werden Logs geprüft
|
||||
- Präzise Zeitstempel (nur Logs seit Request-Start)
|
||||
- Test bricht ab bei Fehler in Logs
|
||||
- Filtert bekannte unwichtige Meldungen
|
||||
|
||||
**2. Intelligenter Output:**
|
||||
- **Standard-Modus:** Nur Ergebnisse (✓/✗)
|
||||
- **Verbose-Modus (`-v`):** Detaillierte Informationen, Timing, Debugging
|
||||
|
||||
**3. CRUD-Tests für alle Entities:**
|
||||
- Testet jede Custom-Entity einzeln
|
||||
- Validiert kompletten Lifecycle
|
||||
- Prüft API-Responses
|
||||
- Detektiert Service-Klassen-Fehler
|
||||
|
||||
**4. KI-freundliches Feedback:**
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"summary": {
|
||||
"errors": 0,
|
||||
"warnings": 1,
|
||||
"entities_checked": 28
|
||||
},
|
||||
"errors": [],
|
||||
"warnings": ["..."],
|
||||
"recommendations": [
|
||||
"Fehlende Service-Klassen - erstelle für jede Entity eine Service-Klasse",
|
||||
"Unvollständige Übersetzungen - füge fehlende i18n-Dateien hinzu"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**5. Fehler-Abbruch bei Log-Errors:**
|
||||
- Script bricht sofort ab wenn Rebuild Fehler in Logs produziert
|
||||
- Zeigt relevante Fehlermeldungen an
|
||||
- Keine weiteren Tests bei kritischen Fehlern
|
||||
|
||||
#### Typischer Workflow
|
||||
|
||||
```bash
|
||||
# 1. Nach Code-Änderungen: Quick Check
|
||||
python3 custom/scripts/validate_and_rebuild.py --dry-run
|
||||
|
||||
# 2. Vor Deployment: Vollständiger Test
|
||||
python3 custom/scripts/validate_and_rebuild.py -v
|
||||
|
||||
# 3. Rebuild ohne Tests (schneller)
|
||||
python3 custom/scripts/validate_and_rebuild.py --skip-tests
|
||||
```
|
||||
|
||||
**Bei Fehlern:** Das Tool zeigt automatisch:
|
||||
- Fehlerlog-Analyse
|
||||
- Betroffene Dateien
|
||||
- Konkrete Fehlermeldungen
|
||||
- Empfohlene Fixes
|
||||
|
||||
### End-to-End Tests
|
||||
|
||||
@@ -1542,6 +1722,102 @@ docker exec espocrm php -l custom/Espo/Custom/Controllers/MyController.php
|
||||
- [ ] Action-Methode korrekt benannt? (postAction..., getAction...)
|
||||
- [ ] ACL-Rechte?
|
||||
|
||||
### ⚠️ KRITISCH: InjectableFactory Error (Service-Klasse fehlt)
|
||||
|
||||
**Fehlermeldung in Logs:**
|
||||
```
|
||||
CRITICAL: (0) InjectableFactory: Class 'Espo\Custom\Services\{EntityName}' does not exist.
|
||||
:: GET /{EntityName} :: /var/www/html/application/Espo/Core/InjectableFactory.php(164)
|
||||
```
|
||||
|
||||
**Symptome:**
|
||||
- Entity in UI sichtbar, aber nicht aufrufbar
|
||||
- API-Requests schlagen fehl (leer oder 500 Error)
|
||||
- Fehler tritt bei JEDEM Zugriff auf die Entity auf
|
||||
- Rebuild erfolgreich, aber Funktionalität fehlt
|
||||
|
||||
**Ursache:**
|
||||
Für die Custom Entity `{EntityName}` wurde keine Service-Klasse erstellt. EspoCRM sucht nach `custom/Espo/Custom/Services/{EntityName}.php` und findet sie nicht.
|
||||
|
||||
**Lösung:**
|
||||
|
||||
**Schritt 1:** Erstelle Service-Datei
|
||||
|
||||
```bash
|
||||
# Erstelle Datei
|
||||
touch custom/Espo/Custom/Services/{EntityName}.php
|
||||
```
|
||||
|
||||
**Schritt 2:** Füge minimale Service-Klasse ein
|
||||
|
||||
**Datei:** `custom/Espo/Custom/Services/{EntityName}.php`
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace Espo\Custom\Services;
|
||||
|
||||
use Espo\Services\Record;
|
||||
|
||||
/**
|
||||
* Service für {EntityName} Entity
|
||||
*/
|
||||
class {EntityName} extends Record
|
||||
{
|
||||
// Basis-Funktionalität wird von Record geerbt
|
||||
}
|
||||
```
|
||||
|
||||
**Schritt 3:** Rebuild + Cache Clear
|
||||
|
||||
```bash
|
||||
python3 custom/scripts/validate_and_rebuild.py
|
||||
```
|
||||
|
||||
**Schritt 4:** Verifizieren
|
||||
|
||||
```bash
|
||||
# Prüfe ob Fehler weg sind
|
||||
docker exec espocrm bash -c 'tail -n 50 /var/www/html/data/logs/espo-$(date +%Y-%m-%d).log | grep -i "InjectableFactory"'
|
||||
|
||||
# Test API-Zugriff
|
||||
docker exec espocrm bash -c 'curl -s -X GET "http://localhost/api/v1/{EntityName}" -u "admin:admin"'
|
||||
```
|
||||
|
||||
**Prävention:**
|
||||
1. ✅ **Immer** Service-Klasse bei Entity-Erstellung mit anlegen
|
||||
2. ✅ Nutze `validate_and_rebuild.py` - detektiert fehlende Services
|
||||
3. ✅ Prüfe Logs nach Rebuild auf InjectableFactory-Fehler
|
||||
4. ✅ Teste Entity-Zugriff nach Erstellung
|
||||
|
||||
**Real-World-Beispiel (März 2026):**
|
||||
|
||||
**Problem:**
|
||||
- Entities `CAICollection` und `CAdvowareAkten` nicht aufrufbar
|
||||
- Logs zeigten: `Class 'Espo\Custom\Services\CAICollection' does not exist`
|
||||
|
||||
**Lösung:**
|
||||
```bash
|
||||
# Service-Klassen erstellt
|
||||
cat > custom/Espo/Custom/Services/CAICollection.php << 'EOF'
|
||||
<?php
|
||||
namespace Espo\Custom\Services;
|
||||
use Espo\Services\Record;
|
||||
class CAICollection extends Record {}
|
||||
EOF
|
||||
|
||||
cat > custom/Espo/Custom/Services/CAdvowareAkten.php << 'EOF'
|
||||
<?php
|
||||
namespace Espo\Custom\Services;
|
||||
use Espo\Services\Record;
|
||||
class CAdvowareAkten extends Record {}
|
||||
EOF
|
||||
|
||||
# Rebuild
|
||||
python3 custom/scripts/validate_and_rebuild.py
|
||||
```
|
||||
|
||||
**Ergebnis:** ✅ Beide Entities sofort funktionsfähig, keine Fehler mehr in Logs
|
||||
|
||||
### Formula triggert nicht
|
||||
|
||||
**Checklist:**
|
||||
|
||||
Reference in New Issue
Block a user