Skip to content

Tabellen- und Feld-Benennung standardisieren (inkl. Backend-Mappings)

Problembeschreibung

Hinweis: Mehrere Aufgaben zum Entfernen ungenutzter Tabellen und Spalten aus der Datenbank laufen bereits. SMS-/Benachrichtigungsfunktionen werden in diesen Issues bereinigt.

Das aktuelle Datenbankschema leidet unter uneinheitlichen Benennungskonventionen, die Wartung erschweren und die Codeklarheit verringern.

„Das folgende stammt aus Projekterfahrung und ist kein bloßer Vorschlag von KI. Eine Bereinigung würde die langfristigen Technologie-Schulden deutlich reduzieren. In den ersten 6–9 Monaten fiel es mir schwer, all das ungefähr im Kopf zu mappen.“ @ThomasAFink

Hier schreiben.

Hier schreiben.

Hier schreiben.

Investition: Schema standardisieren und Abfragen aktualisieren
Rendite:
- Schnelleres Onboarding neuer Entwickler:innen (Monate pro Person)
- Weniger Debug-Zeit (Stunden pro Issue)
- Höhere Entwicklungsgeschwindigkeit (Zeitersparnis bei jedem Backend-Feature)
- Weniger Fehlereinbau (vermeidet kostspielige Produktionsprobleme und Hotfixes)
- Schnelleres Refactoring weiterer Teile später
- Einfachere Test-Erstellung


Die Themen umfassen:

1. Sprachmischung (Deutsch ↔ Englisch)

  • Tabellennamen: oeffnungszeit (Deutsch) vs. availability (englisches Konzept)
  • Tabellennamen: standort (Deutsch) vs. scope (englisches Konzept)
  • Tabellennamen: buerger (Deutsch) vs. citizen (englisches Konzept)
  • Tabellennamen: feiertage (Deutsch) vs. holidays (englisches Konzept)

2. Inkonsistente Benennungskonventionen

  • Mischung aus camelCase und snake_case: StandortID vs. scope_id
  • Gemischte Konventionen in derselben Tabelle: contact__name vs. contact__email vs. StandortID

3. Konzeptionelle Inkonsistenzen

  • Dasselbe Konzept mit unterschiedlichen Namen: availability/oeffnungszeit, scope/standort
  • Query-Klassen nutzen englische Namen, mappen aber auf deutsche Tabellennamen

Aktuelle Beispiele

Availability-Query-Klasse:

php
const TABLE = 'oeffnungszeit';  // German table name
// But maps to English field names:
'id' => 'availability.OeffnungszeitID',
'scope__id' => 'availability.StandortID',

Scope-Query-Klasse:

php
const TABLE = 'standort';  // German table name
// Maps to mixed naming:
'id' => 'scope.StandortID',
'contact__name' => 'scopeprovider.name',

Vorgeschlagene Lösung

Standardisierungsregeln:

  1. Sprache: Alle Tabellen- und Spaltennamen auf Englisch
  2. Datenbankkonvention: snake_case für alle Tabellen- und Spaltennamen
  3. Mapping-Konvention: camelCase für alle Mapping-Variablen in Query-Klassen
  4. Konsistenz: Konzeptnamen im gesamten Schema angleichen

Migrationsplan:

  1. Phase 1: Umbenennung von Tabellen

    • oeffnungszeitavailability
    • standortscope
    • buergercitizen
    • feiertageholidays
    • gesamtkalendercalendar
  2. Phase 2: Spalten-Standardisierung

    • Alle Spaltennamen nach snake_case
    • Fremdschlüssel-Namen vereinheitlichen: StandortIDscope_id
    • Feldnamen angleichen: OeffnungszeitIDavailability_id
  3. Phase 3: Aktualisierung der Query-Klassen

    • Alle Zmsdb/Query/*-Klassen auf neue Tabellen-/Spaltennamen
    • Mapping-Variablen in camelCase: scopeIdavailability.scope_id
    • Entity-Mapping-Methoden aktualisieren

Erwartete Ergebnisse

  • Wartbarkeit: Einheitliche Namen reduzieren kognitive Last
  • Klarheit: Englische Namen verbessern internationale Zusammenarbeit
  • Angleichung: Query-Klassen entsprechen dem tatsächlichen Schema
  • Zukunftssicherheit: Standardkonventionen für neue Entwicklung
  • Testbarkeit: Tests sind mit konsistenter Benennung einfacher zu schreiben und zu pflegen

Hinweise zur Umsetzung

  • Umfassende Migrationsskripte pro Tabelle erstellen
  • Alle betroffenen Query-Klassen in zmsdb/src/Zmsdb/Query/ aktualisieren
  • Abwärtskompatibilität während der Übergangsphase sicherstellen
  • Dokumentation und API-Referenzen aktualisieren

Beispiel nach Standardisierung

Availability-Query-Klasse (nachher):

php
const TABLE = 'availability';  // English snake_case table name
// Maps to camelCase variables:
'id' => 'availability.availability_id',
'scopeId' => 'availability.scope_id',
'startDate' => 'availability.start_date',

Scope-Query-Klasse (nachher):

php
const TABLE = 'scope';  // English snake_case table name
// Maps to camelCase variables:
'id' => 'scope.scope_id',
'contactName' => 'scopeprovider.name',

Damit gilt:

  • Datenbank: Alles snake_case (übliche SQL-Konvention)
  • Query-Mappings: Alles camelCase (übliche PHP-Konvention)
  • Konsistenz: Keine Ausnahmen, einheitliches Muster

Vier Phasen:

  1. Vollständiger Plan zur Tabellenumbenennung – alles Englisch, alles snake_case einfach
  • 47 Tabellen von Deutsch nach Englisch
  • Alle Tabellennamen auf snake_case vereinheitlicht
  • Nach Geschäftspriorität sortiert (Kern → Benutzer → System → Technik)
  • Klarer Migrationspfad mit Beispielen
  1. Vollständiger Plan zur Spaltenumbenennung – alles Englisch, alles snake_case einfach–mittel
  • Hunderte Spalten von Deutsch nach Englisch
  • Alle Spaltennamen auf snake_case
  • Fremdschlüssel-Namen tabellenübergreifend vereinheitlicht
  • Gemeinsame Muster identifiziert und standardisiert
  1. Vollständiger Plan zur Umbenennung der PHP-Variablen-Mappings – alles Englisch, alles camelCase schwer
  • Alle Query-Klassen-Mappings nach camelCase
  • Doppel-Unterstrich-Muster entfernt
  • Verschachtelte Objektmuster standardisiert
  • Referenz-Mappings aktualisiert
  1. Langfristige Schema-Vision (über Umbenennung hinaus) strategisch
  • Strukturelle Aufteilungen (buerger, queue_number_statistics, preferences, DLDB-data-Spalten)
  • Tabellen-Disposition (löschen, prüfen, neu gestalten)
  • Migrations-Benennung und Asset-Speicherung (S3 vs. image_data)

Die Abschnitte 1–3 fokussieren Namensvereinheitlichung. Abschnitt 4 hält größere Architekturänderungen fest, die parallel oder danach umgesetzt werden können.

Die Spaltenzuordnungen in Abschnitt 2 decken alle Tabellen im ZMS-Schema ab (siehe .resources/zms.sql). Die PHP-Variablen-Mappings in Abschnitt 3 werden weiterhin tabellenweise ergänzt.

1. Vollständiger Plan zur Tabellenumbenennung – alles Englisch, alles snake_case

Phase 1: Kerngeschäfts-Tabellen (hohe Priorität)

CurrentNew (snake_case)Reason
oeffnungszeitavailabilityOpening hours/availability
standortscopeLocation/scope
buergercitizenCitizen
feiertageholidaysHolidays
gesamtkalendercalendarCalendar
behoerdedepartmentGovernment department
organisationorganizationOrganization

Phase 2: Benutzer- & Prozess-Tabellen

CurrentNew (snake_case)Reason
buergeranliegencitizen_requestsCitizen requests/issues
buergerarchivcitizen_archiveArchived citizen data
buergerarchivtoday— (delete)Redundanter Snapshot; in citizen_archive auflösen (Abschnitt 4)
nutzeruserSystem users
nutzerzuordnunguser_assignmentUser assignments
kundejurisdictionOwner/jurisdiction (Entity/API today: owner)
kundenlinks— (delete)Unused; drop table and related code

Hinweis: Die Tabelle kunde und die Entity/API owner werden zu jurisdiction umbenannt (nicht customer). Die Permission jurisdiction (ZMSKVR-1345) führt diese Benennung im Berechtigungsmodell bereits ein; die Datenbank-Umbenennung folgt in diesem Refactor.

Hinweis: Die Tabelle kundenlinks (Favoriten-Links) wird nicht mehr genutzt. Geplant sind Löschung der Tabelle sowie des zugehörigen Codes (z. B. Link-Entity, DB-Query, Admin-UI „Favoriten“).

Phase 3: System- & Konfigurations-Tabellen

CurrentNew (snake_case)Reason
abrechnung— (delete)Ungenutzt; Tabelle löschen (Abschnitt 4)
ipausnahmenip_exceptionsIP-Ausnahmen; Nutzung prüfen (Abschnitt 4)
kioskkioskKiosk (universal term)
wartenrstatistikqueue_number_statisticsWartestatistik; in kleinere Tabellen normalisieren (Abschnitt 4)
standortclusterlocation_clusterLocation clustering
statistikstatisticsStatistics
roleroleRBAC roles (already snake_case)
permissionpermissionRBAC permissions (already snake_case)
role_permissionrole_permissionRole–permission mapping (already snake_case)
user_roleuser_roleUser–role mapping (already snake_case)

Phase 4: API- & technische Tabellen

CurrentNew (snake_case)Reason
apiclientapi_clientAPI client
apikeyapi_keyAPI-Key; Produktivnutzung prüfen (Abschnitt 4)
apiquotaapi_quotaAPI-Quota; Produktivnutzung prüfen (Abschnitt 4)

Phase 5: Kommunikations-Tabellen

CurrentNew (snake_case)Reason
emailemailEmail (already snake_case)
smssmsSMS (already snake_case)
mailpartmail_partMail part
mailqueuemail_queueMail queue
mailtemplatemail_templateMail template
notificationqueue— (delete)Nutzung prüfen; Tabelle löschen (Abschnitt 4)

Phase 6: Daten- & Prozess-Tabellen

CurrentNew (snake_case)Reason
closuresclosuresAlready snake_case
configconfigAlready snake_case
eventlogevent_logEvent-Log; Nutzungsumfang prüfen (Abschnitt 4)
imagedataimage_dataBilddaten; Assets nach S3 (Abschnitt 4)
loglogdata-JSON für Suche aufteilen (Abschnitt 4)
migrationsmigrationsAlready snake_case
preferencesscope_preferences / splitScope- und Systemeinstellungen; umbenennen und aufteilen (Abschnitt 4)
process_sequenceprocess_sequenceAlready snake_case
sessiondatasession_dataSession data
sourcesourceAlready snake_case
overview_calendaroverview_calendarOverview calendar (already snake_case)

Phase 7: Leistungs- & Anbieter-Tabellen

CurrentNew (snake_case)Reason
provideroffice (Kandidat)DLDB-Standort; an zmscitizenapi office anlehnen (Abschnitt 4)
requestservice (Kandidat)DLDB-Dienstleistung; an zmscitizenapi service anlehnen (Abschnitt 4)
request_provideroffice_service (Kandidat)Standort–Leistung-Verknüpfung; data-JSON aufteilen (Abschnitt 4)
request_variantservice_variant (Kandidat)Leistungsvariante; Benennung mit Citizen API abstimmen (Abschnitt 4)

Phase 8: Slot-System-Tabellen

CurrentNew (snake_case)Reason
slotslotAlready snake_case
slot_hieraslot_hierarchySlot hierarchy
slot_processslot_processAlready snake_case
slot_sequenceslot_sequenceAlready snake_case

Phase 9: Zuweisungs- & Clustering-Tabellen

CurrentNew (snake_case)Reason
clusterzuordnungcluster_assignmentCluster assignment

2. Vollständiger Plan zur Spaltenumbenennung – alles Englisch, alles snake_case

Phase 1: Kerngeschäfts-Tabellen (hohe Priorität)

availability (formerly oeffnungszeit)

Aktuelle SpalteNeue Spalte (snake_case)Grund
OeffnungszeitIDavailability_idPrimär-/Fremdschlüssel
StandortIDscope_idPrimär-/Fremdschlüssel
Startdatumstart_dateNamensstandardisierung
Endedatumend_dateNamensstandardisierung
allexWochenevery_x_weeksNamensstandardisierung
jedexteWocheevery_other_weekNamensstandardisierung
WochentagweekdayNamensstandardisierung
Anfangszeitstart_timeNamensstandardisierung
Terminanfangszeitappointment_start_timeNamensstandardisierung
Endzeitend_timeNamensstandardisierung
Terminendzeitappointment_end_timeNamensstandardisierung
Timeslottime_slotNamensstandardisierung
Anzahlarbeitsplaetzeworkstation_countNamensstandardisierung
Anzahlterminarbeitsplaetzeappointment_workstation_countNamensstandardisierung
kommentarcommentNamensstandardisierung
reduktionTermineImInternetinternet_reductionNamensstandardisierung
erlaubemehrfachslotsmultiple_slots_allowedNamensstandardisierung
reduktionTermineCallcentercallcenter_reductionNamensstandardisierung
Offen_abopen_from_daysNamensstandardisierung
Offen_bisopen_until_daysNamensstandardisierung
updateZeitstempelupdated_atZeitstempel

scope (formerly standort)

Aktuelle SpalteNeue Spalte (snake_case)Grund
StandortIDscope_idPrimär-/Fremdschlüssel
BehoerdenIDdepartment_idPrimär-/Fremdschlüssel
InfoDienstleisterIDinfo_provider_idPrimär-/Fremdschlüssel
HinweishintNamensstandardisierung
BezeichnungnameNamensstandardisierung
AdresseaddressNamensstandardisierung
Stadtplanlinkcity_map_linkNamensstandardisierung
Bearbeitungszeitprocessing_timeNamensstandardisierung
KennungidentifierNamensstandardisierung
Termine_abappointments_from_daysNamensstandardisierung
Termine_bisappointments_until_daysNamensstandardisierung
smswarteschlangesms_queueNamensstandardisierung
smswmsbestaetigungsms_wms_confirmationNamensstandardisierung
smsbenachrichtigungsfristsms_notification_deadlineNamensstandardisierung
smsbenachrichtigungstextsms_notification_textNamensstandardisierung
smsbestaetigungstextsms_confirmation_textNamensstandardisierung
wartenrsperrequeue_number_lockedNamensstandardisierung
wartenrhinweisqueue_hintNamensstandardisierung
notruffunktionemergency_functionNamensstandardisierung
notrufausgeloestemergency_triggeredNamensstandardisierung
notrufinitiierungemergency_initiationNamensstandardisierung
notrufantwortemergency_responseNamensstandardisierung
emailPflichtfeldemail_requiredNamensstandardisierung
anmerkungPflichtfeldcomment_requiredNamensstandardisierung
anmerkungLabelcomment_labelNamensstandardisierung
telefonPflichtfeldphone_requiredNamensstandardisierung
standortinfozeilelocation_info_lineNamensstandardisierung
standortkuerzelshort_nameNamensstandardisierung
aufrufanzeigetextdisplay_textNamensstandardisierung
reservierungsdauerreservation_durationNamensstandardisierung
anzahlwiederaufrufrecall_countNamensstandardisierung
startwartenrfirst_queue_numberNamensstandardisierung
endwartenrlast_queue_number_limitNamensstandardisierung
letztewartenrlast_queue_numberNamensstandardisierung
wartenrdatumqueue_number_dateNamensstandardisierung
mehrfachterminemultiple_appointmentsNamensstandardisierung
schreibschutzwrite_protectionNamensstandardisierung
ohnestatistikwithout_statisticsNamensstandardisierung
smskioskangebotsfristsms_kiosk_offer_deadlineNamensstandardisierung
emailstandortadminadmin_emailNamensstandardisierung
wartenummernkontingentqueue_number_contingentNamensstandardisierung
vergebenewartenummernassigned_queue_numbersNamensstandardisierung
kundenbefragungcustomer_surveyNamensstandardisierung
kundenbef_labelcustomer_survey_labelNamensstandardisierung
kundenbef_emailtextcustomer_survey_email_textNamensstandardisierung
telefonaktiviertphone_enabledNamensstandardisierung
virtuellesachbearbeiterzahlvirtual_processor_countNamensstandardisierung
datumvirtuellesachbearbeiterzahlvirtual_processor_count_dateNamensstandardisierung
smsnachtragsms_additionNamensstandardisierung
loeschdauerdeletion_durationNamensstandardisierung
updateZeitstempelupdated_atZeitstempel
sourcesourceBereits snake_case
custom_text_field_labelcustom_text_field_labelBereits snake_case
custom_text_field_activecustom_text_field_activeBereits snake_case
custom_text_field_requiredcustom_text_field_requiredBereits snake_case
admin_mail_on_appointmentadmin_mail_on_appointmentBereits snake_case
admin_mail_on_deletedadmin_mail_on_deletedBereits snake_case
admin_mail_on_updatedadmin_mail_on_updatedBereits snake_case
admin_mail_on_mail_sentadmin_mail_on_mail_sentBereits snake_case
appointments_per_mailappointments_per_mailBereits snake_case
whitelisted_mailswhitelisted_mailsBereits snake_case
slots_per_appointmentslots_per_appointmentBereits snake_case
info_for_appointmentinfo_for_appointmentBereits snake_case
aktivierungsdaueractivation_durationNamensstandardisierung
captcha_activated_requiredcaptcha_activated_requiredBereits snake_case
email_confirmation_activatedemail_confirmation_activatedBereits snake_case
custom_text_field2_labelcustom_text_field2_labelBereits snake_case
custom_text_field2_activecustom_text_field2_activeBereits snake_case
custom_text_field2_requiredcustom_text_field2_requiredBereits snake_case
info_for_all_appointmentsinfo_for_all_appointmentsBereits snake_case
last_display_numberlast_display_numberBereits snake_case
max_display_numbermax_display_numberBereits snake_case
display_number_prefixdisplay_number_prefixBereits snake_case

process (formerly buerger)

Aktuelle SpalteNeue Spalte (snake_case)Grund
BuergerIDprocess_idPrimär-/Fremdschlüssel
StandortIDscope_idPrimär-/Fremdschlüssel
DatumdateNamensstandardisierung
UhrzeittimeNamensstandardisierung
NamenameNamensstandardisierung
AnmerkungcommentNamensstandardisierung
TelefonnummerphoneNamensstandardisierung
EMailemailNamensstandardisierung
EMailverschicktemail_sent_countNamensstandardisierung
Erinnerungszeitpunktreminder_timestampZeitstempel
SMSverschicktsms_sent_countNamensstandardisierung
AnzahlAufrufecall_countNamensstandardisierung
ZeitstempeltimestampZeitstempel
IPAdresseip_addressNamensstandardisierung
IPTimeStampip_timestampZeitstempel
NutzerIDuser_idPrimär-/Fremdschlüssel
aufruferfolgreichcall_successfulNamensstandardisierung
wsm_aufnahmezeitticket_printer_capture_timeNamensstandardisierung
aufrufzeitcall_timeNamensstandardisierung
nicht_erschienendid_not_appearNamensstandardisierung
Abholerpickup_personNamensstandardisierung
AbholortIDpickup_scope_idPrimär-/Fremdschlüssel
wartenummerqueue_numberNamensstandardisierung
vorlaeufigeBuchungprovisional_bookingNamensstandardisierung
hatFolgeterminefollow_up_appointment_countNamensstandardisierung
istFolgeterminvonfollow_up_of_process_idPrimär-/Fremdschlüssel
zustimmung_kundenbefragungsurvey_acceptedNamensstandardisierung
telefonnummer_fuer_rueckfragencallback_phoneNamensstandardisierung
absagecodeauth_keyNamensstandardisierung
AnzahlPersonenperson_countNamensstandardisierung
updateZeitstempelupdated_atZeitstempel
apiClientIDapi_client_idPrimär-/Fremdschlüssel
custom_text_fieldcustom_text_fieldBereits snake_case
showUpTimeshow_up_timeNamensstandardisierung
finishTimefinish_timeNamensstandardisierung
timeoutTimetimeout_timeNamensstandardisierung
way_timeway_timeBereits snake_case
parkedparkedBereits snake_case
processing_timeprocessing_timeBereits snake_case
bestaetigtconfirmedNamensstandardisierung
waiting_timewaiting_timeBereits snake_case
wasMissedwas_missedNamensstandardisierung
custom_text_field2custom_text_field2Bereits snake_case
statusstatusBereits snake_case
prioritypriorityBereits snake_case
external_user_idexternal_user_idBereits snake_case
displayNumberdisplay_numberNamensstandardisierung
Dereference-Payload in Anmerkung / Custom-Text-Feldern (technische Schuld)

Wenn ein Vorgang abgeschlossen oder soft-gelöscht wird, führt Process::writeBlockedEntity() QUERY_DEREFERENCED aus (zmsdb/src/Zmsdb/Query/Process.php). Dabei werden PII entfernt und StandortID = 0, Name = 'dereferenced' sowie status = 'blocked' gesetzt. Weil die Zeile danach keine nutzbare scope_id mehr hat, werden ursprünglicher Standort und Metadaten in Freitextspalten per PHP var_export() serialisiert:

SpalteGeschrieben vonPayload-Struktur
AnmerkungProcess::toDerefencedAmendment()BuergerID, StandortID, Anmerkung, IPTimeStamp, LastChange
custom_text_fieldProcess::toDerefencedCustomTextfield()gleiches Muster mit CustomTextfield
custom_text_field2Process::toDerefencedCustomTextfield2()gleiches Muster mit CustomTextfield2

Beispiel (Inhalt von Anmerkung nach Dereferenzierung):

array (
  'BuergerID' => 100000,
  'StandortID' => 1,
  'Anmerkung' => NULL,
  'IPTimeStamp' => 0,
  'LastChange' => '1970-01-01T01:00:00+01:00',
)

Wo diese Payload wieder ausgelesen wird (String-Parsing, keine typisierten Spalten):

  • CalculateDailyWaitingStatisticByCron::extractScopeFromAnmerkung() — Regex über alle drei Spalten, wenn StandortID = 0 (zmsdb/src/Zmsdb/Helper/CalculateDailyWaitingStatisticByCron.php)
  • Ad-hoc-SQL in Wartungs-Migrationen (z. B. SUBSTRING_INDEX / LIKE auf 'StandortID' => in Anmerkung und Custom-Text-Feldern)
  • Jeder Code-Pfad, der auf dereferenzierten Shell-Zeilen noch eine Standort-ID braucht, bevor der Cron sie löscht

Warum das schlechte Praxis ist und im neuen Schema nicht fortgeschrieben werden darf:

  • Falsche Spaltensemantik: Anmerkung / Custom-Text-Felder sind nutzersichtbare Kommentare, kein Archiv oder Audit-Store.
  • Fragiles Parsing: Standort und IDs werden per Regex/SUBSTRING_INDEX aus var_export-Text gewonnen; NULL oder überschriebene StandortID in der Zeichenkette bricht Folge-Jobs (z. B. Archive-Cron mit NULL in buergerarchivtoday.StandortID).
  • Dreifach redundante Payload: dieselbe Array-Struktur in drei unabhängigen Spalten.
  • Keine Schema-Integrität: Nichts verhindert Updates nach dem Finish, die den String zerstören oder status zurückdrehen, während StandortID bei 0 bleibt.

Zielrichtung (beim Refactor von process / Archiv):

  • Dereference-Metadaten in typisierten Spalten oder eigener Tabelle (process_dereference / Audit: process_id, scope_id, archived_at, …).
  • Kein var_export-Array mehr in comment / Custom-Text-Felder schreiben.
  • Regex-basierte Standort-Wiederherstellung in Cron- und Statistik-Pfaden entfernen, sobald Shells echte FK- oder Archiv-Verknüpfung haben.

holidays (formerly feiertage)

Aktuelle SpalteNeue Spalte (snake_case)Grund
FeiertagIDholiday_idPrimär-/Fremdschlüssel
DatumdateNamensstandardisierung
FeiertagnameNamensstandardisierung
BehoerdenIDdepartment_idPrimär-/Fremdschlüssel
updateZeitstempelupdated_atZeitstempel

calendar (formerly gesamtkalender)

Aktuelle SpalteNeue Spalte (snake_case)Grund
ididBereits snake_case
scope_idscope_idBereits snake_case
availability_idavailability_idBereits snake_case
timetimeBereits snake_case
seatseatBereits snake_case
process_idprocess_idBereits snake_case
slotsslotsBereits snake_case
statusstatusBereits snake_case
updated_atupdated_atBereits snake_case

department (formerly behoerde)

Aktuelle SpalteNeue Spalte (snake_case)Grund
BehoerdenIDdepartment_idPrimär-/Fremdschlüssel
OrganisationsIDorganization_idPrimär-/Fremdschlüssel
KundenIDjurisdiction_idPrimär-/Fremdschlüssel
NamenameNamensstandardisierung
AdresseaddressNamensstandardisierung
Ansprechpartnercontact_personNamensstandardisierung
IPProtectZeitip_protection_timeNamensstandardisierung

organization (formerly organisation)

Aktuelle SpalteNeue Spalte (snake_case)Grund
OrganisationsIDorganization_idPrimär-/Fremdschlüssel
InfoBezirkIDinfo_district_idPrimär-/Fremdschlüssel
KundenIDjurisdiction_idPrimär-/Fremdschlüssel
OrganisationsnamenameNamensstandardisierung
AnschriftaddressNamensstandardisierung
kioskpasswortschutzkiosk_password_protectionNamensstandardisierung

Phase 2: Benutzer- & Prozess-Tabellen (mittlere Priorität)

citizen_requests (formerly buergeranliegen)

Aktuelle SpalteNeue Spalte (snake_case)Grund
BuergeranliegenIDcitizen_request_idPrimär-/Fremdschlüssel
BuergerIDprocess_idPrimär-/Fremdschlüssel
BuergerarchivIDcitizen_archive_idPrimär-/Fremdschlüssel
AnliegenIDrequest_idPrimär-/Fremdschlüssel
sourcesourceBereits snake_case

citizen_archive (formerly buergerarchiv)

Aktuelle SpalteNeue Spalte (snake_case)Grund
BuergerarchivIDcitizen_archive_idPrimär-/Fremdschlüssel
StandortIDscope_idPrimär-/Fremdschlüssel
DatumdateNamensstandardisierung
mitTerminwith_appointmentNamensstandardisierung
nicht_erschienendid_not_appearNamensstandardisierung
ZeitstempeltimestampZeitstempel
waiting_timewaiting_timeBereits snake_case
AnzahlPersonenperson_countNamensstandardisierung
processing_timeprocessing_timeBereits snake_case
namenameBereits snake_case
dienstleistungenservicesNamensstandardisierung
way_timeway_timeBereits snake_case

citizen_archive_today (formerly buergerarchivtoday)

Aktuelle SpalteNeue Spalte (snake_case)Grund
BuergerarchivIDcitizen_archive_idPrimär-/Fremdschlüssel
StandortIDscope_idPrimär-/Fremdschlüssel
DatumdateNamensstandardisierung
mitTerminwith_appointmentNamensstandardisierung
nicht_erschienendid_not_appearNamensstandardisierung
ZeitstempeltimestampZeitstempel
waiting_timewaiting_timeBereits snake_case
AnzahlPersonenperson_countNamensstandardisierung
processing_timeprocessing_timeBereits snake_case
namenameBereits snake_case
dienstleistungenservicesNamensstandardisierung
way_timeway_timeBereits snake_case

user (formerly nutzer)

Aktuelle SpalteNeue Spalte (snake_case)Grund
NutzerIDuser_idPrimär-/Fremdschlüssel
NamenameNamensstandardisierung
Passworthashpassword_hashNamensstandardisierung
Fragesecurity_questionNamensstandardisierung
Antworthashanswer_hashNamensstandardisierung
Berechtigungpermission_levelNamensstandardisierung
KundenIDjurisdiction_idPrimär-/Fremdschlüssel
BehoerdenIDdepartment_idPrimär-/Fremdschlüssel
SessionIDsession_idPrimär-/Fremdschlüssel
StandortIDscope_idPrimär-/Fremdschlüssel
Arbeitsplatznrworkstation_numberNamensstandardisierung
DatumdateNamensstandardisierung
Kalenderansichtcalendar_viewNamensstandardisierung
clusteransichtcluster_viewNamensstandardisierung
notrufinitiierungemergency_initiationNamensstandardisierung
notrufantwortemergency_responseNamensstandardisierung
aufrufzusatzcall_suffixNamensstandardisierung
lastUpdatelast_updateNamensstandardisierung
sessionExpirysession_expiryNamensstandardisierung

user_assignment (formerly nutzerzuordnung)

Aktuelle SpalteNeue Spalte (snake_case)Grund
nutzeriduser_idPrimär-/Fremdschlüssel
behoerdeniddepartment_idPrimär-/Fremdschlüssel

jurisdiction (formerly kunde)

Aktuelle SpalteNeue Spalte (snake_case)Grund
KundenIDjurisdiction_idPrimär-/Fremdschlüssel
KundennamenameNamensstandardisierung
AnschriftaddressNamensstandardisierung
ModulemodulesNamensstandardisierung
Startkennungstart_identifierNamensstandardisierung
Anzahlkennungenidentifier_countNamensstandardisierung
TerminURLappointment_urlNamensstandardisierung
Aktuelle SpalteNeue Spalte (snake_case)Grund
linkidlink_idPrimär-/Fremdschlüssel
kundenidjurisdiction_idPrimär-/Fremdschlüssel
organisationsidorganization_idPrimär-/Fremdschlüssel
behoerdeniddepartment_idPrimär-/Fremdschlüssel
beschreibungdescriptionNamensstandardisierung
linklinkBereits snake_case
oeffentlichpublicNamensstandardisierung
neuerFramenew_frameNamensstandardisierung

Phase 3: System- & Konfigurations-Tabellen (niedrigere Priorität)

billing (formerly abrechnung)

Aktuelle SpalteNeue Spalte (snake_case)Grund
AbrechnungsIDbilling_idPrimär-/Fremdschlüssel
StandortIDscope_idPrimär-/Fremdschlüssel
TelefonnummerphoneNamensstandardisierung
DatumdateNamensstandardisierung
gesendetsentNamensstandardisierung

ip_exceptions (formerly ipausnahmen)

Aktuelle SpalteNeue Spalte (snake_case)Grund
IPIDip_exception_idPrimär-/Fremdschlüssel
BehoerdenIDdepartment_idPrimär-/Fremdschlüssel
IPAdresseip_addressNamensstandardisierung

kiosk

Aktuelle SpalteNeue Spalte (snake_case)Grund
kioskidkiosk_idPrimär-/Fremdschlüssel
kundenidjurisdiction_idPrimär-/Fremdschlüssel
organisationsidorganization_idPrimär-/Fremdschlüssel
timestamptimestampBereits snake_case
cookiecodecookie_codeNamensstandardisierung
namenameBereits snake_case
zugelassenallowedNamensstandardisierung

queue_number_statistics (formerly wartenrstatistik)

Aktuelle SpalteNeue Spalte (snake_case)Grund
datumdateDatum
standortidscope_idFremdschlüssel zu scope
wartenrstatistikidqueue_number_statistics_idPrimärschlüssel
hour_##_waiting_time_spontaneoushour_##_waiting_time_spontaneousPer-hour actual waiting time (spontaneous); ## = 00–23
hour_##_waiting_time_appointmenthour_##_waiting_time_appointmentPer-hour actual waiting time (appointment); ## = 00–23
hour_##_way_time_spontaneoushour_##_way_time_spontaneousPer-hour way time (spontaneous); ## = 00–23
hour_##_way_time_appointmenthour_##_way_time_appointmentPer-hour way time (appointment); ## = 00–23
hour_##_estimated_waiting_time_spontaneoushour_##_estimated_waiting_time_spontaneousPer-hour estimated waiting time (spontaneous); ## = 00–23
hour_##_estimated_waiting_time_appointmenthour_##_estimated_waiting_time_appointmentPer-hour estimated waiting time (appointment); ## = 00–23
hour_##_waiting_count_spontaneoushour_##_waiting_count_spontaneousPer-hour waiting count (spontaneous); ## = 00–23
hour_##_waiting_count_appointmenthour_##_waiting_count_appointmentPer-hour waiting count (appointment); ## = 00–23

Legacy columns echte_zeit_ab_##_*, wegezeit_ab_##_*, zeit_ab_##_*, and wartende_ab_##_* map to the hour_##_* names above (see migration 91775568666-rename-waiting-way-processing-columns.sql).

location_cluster (formerly standortcluster)

Aktuelle SpalteNeue Spalte (snake_case)Grund
clusterIDcluster_idPrimär-/Fremdschlüssel
namenameBereits snake_case
clusterinfozeile1cluster_info_line_1Namensstandardisierung
clusterinfozeile2cluster_info_line_2Namensstandardisierung
stadtplanlinkcity_map_linkNamensstandardisierung
aufrufanzeigetextdisplay_textNamensstandardisierung
standortkuerzelanzeigenshow_scope_short_nameNamensstandardisierung

statistics (formerly statistik)

Aktuelle SpalteNeue Spalte (snake_case)Grund
statistikidstatistics_idPrimär-/Fremdschlüssel
kundenidjurisdiction_idPrimär-/Fremdschlüssel
organisationsidorganization_idPrimär-/Fremdschlüssel
behoerdeniddepartment_idPrimär-/Fremdschlüssel
clusteridcluster_idPrimär-/Fremdschlüssel
standortidscope_idPrimär-/Fremdschlüssel
anliegenidrequest_idPrimär-/Fremdschlüssel
datumdatumBereits snake_case
lastbuergerarchividlast_citizen_archive_idPrimär-/Fremdschlüssel
terminwith_appointmentNamensstandardisierung
info_dl_idinfo_provider_idPrimär-/Fremdschlüssel
processing_timeprocessing_timeBereits snake_case

api_client (formerly apiclient)

Aktuelle SpalteNeue Spalte (snake_case)Grund
apiClientIDapi_client_idPrimär-/Fremdschlüssel
clientKeyclient_keyNamensstandardisierung
shortnameshort_nameNamensstandardisierung
accesslevelaccess_levelNamensstandardisierung
updateZeitstempelupdated_atZeitstempel

api_key (formerly apikey)

Aktuelle SpalteNeue Spalte (snake_case)Grund
keykeyBereits snake_case
createIPcreate_ipNamensstandardisierung
tstsBereits snake_case
apiClientIDapi_client_idPrimär-/Fremdschlüssel

api_quota (formerly apiquota)

Aktuelle SpalteNeue Spalte (snake_case)Grund
quotaidquota_idPrimär-/Fremdschlüssel
keykeyBereits snake_case
routerouteBereits snake_case
periodperiodBereits snake_case
requestsrequestsBereits snake_case
tstsBereits snake_case

role

Aktuelle SpalteNeue Spalte (snake_case)Grund
ididBereits snake_case
namenameBereits snake_case
descriptiondescriptionBereits snake_case

permission

Aktuelle SpalteNeue Spalte (snake_case)Grund
ididBereits snake_case
namenameBereits snake_case
descriptiondescriptionBereits snake_case

role_permission

Aktuelle SpalteNeue Spalte (snake_case)Grund
role_idrole_idBereits snake_case
permission_idpermission_idBereits snake_case

user_role

Aktuelle SpalteNeue Spalte (snake_case)Grund
user_iduser_idBereits snake_case
role_idrole_idBereits snake_case

Phase 4: Kommunikations-Tabellen

email

Aktuelle SpalteNeue Spalte (snake_case)Grund
emailIDemail_idPrimär-/Fremdschlüssel
BehoerdenIDdepartment_idPrimär-/Fremdschlüssel
serveradresseserver_addressNamensstandardisierung
authenticationauthenticationBereits snake_case
usernameusernameBereits snake_case
passwordpasswordBereits snake_case
ssl_codingssl_encodingNamensstandardisierung
absenderadressesender_addressNamensstandardisierung
send_remindersend_reminderBereits snake_case
send_reminder_minutes_beforesend_reminder_minutes_beforeBereits snake_case

sms

Aktuelle SpalteNeue Spalte (snake_case)Grund
smsIDsms_idPrimär-/Fremdschlüssel
BehoerdenIDdepartment_idPrimär-/Fremdschlüssel
enabledenabledBereits snake_case
AbsendersenderNamensstandardisierung
interneterinnerunginternet_reminderNamensstandardisierung
internetbestaetigunginternet_confirmationNamensstandardisierung

mail_part (formerly mailpart)

Aktuelle SpalteNeue Spalte (snake_case)Grund
ididBereits snake_case
queueIdqueue_idPrimär-/Fremdschlüssel
mimemimeBereits snake_case
contentcontentBereits snake_case
base64base64Bereits snake_case

mail_queue (formerly mailqueue)

Aktuelle SpalteNeue Spalte (snake_case)Grund
ididBereits snake_case
processIDprocess_idPrimär-/Fremdschlüssel
departmentIDdepartment_idPrimär-/Fremdschlüssel
createIPcreate_ipNamensstandardisierung
createZeitstempelcreated_atZeitstempel
subjectsubjectBereits snake_case
clientFamilyNameclient_family_nameNamensstandardisierung
clientEmailclient_emailNamensstandardisierung

mail_template (formerly mailtemplate)

Aktuelle SpalteNeue Spalte (snake_case)Grund
ididBereits snake_case
namenameBereits snake_case
valuevalueBereits snake_case
providerproviderBereits snake_case
changeZeitstempelchanged_atZeitstempel

notification_queue (formerly notificationqueue)

Aktuelle SpalteNeue Spalte (snake_case)Grund
ididBereits snake_case
processIDprocess_idPrimär-/Fremdschlüssel
departmentIDdepartment_idPrimär-/Fremdschlüssel
createIPcreate_ipNamensstandardisierung
createZeitstempelcreated_atZeitstempel
messagemessageBereits snake_case
clientFamilyNameclient_family_nameNamensstandardisierung
clientTelephoneclient_phoneNamensstandardisierung
scopeIDscope_idPrimär-/Fremdschlüssel

Phase 5: Datumn- & Prozess-Tabellen

closures

Aktuelle SpalteNeue Spalte (snake_case)Grund
ididBereits snake_case
yearyearBereits snake_case
monthmonthBereits snake_case
daydayBereits snake_case
StandortIDscope_idPrimär-/Fremdschlüssel
updateZeitstempelupdated_atZeitstempel

config

Aktuelle SpalteNeue Spalte (snake_case)Grund
namenameBereits snake_case
valuevalueBereits snake_case
changeZeitstempelchanged_atZeitstempel

event_log (formerly eventlog)

Aktuelle SpalteNeue Spalte (snake_case)Grund
eventIdevent_idPrimär-/Fremdschlüssel
eventNameevent_nameNamensstandardisierung
originoriginBereits snake_case
referenceTypereference_typeNamensstandardisierung
referencereferenceBereits snake_case
sessionidsession_idPrimär-/Fremdschlüssel
contextjsoncontext_jsonNamensstandardisierung
creationDatumTimecreated_atZeitstempel
expirationDatumTimeexpires_atZeitstempel

image_data (formerly imagedata)

Aktuelle SpalteNeue Spalte (snake_case)Grund
imagenameimage_nameNamensstandardisierung
imagecontentimage_contentNamensstandardisierung
tstsBereits snake_case

log

Aktuelle SpalteNeue Spalte (snake_case)Grund
log_idlog_idBereits snake_case
typetypeBereits snake_case
reference_idreference_idBereits snake_case
tstsBereits snake_case
messagemessageBereits snake_case
scope_idscope_idBereits snake_case
datadataBereits snake_case
user_iduser_idBereits snake_case

migrations

Aktuelle SpalteNeue Spalte (snake_case)Grund
filenamefilenameBereits snake_case
changeZeitstempelchanged_atZeitstempel

preferences

Aktuelle SpalteNeue Spalte (snake_case)Grund
entityentityBereits snake_case
ididBereits snake_case
groupNamegroup_nameNamensstandardisierung
namenameBereits snake_case
valuevalueBereits snake_case
updateZeitstempelupdated_atZeitstempel

process_sequence

Aktuelle SpalteNeue Spalte (snake_case)Grund
processIdprocess_idPrimär-/Fremdschlüssel

session_data (formerly sessiondata)

Aktuelle SpalteNeue Spalte (snake_case)Grund
sessionidsession_idPrimär-/Fremdschlüssel
sessionnamesession_nameNamensstandardisierung
sessioncontentsession_contentNamensstandardisierung
tstsBereits snake_case

source

Aktuelle SpalteNeue Spalte (snake_case)Grund
sourcesourceBereits snake_case
labellabelBereits snake_case
editableeditableBereits snake_case
contact__namecontact__nameBereits snake_case
contact__emailcontact__emailBereits snake_case
lastChangelast_changeNamensstandardisierung

overview_calendar

Aktuelle SpalteNeue Spalte (snake_case)Grund
ididBereits snake_case
scope_idscope_idBereits snake_case
process_idprocess_idBereits snake_case
statusstatusBereits snake_case
starts_atstarts_atBereits snake_case
ends_atends_atBereits snake_case
updated_atupdated_atBereits snake_case

Phase 6: Leistungs- & Anbieter-Tabellen

provider

Aktuelle SpalteNeue Spalte (snake_case)Grund
sourcesourceBereits snake_case
ididBereits snake_case
namenameBereits snake_case
contact__citycontact__cityBereits snake_case
contact__countrycontact__countryBereits snake_case
contact__latcontact__latBereits snake_case
contact__loncontact__lonBereits snake_case
contact__postalCodecontact__postalCodeBereits snake_case
contact__regioncontact__regionBereits snake_case
contact__streetcontact__streetBereits snake_case
contact__streetNumbercontact__streetNumberBereits snake_case
linklinkBereits snake_case
datadataBereits snake_case
display_namedisplay_nameBereits snake_case
parent_idparent_idBereits snake_case

request

Aktuelle SpalteNeue Spalte (snake_case)Grund
sourcesourceBereits snake_case
ididBereits snake_case
namenameBereits snake_case
linklinkBereits snake_case
groupgroupBereits snake_case
datadataBereits snake_case
parent_idparent_idBereits snake_case
variant_idvariant_idBereits snake_case

request_provider

Aktuelle SpalteNeue Spalte (snake_case)Grund
sourcesourceBereits snake_case
request__idrequest__idBereits snake_case
provider__idprovider__idBereits snake_case
slotsslotsBereits snake_case
bookablebookableBereits snake_case
max_quantitymax_quantityBereits snake_case
public_visibilitypublic_visibilityBereits snake_case

request_variant

Aktuelle SpalteNeue Spalte (snake_case)Grund
ididBereits snake_case
namenameBereits snake_case

Phase 7: Slot-System-Tabellen

slot

Aktuelle SpalteNeue Spalte (snake_case)Grund
slotIDslot_idPrimär-/Fremdschlüssel
scopeIDscope_idPrimär-/Fremdschlüssel
yearyearBereits snake_case
monthmonthBereits snake_case
daydayBereits snake_case
timetimeBereits snake_case
availabilityIDavailability_idPrimär-/Fremdschlüssel
publicpublicBereits snake_case
callcentercallcenterBereits snake_case
interninternBereits snake_case
statusstatusBereits snake_case
slotTimeInMinutesslot_time_in_minutesNamensstandardisierung
createZeitstempelcreated_atZeitstempel
updateZeitstempelupdated_atZeitstempel

slot_hierarchy (formerly slot_hiera)

Aktuelle SpalteNeue Spalte (snake_case)Grund
slothieraIDslot_hierarchy_idPrimär-/Fremdschlüssel
slotIDslot_idPrimär-/Fremdschlüssel
ancestorIDancestor_idPrimär-/Fremdschlüssel
ancestorLevelancestor_levelNamensstandardisierung

slot_process

Aktuelle SpalteNeue Spalte (snake_case)Grund
slotIDslot_idPrimär-/Fremdschlüssel
processIDprocess_idPrimär-/Fremdschlüssel
updateZeitstempelupdated_atZeitstempel

slot_sequence

Aktuelle SpalteNeue Spalte (snake_case)Grund
slotsequenceslot_sequenceNamensstandardisierung

Phase 8: Zuweisungs- & Clustering-Tabellen

cluster_assignment (formerly clusterzuordnung)

Aktuelle SpalteNeue Spalte (snake_case)Grund
clusterIDcluster_idPrimär-/Fremdschlüssel
standortIDscope_idPrimär-/Fremdschlüssel

Phase 9: Fremdschlüssel-Standardisierung

Aktuelles MusterNeues MusterBeispiel
StandortIDscope_idFremdschlüssel zur scope-Tabelle
BehoerdenIDdepartment_idFremdschlüssel zur department-Tabelle
BuergerIDprocess_idFremdschlüssel zur process-Tabelle (buerger)
NutzerIDuser_idFremdschlüssel zur user-Tabelle
KundenIDjurisdiction_idFremdschlüssel zur jurisdiction-Tabelle (kunde)
OrganisationsIDorganization_idFremdschlüssel zur organization-Tabelle
OeffnungszeitIDavailability_idFremdschlüssel zur availability-Tabelle
apiClientIDapi_client_idFremdschlüssel zur api_client-Tabelle
clusterIDcluster_idFremdschlüssel zur location_cluster-Tabelle

Die Spaltenzuordnungen in Abschnitt 2 basieren auf .resources/zms.sql und aktuellen Migrationen. Einige Spalten (z. B. provider.contact__*) sind JSON-Pfad-Schlüssel in relationalen Spalten und bleiben vorerst unverändert, bis der PHP-Mapping-Refactor (Abschnitt 3) sie in camelCase-Entity-Felder überführt.

3. Vollständiger Plan zur Umbenennung der PHP-Variablen-Mappings – alles Englisch, alles camelCase

Step 1: Update Entity Mappings Replace all double underscore patterns with camelCase Update all German variable names to English Ensure consistent naming across all Query classes

Step 2: Update Method Names Update method names that reference old mappings Ensure parameter names match new mapping keys Update any hardcoded references

Step 3: Update Tests Update all test cases to use new mapping keys Ensure test data matches new naming conventions Update any mock data or fixtures

Step 4: Update Documentation Update API documentation with new field names Update any external references to old field names Create migration guide for API consumers This comprehensive plan ensures: Complete camelCase conversion for all PHP variable mappings Elimination of double underscores in favor of camelCase Consistent English naming throughout the codebase Maintainable structure with clear patterns

Phase 1: Kern-Entity-Mappings (hohe Priorität)

Availability Query Class

Current MappingNew Mapping (camelCase)Database Column (snake_case)
'id''id'availability.availability_id
'scope**id''scopeId'availability.scope_id
'bookable**startInDays''bookableStartInDays'availability.open_from_days
'bookable**endInDays''bookableEndInDays'availability.open_until_days
'description''description'availability.comment
'startDate''startDate'availability.start_date
'startTime''startTime'availability.start_time
'endDate''endDate'availability.end_date
'endTime''endTime'availability.end_time
'lastChange''lastChange'availability.updated_at
'multipleSlotsAllowed''multipleSlotsAllowed'availability.multiple_slots_allowed
'repeat**afterWeeks''repeatAfterWeeks'availability.every_x_weeks
'repeat**weekOfMonth''repeatWeekOfMonth'availability.every_other_week
'slotTimeInMinutes''slotTimeInMinutes'availability.time_slot
'type''type'availability.type
'weekday**monday''weekdayMonday'availability.weekday
'weekday**tuesday''weekdayTuesday'availability.weekday
'weekday**wednesday''weekdayWednesday'availability.weekday
'weekday**thursday''weekdayThursday'availability.weekday
'weekday**friday''weekdayFriday'availability.weekday
'weekday**saturday''weekdaySaturday'availability.weekday
'weekday**sunday''weekdaySunday'availability.weekday
'workstationCount**callcenter''workstationCountCallcenter'availability.workstation_count_callcenter
'workstationCount**intern''workstationCountIntern'availability.workstation_count_intern
'workstationCount__public''workstationCountPublic'availability.workstation_count_public

Scope Query Class

Current MappingNew Mapping (camelCase)Database Column (snake_case)
'hint''hint'scope.hint
'id''id'scope.scope_id
'contact**name''contactName'scopeprovider.name
'contact**street''contactStreet'scope.address
'contact**email''contactEmail'scope.admin_email
'contact**country''contactCountry'"Germany"
'lastChange''lastChange'scope.updated_at
'preferencesappointmentdeallocationDuration''preferencesAppointmentDeallocationDuration'scope.deletion_duration
'preferencesappointmentinfoForAppointment''preferencesAppointmentInfoForAppointment'scope.info_for_appointment
'preferencesappointmentinfoForAllAppointments''preferencesAppointmentInfoForAllAppointments'scope.info_for_all_appointments
'preferencesappointmentendInDaysDefault''preferencesAppointmentEndInDaysDefault'scope.appointments_until_days
'preferencesappointmentmultipleSlotsEnabled''preferencesAppointmentMultipleSlotsEnabled'scope.multiple_appointments
'preferencesappointmentreservationDuration''preferencesAppointmentReservationDuration'scope.reservation_duration
'preferencesappointmentactivationDuration''preferencesAppointmentActivationDuration'scope.activation_duration
'preferencesappointmentstartInDaysDefault''preferencesAppointmentStartInDaysDefault'scope.appointments_from_days
'preferencesappointmentnotificationConfirmationEnabled''preferencesAppointmentNotificationConfirmationEnabled'scope.sms_confirmation_enabled
'preferencesappointmentnotificationHeadsUpEnabled''preferencesAppointmentNotificationHeadsUpEnabled'scope.sms_notification_enabled
'preferencesclientalternateAppointmentUrl''preferencesClientAlternateAppointmentUrl'scope.qtv_url
'preferencesclientamendmentActivated''preferencesClientAmendmentActivated'scope.comment_required
'preferencesclientamendmentLabel''preferencesClientAmendmentLabel'scope.comment_label
'preferencesclientemailFrom''preferencesClientEmailFrom'scopemail.sender_address
'preferencesclientemailRequired''preferencesClientEmailRequired'scope.email_required
'preferencesclientemailConfirmationActivated''preferencesClientEmailConfirmationActivated'scope.email_confirmation_activated
'preferencesclienttelephoneActivated''preferencesClientTelephoneActivated'scope.phone_enabled
'preferencesclienttelephoneRequired''preferencesClientTelephoneRequired'scope.phone_required
'preferencesclientappointmentsPerMail''preferencesClientAppointmentsPerMail'scope.appointments_per_mail
'preferencesclientslotsPerAppointment''preferencesClientSlotsPerAppointment'scope.slots_per_appointment
'preferencesclientwhitelistedMails''preferencesClientWhitelistedMails'scope.whitelisted_mails
'preferencesclientcustomTextfieldActivated''preferencesClientCustomTextfieldActivated'scope.custom_text_field_active
'preferencesclientcustomTextfieldRequired''preferencesClientCustomTextfieldRequired'scope.custom_text_field_required
'preferencesclientcustomTextfieldLabel''preferencesClientCustomTextfieldLabel'scope.custom_text_field_label
'preferencesclientcustomTextfield2Activated''preferencesClientCustomTextfield2Activated'scope.custom_text_field2_active
'preferencesclientcustomTextfield2Required''preferencesClientCustomTextfield2Required'scope.custom_text_field2_required
'preferencesclientcustomTextfield2Label''preferencesClientCustomTextfield2Label'scope.custom_text_field2_label
'preferencesclientcaptchaActivatedRequired''preferencesClientCaptchaActivatedRequired'scope.captcha_activated_required
'preferencesclientadminMailOnAppointment''preferencesClientAdminMailOnAppointment'scope.admin_mail_on_appointment
'preferencesclientadminMailOnDeleted''preferencesClientAdminMailOnDeleted'scope.admin_mail_on_deleted
'preferencesclientadminMailOnUpdated''preferencesClientAdminMailOnUpdated'scope.admin_mail_on_updated
'preferencesclientadminMailOnMailSent''preferencesClientAdminMailOnMailSent'scope.admin_mail_on_mail_sent
'preferencesnotificationsconfirmationContent''preferencesNotificationsConfirmationContent'scope.sms_confirmation_text
'preferencesnotificationsheadsUpContent''preferencesNotificationsHeadsUpContent'scope.sms_notification_text
'preferencesnotificationsheadsUpTime''preferencesNotificationsHeadsUpTime'scope.sms_notification_deadline
'preferencespickupalternateName''preferencesPickupAlternateName'scope.pickup_counter_name
'preferencespickupisDefault''preferencesPickupIsDefault'scope.default_pickup_location
'preferencesqueuecallCountMax''preferencesQueueCallCountMax'scope.recall_count
'preferencesqueuecallDisplayText''preferencesQueueCallDisplayText'scope.display_text
'preferencesqueuefirstNumber''preferencesQueueFirstNumber'scope.first_queue_number
'preferencesqueuelastNumber''preferencesQueueLastNumber'scope.last_queue_number
'preferencesqueuemaxNumberContingent''preferencesQueueMaxNumberContingent'scope.queue_number_contingent
'preferencesqueueprocessingTimeAverage''preferencesQueueProcessingTimeAverage'scope.processing_time
'preferencesqueuepublishWaitingTimeEnabled''preferencesQueuePublishWaitingTimeEnabled'scope.publish_waiting_time
'preferencesqueuestatisticsEnabled''preferencesQueueStatisticsEnabled'scope.statistics_enabled
'preferencessurveyemailContent''preferencesSurveyEmailContent'scope.customer_survey_email_text
'preferencessurveyenabled''preferencesSurveyEnabled'scope.customer_survey
'preferencessurveylabel''preferencesSurveyLabel'scope.customer_survey_label
'preferencesticketprinterbuttonName''preferencesTicketprinterButtonName'scope.location_info_line
'preferencesticketprinterconfirmationEnabled''preferencesTicketprinterConfirmationEnabled'scope.sms_wms_confirmation
'preferencesticketprinterdeactivatedText''preferencesTicketprinterDeactivatedText'scope.queue_hint
'preferencesticketprinternotificationsAmendmentEnabled''preferencesTicketprinterNotificationsAmendmentEnabled'scope.sms_addition
'preferencesticketprinternotificationsEnabled''preferencesTicketprinterNotificationsEnabled'scope.sms_queue
'preferencesticketprinternotificationsDelay''preferencesTicketprinterNotificationsDelay'scope.sms_kiosk_offer_deadline
'preferencesworkstationemergencyEnabled''preferencesWorkstationEmergencyEnabled'scope.emergency_function
'preferencesworkstationemergencyRefreshInterval''preferencesWorkstationEmergencyRefreshInterval'scope.emergency_refresh_interval
'shortName''shortName'scope.short_name
'statusemergencyacceptedByWorkstation''statusEmergencyAcceptedByWorkstation'scope.emergency_response
'statusemergencyactivated''statusEmergencyActivated'scope.emergency_triggered
'statusemergencycalledByWorkstation''statusEmergencyCalledByWorkstation'scope.emergency_initiation
'statusqueueghostWorkstationCount''statusQueueGhostWorkstationCount'scope.virtual_processor_count
'statusqueuegivenNumberCount''statusQueueGivenNumberCount'scope.assigned_queue_numbers
'statusqueuelastGivenNumber''statusQueueLastGivenNumber'scope.last_queue_number
'statusqueuelastGivenNumberTimestamp''statusQueueLastGivenNumberTimestamp'scope.queue_number_date

Phase 2: Prozess- & Bürger-Mappings (mittlere Priorität)

Process Query Class

Current MappingNew Mapping (camelCase)Database Column (snake_case)
'amendment''amendment'process.comment
'id''id'process.citizen_id
'appointments0date''appointments0Date'process.appointment_datetime
'scope**id''scopeId'process.scope_id
'appointments0scope**id''appointments0ScopeId'process.scope_id

Citizen Query Class

Current MappingNew Mapping (camelCase)Database Column (snake_case)
'id''id'citizen.citizen_id
'scopeId''scopeId'citizen.scope_id
'pickupLocationId''pickupLocationId'citizen.pickup_location_id
'userId''userId'citizen.user_id
'name''name'citizen.name
'email''email'citizen.email
'phone''phone'citizen.phone
'comment''comment'citizen.comment
'provisionalBooking''provisionalBooking'citizen.provisional_booking
'confirmed''confirmed'citizen.confirmed
'callSuccessful''callSuccessful'citizen.call_successful
'callTime''callTime'citizen.call_time
'pickupPerson''pickupPerson'citizen.pickup_person
'queueNumber''queueNumber'citizen.queue_number
'queueNumberDate''queueNumberDate'citizen.queue_number_date
'waitingTime''waitingTime'citizen.waiting_time
'processingTime''processingTime'citizen.processing_time
'parked''parked'citizen.parked
'wasMissed''wasMissed'citizen.was_missed
'apiClientId''apiClientId'citizen.api_client_id
'source''source'citizen.source
'lastChange''lastChange'citizen.updated_at

Phase 3: Anbieter- & Request-Mappings (niedrigere Priorität)

Provider Query Class

Current MappingNew Mapping (camelCase)Database Column (snake_case)
'contact**city''contactCity'provider.contact_city
'contact**country''contactCountry'provider.contact_country
'contact**name''contactName'provider.name
'contact**postalCode''contactPostalCode'provider.contact_postal_code
'contact**region''contactRegion'provider.contact_region
'contact**street''contactStreet'provider.contact_street
'contact__streetNumber''contactStreetNumber'provider.contact_street_number
'id''id'provider.id
'link''link'provider.link
'name''name'provider.name
'displayName''displayName'provider.display_name
'source''source'provider.source
'data''data'provider.data

4. Langfristige Schema-Vision (über Umbenennung hinaus)

Die Abschnitte 1–3 standardisieren Namen. Dieser Abschnitt dokumentiert strukturelle Änderungen für ein gesünderes Langzeit-Schema. Es handelt sich um Planungsnotizen, keine festen Zeitpläne.

4.1 Größere strukturelle Refactorings

Aufteilung von buerger in citizen und process (oder citizen und appointment)

Heute ist buerger die physische Tabelle der process-Entity. Sie vermischt gewachsene Concerns:

  • Bürger-/Kunden-PII (Name, EMail, Telefonnummer, Custom Fields)
  • Terminplanung (Datum, Uhrzeit, Standortbezug, Slot-Verknüpfung)
  • Warteschlangen-Laufzeit (wartenummer, status, waiting_time, Aufruf-Metadaten)
  • Archiv- und Statistik-Eingaben

Zielmodelle (Kandidaten):

OptionTabellenPasst wenn
Acitizen + processWarteschlangen-zentriert; Process als Arbeitseinheit
Bcitizen + appointmentTermin-zentriert; klare Trennung Buchung vs. Warteschlange

Die kurzfristige Umbenennung in Abschnitt 1 kann buergerprocess vorsehen. Die Aufteilung hier ist eine spätere Migration, sobald API, Statistik und Archiv-Pfade entflochten sind.

Entfernen von buergerarchivtoday / citizen_archive_today

buergerarchivtoday ist ein Tages-Snapshot von buergerarchiv — redundant und wartungsintensiv. Stattdessen:

  • citizen_archive mit Datumsfilter und passenden Indizes, oder
  • View / materialisierte View bei Performance-Bedarf

Tabelle entfernen, sobald Abfragen und Dashboards ohne sie auskommen.

Normalisierung von queue_number_statistics (wartenrstatistik)

Die Tabelle ist extrem breit: stündliche Spalten für geschätzte Wartezeit, echte Wartezeit, Wegezeit und Wartende — jeweils Spontan vs. Termin (96+ Spalten pro Metrik-Familie). Zwischen-Umbenennungen: Migration 91775568666-rename-waiting-way-processing-columns.sql.

Langfristig: Fact-Tabellen, z. B. queue_statistics_hourly mit (scope_id, date, hour, metric, channel, value)channel: spontaneous | appointment.

Normalisierung von log.data (JSON)

Neben indexierten Spalten (type, scope_id, user_id, reference_id) liegt ein JSON-Blob data. Suche darin ist langsam.

Richtung: häufig gefilterte Felder als typisierte Spalten; data nur noch für Debug oder entfernen.

Aufteilen und umbenennen von preferences

preferences nutzt (entity, id, groupName, name), mischt aber:

  • Scope-Einstellungen (entity scope, aus standort migriert)
  • System-/Admin-Konfiguration (zmsadmin Config-Bereich)

Richtung: scope_preference / scope_setting plus system_setting (oder Abgrenzung zu config); generischen Namen preferences für Scope-Daten vermeiden.

DLDB-Tabellen: provider, request, request_provider, request_variant

DLDB-synchronisiert; mehrere Tabellen mit data-JSON. zmscitizenapi spricht von offices und services (officeId, serviceId, OfficeServiceRelation).

AktuellCitizen-APIKandidat Tabellenname
providerofficeoffice
requestserviceservice
request_providerStandort–Leistungoffice_service
request_variantLeistungsvarianteservice_variant

Struktur: data in relationale Spalten überführen, wo abgefragt; JSON nur für seltene DLDB-Felder.

4.2 Tabellen-Disposition

Tabelle (aktuell)Geplante DispositionHinweise
abrechnungLöschenUngenutzt. Migration 91772633097-drop-abrechnung.sql löscht die Tabelle bereits.
ipausnahmenPrüfen → vermutlich löschenKeine PHP-Referenzen zum Zeitpunkt der Dokumentation.
apikeyPrüfenRouten in zmsapi; klären ob Produktion noch API-Keys nutzt.
apiquotaPrüfenAn apikey gekoppelt.
notificationqueueLöschenSMS-/Notification-Abbau. Migration 91772633137-drop-notifcationqueue.sql.
eventlogPrüfenNoch in Nutzung (z. B. ProcessListSummaryMail); ggf. mit log zusammenführen.
imagedataNeu gestaltenNur URL in DB; Binärdaten in RefArch-S3 via zmsadmin. Admin-Logo aktuell statisch unter terminvereinbarung/admin/_css/images/muc_logo_head2.png — DB-Upload vermutlich ungenutzt/defekt.
kundenlinksLöschenBereits in Abschnitt 1 als ungenutzt markiert.
buergerarchivtodayLöschenSiehe §4.1; redundant zu buergerarchiv.

4.3 Assets, Migrationen und Betrieb

image_data → Object Storage

  • Logos und Calldisplay-Bilder über zmsadmin in RefArch-S3 (oder kompatiblen Store) hochladen.
  • In der DB nur bucket, object_key und/oder HTTPS-URL speichern.
  • imagecontent-BLOB/TEXT nach Migration entfernen.
  • Alle Consumer prüfen: FileUploader, Calldisplay-Routen, Cluster/Scope-Bilder.

Migrations-Dateinamen

Viele Dateien beginnen mit 917… — chronologische Sortierung und Review erschwert.

Vorschlag für neue Migrationen:

{YYYYMMDD}-{HHMMSS}-{ticket-oder-kurzbeschreibung}.sql

Beispiel: 20260302-143000-ZMS-1234-split-buerger.sql. Historische Dateien nur umbenennen, wenn der Aufwand gerechtfertigt ist.

Tabelle migrations

Die Tabelle selbst ist in Ordnung; Ziel ist die Dateikonvention im Dateisystem, nicht ein Tabellenumbenennung.