forms — Datensätze per Skript anlegen, lesen, ändern, löschen¶
Das forms-Binding gibt Skripten programmatischen Zugriff auf Formular-Datensätze:
anlegen, lesen, aktualisieren, löschen und iterieren. Typischer Einsatz: eingehende
Daten aus einem REST-Service, einem
WebService oder dem
Dateisystem in ein Formular schreiben — oder in einem
Job Datensätze reihenweise verarbeiten.
forms ist in allen Skript-Kontexten verfügbar (Form-Hooks, REST-Services,
Scheduler-Jobs, Skript-Konsole).
Roh, aber konsistent
forms.create/update nutzen denselben Persistenz-Kern wie ein UI-Submit
(Spalten-Zuordnung, Typ-Coercion, kc:maxLength, form_version-Stempel,
Detail-Tabellen). Sie führen aber keine Formular-Validierung und keine
Lifecycle-Hooks (onValidate/onBeforeSave/onSubmit/…) aus. Das ist
Absicht: schnell und vorhersehbar für Import/Automatisierung — und ohne
Rekursionsgefahr, falls forms.* selbst aus einem Hook läuft. Wer Prüfungen
braucht, baut sie vor dem create/update selbst ein.
Überblick¶
| Aufruf | Ergebnis |
|---|---|
forms.create(formId, values [, opts]) |
Legt einen Datensatz an → neue id (Zahl) |
forms.get(formId, id [, opts]) |
Datensatz als Objekt {id, …Felder…, …Detail-Listen…} oder null |
forms.list(formId [, opts]) |
Array [{id, …Felder…}, …] (ohne Detail-Listen) |
forms.forEach(formId, [opts,] callback) |
Ruft callback(record) je Datensatz |
forms.update(formId, id, values [, opts]) |
Teil-Update (nur übergebene Felder) → betroffene Zeilen (0/1) |
forms.delete(formId, id [, opts]) |
Löscht + räumt Anhänge/Änderungsprotokoll auf → betroffene Zeilen |
forms.exists(formId, id) |
true / false |
forms.validate(formId, values) |
Nur prüfen (nicht schreiben) → Array [{field, message}] (leer = gültig) |
Anlegen — auch mit Detail-Tabellen¶
// z.B. im REST-Handler: eingehende Bestellung in ein Formular schreiben
var id = forms.create('bestellung', {
kunde: body.kunde,
betrag: body.betrag,
positionen: body.positionen // kc:childTable-Feld → Detail-Zeilen (Array von Objekten)
});
return { ok: true, id: id };
Detail-Zeilen (kc:childTable-Felder) werden automatisch mitgeschrieben, wenn der
Wert eine Liste von Objekten ist — im selben verschachtelten Format wie ctx.form.
Prüfen vor dem Import — Problemdatensätze abweisen¶
Damit fehlerhafte Eingangsdaten nicht importiert werden, sondern die Fehlermeldung an den aufrufenden Service zurückgeht:
// REST-Handler: erst prüfen, dann anlegen
var errs = forms.validate('bestellung', body); // [] = gültig
if (errs.length) {
return { ok: false, status: 422, errors: errs }; // an den Service zurückmelden
}
var id = forms.create('bestellung', body);
return { ok: true, id: id };
forms.validate(formId, values) liefert ein Array von {field, message[, severity]}
(leer = gültig). Alternativ bricht opts.validate: true ein create/update
bei Fehlern ab und wirft eine sprechende Meldung — praktisch mit try/catch:
try {
forms.create('bestellung', body, { validate: true });
} catch (e) {
return { ok: false, error: String(e.message) }; // "forms: Validierung fehlgeschlagen — …"
}
In beiden Fällen wird bei Fehlern nichts geschrieben.
Was geprüft wird
validate prüft auf Schema-Ebene: Pflichtfelder, Typ
(Zahl/Datum), kc:maxLength, Auswahl (enum) und Validierungsmuster. Bei
update wird gegen den zusammengeführten Datensatz geprüft (bereits
gespeicherte Pflichtfelder gelten nicht als leer). Formular-Hooks
(onValidate) und kc:validator-Feldvalidatoren laufen dabei nicht —
solche Geschäftsregeln baut der Import-Skriptautor bei Bedarf selbst ein.
Lesen¶
var rec = forms.get('bestellung', 42);
// rec = { id: 42, kunde: 'ACME', betrag: 900, positionen: [ {…}, {…} ] } | null
get liefert ein serialisierbares Objekt (JSON.stringify, Rückgabe aus
REST-Handlern und Feldzugriff funktionieren) inklusive der Detail-Listen.
list lässt Detail-Listen aus Performance-Gründen weg — für sie get(id) je Satz.
// Alle Datensätze durchgehen
forms.forEach('bestellung', function (rec) {
if (rec.betrag > 1000) log.info('Groß: #' + rec.id);
});
Aktualisieren (Teil-Update)¶
Es werden nur die übergebenen Felder geschrieben — nicht genannte Spalten
bleiben unangetastet. Enthält values Detail-Listen, werden diese ersetzt.
Löschen¶
Räumt zusätzlich Dateianlagen und das Änderungsprotokoll dieses Satzes weg; Detail-Zeilen verschwinden über die Fremdschlüssel-Kaskade. Keine Datenbankleichen.
Optionen (opts)¶
Letztes Argument von create/get/list/update/delete:
| Option | Default | Wirkung |
|---|---|---|
owner |
ctx.user |
Eigentümer (setzt createdBy und idUser) |
createdBy / idUser |
aus ctx.user |
Eigentümer-Felder einzeln setzen |
scope |
'all' |
'all' = alle Datensätze sichtbar (System-Bypass); 'mine' = nur eigene (id_user-Filter) bei get/list/update/delete |
children |
true |
Detail-Listen mitschreiben (create/update) |
validate |
false |
vor create/update auf Schema-Ebene prüfen; bricht bei Fehlern ab |
// Import ohne konkreten Benutzer, als System-Eigentümer:
forms.create('bestellung', values, { owner: 'import-job' });
// nur eigene Datensätze löschen:
forms.delete('bestellung', 42, { scope: 'mine' });
Standardmäßig gehört ein neuer Datensatz ctx.user (in Jobs "scheduler", in
REST dem Remote-User), und get/update/delete/list sehen alle
Datensätze (System-Bypass). scope:'mine' schränkt auf die eigenen ein.
Grenzen¶
- Schema-Validierung ja, Hooks nein.
forms.validate/opts.validateprüfen auf Schema-Ebene (s.o.). Die vollen Lifecycle-Hooks (opts.runHooks— onValidate/onBeforeSave/…) sind noch nicht umgesetzt (Rekursionsgefahr) und werfen einen Fehler, wenn gesetzt. - Memory-Formulare (ohne Tabelle) werden abgelehnt.
- Detail-Tabellen: der
kc:childTable-Pfad wird unterstützt. - Zahlenfelder kommen als Zahl zurück;
DECIMAL-Felder je nach Dezimalstellen als Zahl (identisch zur übrigen KCForms-Dekodierung).
Schulungs-Beispiel (zwei Formulare)¶
Für Schulungen gibt es ein Beispielpaar „Schulung: forms-API" — installierbar unter System-Administration → Formular-Bausteine → Beispiele, danach im Katalog „Demo", Ordner „Schulung":
- „Aufträge (Datenquelle)" (
schulung-auftraege) — ein normales Erfassungsformular (Auftragsnummer + Kunde sind Pflicht). Das ist das Ziel der Operationen; man befüllt es von Hand oder per Skript. - „Skript-Werkbank (forms-API)" (
schulung-werkbank) — je ein Button pro Operation. Jeder Button löst einen Server-Hook aus, der die zugehörigeforms.*-Funktion gegen die Datenquelle ausführt und das Ergebnis in einem Dialog (view.add) zeigt; ein Kurztext landet zusätzlich im Feld „Ergebnis".
Trainer-Ablauf: Felder ausfüllen → Anlegen (create), die neue id landet in
„Datensatz-id" → Alle laden (list), Öffnen (get), Status setzen
(update), Löschen (delete). Für die Validierung: Pflichtfeld leer lassen
→ Anlegen mit Prüfung / Nur prüfen zeigt den Fehler, ohne zu speichern.
Die Hook-Skripte (demoCreate, demoValidate, … im Reiter „Skripte" der
Werkbank) sind ausführlich kommentiert und dienen als Vorlage.
So zeigt ein Button-Hook sein Ergebnis
Ein kc:button mit scriptHook: "meinHook" ruft den Hook meinHook auf dem
Server auf. Sein Ergebnis wird immer in die Maske zurückgespiegelt (kein
kc:reflectServerValues nötig) — kurze Werte also einfach per
ctx.form.<feld> = … setzen. Für mehrzeilige/JSON-Ausgabe einen Dialog:
view.add(new view.Message('html:<pre>' + JSON.stringify(r, null, 2) + '</pre>', 'Titel', { modal:true, okButton:true })).
Der Hook muss im Reiter Skripte des Formulars angelegt sein — andere
Namen lehnt der Server ab.
Abgrenzung zu db¶
Das db-Binding schreibt rohes SQL gegen die per-Form-Tabelle —
ohne Spalten-/Typ-Auflösung, ohne Detail-Verteilung, ohne form_version und mit
hartcodiertem Tabellennamen. forms.* nimmt dir all das ab und ist der
empfohlene Weg für Datensatz-CRUD. db bleibt für freie Abfragen/Joins.