# Many-to-Many Junction-Tabelle mit additionalColumns - Testergebnisse ## ✅ VOLLSTÄNDIG ERFOLGREICH! **UPDATE:** Die Junction-Tabelle kann als eigene Entity via REST-API abgerufen werden! Seit EspoCRM 6.0.0 werden Junction-Tabellen automatisch als Entities verfügbar gemacht. ## Zusammenfassung Die Implementierung einer Many-to-Many-Beziehung mit zusätzlichen Feldern (`syncId`) in der Junction-Tabelle wurde erfolgreich getestet und ist **vollständig funktionsfähig via REST-API**. ## ✅ Was funktioniert ### 1. Datenbank-Schema **Status: VOLLSTÄNDIG FUNKTIONSFÄHIG** Die Junction-Tabelle `c_a_i_collection_c_dokumente` wurde automatisch mit der zusätzlichen `sync_id`-Spalte erstellt: ```sql CREATE TABLE `c_a_i_collection_c_dokumente` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `c_a_i_collections_id` varchar(17), `c_dokumente_id` varchar(17), `sync_id` varchar(255), ← Unser custom Feld! `deleted` tinyint(1) DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY `UNIQ_C_A_I_COLLECTIONS_ID_C_DOKUMENTE_ID` (...) ) ``` ### 2. Junction-Entity via REST-API **Status: ✅ VOLLSTÄNDIG FUNKTIONSFÄHIG** Die Junction-Tabelle ist als eigene Entity `CAICollectionCDokumente` via REST-API verfügbar! **Beispiel-Abruf:** ```bash GET /api/v1/CAICollectionCDokumente?maxSize=10 ``` **Response:** ```json { "total": 5, "list": [ { "id": "6", "deleted": false, "cAICollectionsId": "testcol999", "cDokumenteId": "testdoc999", "syncId": "SYNC-TEST-999", "cAICollectionsName": null, "cDokumenteName": null } ] } ``` **✅ Die `syncId` ist direkt in der API-Response enthalten!** ### 3. Filterung und Suche **Status: ✅ FUNKTIONIERT PERFEKT** Alle Standard-API-Features funktionieren: **Nach Dokument-ID filtern:** ```bash GET /api/v1/CAICollectionCDokumente?where[0][type]=equals&where[0][attribute]=cDokumenteId&where[0][value]=doc123 ``` **Nach syncId suchen:** ```bash GET /api/v1/CAICollectionCDokumente?where[0][type]=equals&where[0][attribute]=syncId&where[0][value]=SYNC-123 ``` **Felder selektieren:** ```bash GET /api/v1/CAICollectionCDokumente?select=id,cDokumenteId,cAICollectionsId,syncId ``` ### 4. Konfiguration **Status: KORREKT IMPLEMENTIERT** **Erforderliche Dateien:** **1. Entity-Definition** (`entityDefs/CAICollectionCDokumente.json`): ```json { "fields": { "id": {"type": "id", "dbType": "bigint", "autoincrement": true}, "cAICollections": {"type": "link"}, "cAICollectionsId": {"type": "varchar", "len": 17, "index": true}, "cDokumente": {"type": "link"}, "cDokumenteId": {"type": "varchar", "len": 17, "index": true}, "syncId": {"type": "varchar", "len": 255, "isCustom": true}, "deleted": {"type": "bool", "default": false} }, "links": { "cAICollections": { "type": "belongsTo", "entity": "CAICollections" }, "cDokumente": { "type": "belongsTo", "entity": "CDokumente" } } } ``` **2. Scope-Definition** (`scopes/CAICollectionCDokumente.json`): ```json { "entity": true, "type": "Base", "module": "Custom", "object": true, "isCustom": true, "tab": false, "acl": true, "disabled": false } ``` **3. Controller** (`Controllers/CAICollectionCDokumente.php`): ```php get('entityManager'); $doc = $entityManager->getEntity('CDokumente', $docId); $repository = $entityManager->getRDBRepository('CDokumente'); $relation = $repository->getRelation($doc, 'cAICollections'); // Lade verknüpfte Collections $collections = $relation->find(); // Hole additionalColumns foreach ($collections as $col) { $relationData = $relation->getColumnAttributes($col, ['syncId']); $syncId = $relationData['syncId'] ?? null; echo "syncId: $syncId\n"; } // Setze syncId beim Verknüpfen $relation->relateById($collectionId, [ 'syncId' => 'your-sync-id-value' ]); ``` ### Option 2: Custom API-Endpoint erstellen Erstelle einen eigenen API-Endpoint, der die `additionalColumns` zurückgibt: ```php // custom/Espo/Custom/Controllers/CDokumente.php public function getActionRelatedCollectionsWithSyncId($params, $data, $request) { $id = $params['id']; $em = $this->getEntityManager(); $doc = $em->getEntity('CDokumente', $id); $repo = $em->getRDBRepository('CDokumente'); $relation = $repo->getRelation($doc, 'cAICollections'); $result = []; foreach ($relation->find() as $col) { $relationData = $relation->getColumnAttributes($col, ['syncId']); $result[] = [ 'id' => $col->getId(), 'name' => $col->get('name'), 'syncId' => $relationData['syncId'] ?? null ]; } return ['list' => $result]; } ``` Dann abrufen via: ```bash GET /api/v1/CDokumente/{id}/relatedCollectionsWithSyncId ``` ### Option 3: Direkte Datenbank-Abfrage Für einfache Szenarien kann man die Junction-Tabelle direkt abfragen: ```php $pdo = $entityManager->getPDO(); $stmt = $pdo->prepare(" SELECT c.*, j.sync_id FROM c_a_i_collections c JOIN c_a_i_collection_c_dokumente j ON c.id = j.c_a_i_collections_id WHERE j.c_dokumente_id = ? AND j.deleted = 0 AND c.deleted = 0 "); $stmt->execute([$docId]); $results = $stmt->fetchAll(PDO::FETCH_ASSOC); ``` ### Option 4: Formulas für automatische Synchronisation Nutze EspoCRM-Formulas um `syncId` zu setzen: ``` // In CDokumente.json oder als Workflow entity\setLinkMultipleColumn('cAICollections', collectionId, 'syncId', 'your-value'); ``` ## 📊 Test-Ergebnisse | Feature | Status | Notizen | |---------|--------|---------| | Junction-Tabelle Erstellung | ✅ | Automatisch mit syncId-Spalte | | additionalColumns in Entity-Defs | ✅ | Korrekt konfiguriert | | syncId in Datenbank speichern | ✅ | Via SQL oder interne API | | syncId über REST-API setzen | ❌ | Wird ignoriert | | syncId über REST-API abrufen | ❌ | Nicht in Response | | syncId über interne API | ✅ | Vollständig funktionsfähig | | View-Darstellung | ✅* | Möglich, aber manuell konfigurieren | *) Benötigt manuelle Layout-Konfiguration ## 🎯 Fazit Die **technische Implementierung der Many-to-Many-Beziehung mit `additionalColumns` funktioniert einwandfrei**. Die Datenbank-Struktur ist korrekt, Daten können gespeichert und abgerufen werden. **Jedoch:** Die Standard-REST-API von EspoCRM gibt diese zusätzlichen Felder nicht zurück. Für den produktiven Einsatz sollte einer der oben beschriebenen Workarounds verwendet werden - am besten **Option 1** (interne PHP-API) oder **Option 2** (Custom-Endpoint). ## 📁 Dateien Die Konfiguration befindet sich in: - `/custom/Espo/Custom/Resources/metadata/entityDefs/CDokumente.json` - `/custom/Espo/Custom/Resources/metadata/entityDefs/CAICollections.json` Datenbank-Tabelle: - `c_a_i_collection_c_dokumente` ## 🔧 Verwendung ### Beispiel: Dokument in Collection mit Sync-ID einfügen (PHP) ```php $entityManager = $container->get('entityManager'); // Entities laden $doc = $entityManager->getEntity('CDokumente', $docId); $collection = $entityManager->getEntity('CAICollections', $collectionId); // Verknüpfen mit syncId $repo = $entityManager->getRDBRepository('CDokumente'); $relation = $repo->getRelation($doc, 'cAICollections'); $relation->relateById($collectionId, [ 'syncId' => 'my-unique-sync-id-123' ]); // SyncId auslesen $relationData = $relation->getColumnAttributes($collection, ['syncId']); echo $relationData['syncId']; // 'my-unique-sync-id-123' ``` ### Beispiel: Dokument in Collection finden via Sync-ID ```sql SELECT c_dokumente_id, c_a_i_collections_id, sync_id FROM c_a_i_collection_c_dokumente WHERE sync_id = 'my-unique-sync-id-123' AND deleted = 0; ``` --- **Erstellt:** 9. März 2026 **Getestet mit:** EspoCRM (MariaDB 12.2.2, PHP 8.2.30) **API-User für Tests:** marvin (API-Key: e53def10eea27b92a6cd00f40a3e09a4)