Das Morphologie-System des Glossariums
1. Übersicht
Was ist das Morphologie-System?
Das Morphologie-System ist das zentrale linguistische Verarbeitungsmodul des Hermeneus-Glossariums. Es generiert automatisch alle grammatikalisch möglichen Formen eines lateinischen Lemmas basierend auf dessen linguistischen Grundangaben.
Zweck und Funktion
Das System verfolgt mehrere Ziele:
- Vollständige Formgenerierung: Automatische Erzeugung aller grammatikalisch korrekten Flexionsformen
- Linguistische Korrektheit: Anwendung wissenschaftlich fundierter Deklinations- und Konjugationsregeln
- Sonderfallbehandlung: Berücksichtigung von Anomalien, Defektiva, Semideponentien und anderen Ausnahmen
- Strukturierte Datenhaltung: Speicherung in hierarchischem JSON-Format für effiziente Abfragen
- Synchronisation: Automatischer Abgleich mit der HermeneusLemmaBank
Welche Wortarten sind morphologisierbar?
Folgende Wortarten können morphologisiert werden:
- Nomina (alle Deklinationsklassen: a, o, u, e, 3dekl, indekl)
- Adjektive (a/o-Deklination, konsonantische Deklination mit Komparation)
- Verben (alle Konjugationsklassen inklusive Deponentien, Semideponentien, Perfektopräsentien)
- Pronomina (Personal-, Demonstrativ-, Relativ-, Interrogativpronomina)
- Numeralia (Kardinal- und Ordinalzahlen)
- Eigennamen (nutzen NomenMorpher)
Nicht morphologisierbar sind:
- Partikel (Adverbien, Konjunktionen, Präpositionen, Interjektionen)
- Wendungen (feststehende Ausdrücke)
2. Architektur
Das Morphologie-System basiert auf einer klaren Dreischicht-Architektur:
Morphable-Trait
Der Morphable-Trait (app/Traits/Glossarium/Morphable.php) ist die zentrale Schnittstelle. Er wird von allen morphologisierbaren Model-Klassen verwendet und stellt zwei Hauptmethoden bereit:
trait Morphable
{
/**
* Hauptmethode zur Morphologisierung
* @param bool $SyncWithLemmabank
*/
public function morph(bool $SyncWithLemmabank = true): void
{
try {
$this->update(['status' => 1]); // Status: in Bearbeitung
$VocabMorpher = new ($this::MORPHER)($this);
$VocabMorpher->autoMorph();
$VocabMorpher->writeJSON();
if ($SyncWithLemmabank) {
HermeneusLemmaBank::sync($this, true);
}
} catch (\Exception $exception) {
// Fehlerbehandlung
Log::error("Vokabel $this->id ($this->wortart) konnte nicht morphologisiert werden!");
}
}
/**
* Gibt Informationen zur Morphologisierung zurück
* @return Response
*/
public function morphInfo(): Response
{
$VocabMorpher = $this::MORPHER;
$VocabMorphInfoHandler = $this::MORPH_INFO_HANDLER;
$MorphingVocab = new $VocabMorpher($this);
$MorphInfo = new $VocabMorphInfoHandler($MorphingVocab);
return $MorphInfo->getMorphInfo();
}
}Morpher-Klassen
Jede Wortart hat eine eigene Morpher-Klasse (app/Morpher/):
NomenMorpher.php- Dekliniert Nomina und EigennamenAdjektivMorpher.php- Dekliniert Adjektive und bildet KomparationVerbMorpher.php- Konjugiert Verben (komplexeste Klasse mit ca. 2000 Zeilen)PronomenMorpher.php- Dekliniert PronominaNumeraleMorpher.php- Dekliniert Numeralia
Jeder Morpher besitzt:
- Konstruktor zur Initialisierung
autoMorph()-Methode mit Regellogik- Build-Methoden für spezifische Paradigmen
writeJSON()-Methode zum SpeicherngetArray()-Methode zur Datenausgabe
MorphInfoHandler-Klassen
Diese Klassen liefern benutzerfreundliche Informationen über den Morphologisierungsprozess:
NomenMorphInfoHandler.phpAdjektivMorphInfoHandler.phpVerbMorphInfoHandler.phpPronomenMorphInfoHandler.phpNumeraleMorphInfoHandler.php
Sie geben Feedback wie:
- "A-Deklination erkannt"
- "Vokalischer Stamm (Gruppe 1: Neutrum) nach RH §38.1 erkannt"
- "Deponens: Kein Aktiv gebildet"
- "Semideponens erkannt"
Zusammenspiel zwischen Model, Trait und Morpher
Model (z.B. Nomen)
├── const MORPHER = NomenMorpher::class
├── const MORPH_INFO_HANDLER = NomenMorphInfoHandler::class
├── $RequiredFields = ['lemma', 'fb_stamm', 'fb_dklasse', 'fb_genus', 'bedeutung']
└── uses Morphable Trait
├── morph() Methode
│ └── instantiiert NomenMorpher($this)
│ ├── autoMorph()
│ └── writeJSON()
└── morphInfo() Methode
└── instantiiert NomenMorphInfoHandler3. Der Morphologie-Workflow
Schritt-für-Schritt-Ablauf
Erstellung eines Lemmas mit linguistischen Angaben
php$nomen = Nomen::create([ 'lemma' => 'puella', 'fb_stamm' => 'puell', 'fb_dklasse' => 'a', 'fb_genus' => 'f', 'bedeutung' => 'Mädchen' ]);Validierung der erforderlichen Felder
phppublic function IsValid(): bool { if ( !empty($this->lemma) && !empty($this->fb_stamm) && !empty($this->fb_dklasse) && !empty($this->fb_genus) && !empty($this->bedeutung) ) { return true; } return false; }Aufruf der morph()-Methode
php$nomen->morph(); // Mit LemmaBank-Sync // oder $nomen->morph(false); // Ohne LemmaBank-SyncInstantiierung des Morphers
php$VocabMorpher = new ($this::MORPHER)($this); // Dynamische Instantiierung basierend auf Klassen-KonstanteautoMorph()-Methode und Regelanwendung
Der Morpher analysiert die linguistischen Angaben und wendet die entsprechenden Regeln an:
phppublic function autoMorph() { switch ($this->Nomen->fb_dklasse) { case 'a': $this->buildNomenADeklination(); break; case 'o': if ($this->IsNeutrum()) { $this->buildNomenODeklinationNeutrum(); } else { $this->buildNomenODeklinationMaskulin(); } break; // ... weitere Fälle } // Sonderfälle behandeln if ($this->IsPluraleTantum()) { $this->clearSingular(); } if ($this->IsAnomale()) { // Spezielle Behandlung für bos, vis, etc. } $this->substituteForms(); }JSON-Generierung
Die generierten Formen werden in ein strukturiertes Array übertragen:
phppublic function getArray() { return $this->formen; // Hierarchisches Array }Speicherung in der morph-Spalte
phppublic function writeJSON() { $Formen = $this->getArray(); $this->Nomen->morph = json_encode($Formen); $this->Nomen->save(); }Status-Update
Nach erfolgreicher Morphologisierung wird der Status automatisch auf 2 gesetzt (indirekt durch das Model-Saving-Event).
Synchronisation mit HermeneusLemmaBank
phpif ($SyncWithLemmabank) { HermeneusLemmaBank::sync($this, true); }
4. Morpher-Klassen im Detail
NomenMorpher
Konstruktor und Initialisierung
public function __construct(Nomen $Nomen)
{
$this->Nomen = $Nomen;
$this->lemma = $Nomen->lemma;
$this->stamm = $Nomen->fb_stamm;
$this->endung = substr($Nomen->lemma, -2);
$this->lastletter = substr($Nomen->lemma, -1);
$this->info = '';
$this->status = '';
$this->warning = '';
}autoMorph()-Methode
Die zentrale Entscheidungslogik basiert auf der Deklinationsklasse:
public function autoMorph()
{
switch ($this->Nomen->fb_dklasse) {
case 'a':
$this->buildNomenADeklination();
break;
case 'o':
// Neutrum-Unterscheidung
break;
case 'u':
// U-Deklination
break;
case 'e':
// E-Deklination
break;
case '3dekl':
// Komplexe konsonantische Deklination
break;
case 'indekl':
$this->buildNomenIndeklinabel();
break;
}
// Numerus-Beschränkungen
if ($this->IsPluraleTantum()) {
$this->clearSingular();
}
if ($this->IsSingulareTantum()) {
$this->clearPlural();
}
// Sonderformen substituieren
$this->substituteForms();
}Build-Methoden
Jede Deklinationsklasse hat eine eigene Build-Methode:
private function buildNomenADeklination()
{
$this->formen = [
'1_sg' => [
'1_nom' => $this->lemma,
'2_gen' => $this->stamm . 'ae',
'3_dat' => $this->stamm . 'ae',
'4_akk' => $this->stamm . 'am',
'5_vok' => $this->lemma,
'6_abl' => $this->stamm . 'a',
],
'2_pl' => [
'1_nom' => $this->stamm . 'ae',
'2_gen' => $this->stamm . 'arum',
'3_dat' => $this->stamm . 'is',
'4_akk' => $this->stamm . 'as',
'5_vok' => $this->stamm . 'ae',
'6_abl' => $this->stamm . 'is',
],
];
}Für die 3. Deklination mit ihren Untergruppen:
private function buildNomen3Deklination()
{
// Regelendungen setzen
$this->kasusendungen = array(
'sg_gen' => 'is',
'sg_dat' => 'i',
'sg_akk' => 'em',
'sg_abl' => 'e',
'pl_nom' => 'es',
'pl_gen' => 'um',
'pl_dat' => 'ibus',
'pl_akk' => 'es',
'pl_vok' => 'es',
'pl_abl' => 'ibus'
);
// Unterklasse feststellen
if ($this->IsIn3DeklNeutraVokalischGruppe1()) {
$this->kasusendungen['pl_nom'] = 'ia';
$this->kasusendungen['pl_akk'] = 'ia';
$this->kasusendungen['pl_gen'] = 'ium';
// ...
}
// Formen mit Stamm + Endungen bilden
$this->formen = [
'1_sg' => [
'1_nom' => $this->lemma,
'2_gen' => $this->stamm . $this->kasusendungen['sg_gen'],
// ...
],
// ...
];
}Sonderfälle und Ausnahmen
Der NomenMorpher kennt umfangreiche Listen von Sonderformen:
public $sonderformen = array(
'familia', 'deus', 'dea', 'filia', 'filius',
'domus', 'locus', 'Iuppiter', 'bos', 'vis'
// ...
);
public $anomalia = ['Iuppiter', 'senex', 'bos', 'suppellex', 'caro', 'nix', 'iter', 'vas'];
public $defectiva = ['vis', 'opes', 'fruges', 'preces', 'fas', 'nefas'];Behandlung von Anomalien:
if ($this->IsAnomale()) {
switch ($this->lemma) {
case 'bos':
$this->Substitutes['2_pl']['2_gen'] = 'boum';
$this->Substitutes['2_pl']['3_dat'] = 'bubus / bobus';
$this->Substitutes['2_pl']['6_abl'] = 'bubus / bobus';
break;
case 'vas':
$this->Substitutes['2_pl']['2_gen'] = 'vasorum';
$this->Substitutes['2_pl']['3_dat'] = 'vasis';
break;
}
}writeJSON() und getArray()
public function writeJSON()
{
$Formen = $this->getArray();
$this->Nomen->morph = json_encode($Formen);
$this->Nomen->save();
}
public function getArray()
{
return $this->formen;
}AdjektivMorpher
Der AdjektivMorpher ist komplexer, da er drei Steigerungsstufen morphologisiert:
Konstruktor
public function __construct(Adjektiv $Adjektiv)
{
$this->Adjektiv = $Adjektiv;
$this->komparativ = $Adjektiv->fb_komparativ;
$this->superlativ = $Adjektiv->fb_superlativ;
$this->endung_2 = substr($Adjektiv->lemma, -2);
$this->endung_3 = substr($Adjektiv->lemma, -3);
$this->endung_4 = substr($Adjektiv->lemma, -4);
$this->komparativ_stamm = substr($Adjektiv->fb_komparativ, 0, -2);
$this->superlativ_stamm = substr($Adjektiv->fb_superlativ, 0, -2);
}autoMorph() mit Positiv, Komparativ und Superlativ
public function autoMorph()
{
// POSITIV
if ($this->IsAODeklination()) {
$this->buildPositiv3endigAODeklination();
} else {
switch ($this->Adjektiv->fb_genera) {
case '3endig':
$this->buildPositiv3endigKonsDeklination();
break;
case '2endig':
$this->buildPositiv2endigKonsDeklination();
break;
case '1endig':
$this->buildPositiv1endigKonsDeklination();
break;
}
}
// KOMPARATIV
if ($this->BildetKomparativInput()) {
if ($this->HasEndungAufUs() && /* ... */) {
$this->buildKomparativAufMagis();
} else {
$this->buildKomparativNormal();
}
} else {
$this->buildKomparativEmpty();
}
// SUPERLATIV
if ($this->BildetSuperlativInput()) {
if ($this->HasEndungAufUs() && /* ... */) {
$this->buildSuperlativAufMaxime();
} else {
$this->buildSuperlativNormal();
}
} else {
$this->buildSuperlativEmpty();
}
}Komparation mit magis/maxime
private function buildKomparativAufMagis()
{
$this->adverb['2_komp'] = 'magis ' . $this->adverb['1_pos'];
$this->formen_komparativ = [
'1_sg' => [
'1_mask' => [
'1_nom' => 'magis ' . $this->formen_positiv['1_sg']['1_mask']['1_nom'],
'2_gen' => 'magis ' . $this->formen_positiv['1_sg']['1_mask']['2_gen'],
// ...
],
// ...
],
// ...
];
}getArray() kombiniert alle Steigerungsstufen
public function getArray()
{
$this->formen = [
'1_pos' => $this->formen_positiv,
'2_komp' => $this->formen_komparativ,
'3_superl' => $this->formen_superlativ,
'4_adv' => $this->adverb,
];
return $this->formen;
}VerbMorpher
Der VerbMorpher ist die mit Abstand komplexeste Klasse mit über 60.000 Zeichen Code.
Struktur-Konstanten
const FORMEN_AKTIV = [
'1_praesens' => [
'0_infinitiv' => '',
'1_indikativ' => [
'1_sg1' => '', '2_sg2' => '', '3_sg3' => '',
'4_pl1' => '', '5_pl2' => '', '6_pl3' => '',
],
'2_konjunktiv' => [ /* ... */ ],
'3_imperativ' => [
'2_sg2' => '', '5_pl2' => '',
],
'4_partizip' => [ /* vollständige Deklination */ ],
],
'2_imperfekt' => [ /* ... */ ],
'3_perfekt' => [ /* ... */ ],
'4_plusquamperfekt' => [ /* ... */ ],
'5_futur' => [ /* ... */ ],
'6_futurII' => [ /* ... */ ],
];
const FORMEN_PASSIV = [ /* ähnliche Struktur */ ];
const FORMEN_DEPONENS = [ /* kombiniert Aktiv-Formen mit Passiv-Endungen */ ];autoMorph() mit Sonderfallbehandlung
public function autoMorph()
{
if ($this->IstFormVonEsse()) {
$this->buildEsse();
} elseif ($this->Verb->lemma == 'posse') {
$this->buildPosse();
} elseif ($this->IstFormVonFerre()) {
$this->buildFerre();
} elseif ($this->IstFormVonIre()) {
$this->buildIre();
} elseif ($this->istPerfektopraesens()) {
$this->buildPerfektopraesens();
} elseif ($this->IstSemideponens()) {
$this->buildSemideponens();
} elseif ($this->IstDeponens()) {
$this->buildDeponens();
} else {
$this->buildRegularVerb();
}
}Build-Methoden für Konjugationen
private function buildVerbAKonjugation()
{
$praesens_stamm = $this->stamm; // z.B. "am" von "amare"
$perfekt_stamm = $this->Verb->fb_perfstamm; // z.B. "amav"
$ppp_stamm = $this->Verb->fb_pppstamm; // z.B. "amat"
// Präsens Indikativ Aktiv
$this->formen_aktiv['1_praesens']['1_indikativ'] = [
'1_sg1' => $praesens_stamm . 'o',
'2_sg2' => $praesens_stamm . 's',
'3_sg3' => $praesens_stamm . 't',
'4_pl1' => $praesens_stamm . 'mus',
'5_pl2' => $praesens_stamm . 'tis',
'6_pl3' => $praesens_stamm . 'nt',
];
// Präsens Konjunktiv Aktiv
$this->formen_aktiv['1_praesens']['2_konjunktiv'] = [
'1_sg1' => $praesens_stamm . 'em',
'2_sg2' => $praesens_stamm . 'es',
// ...
];
// Imperativ
$this->formen_aktiv['1_praesens']['3_imperativ'] = [
'2_sg2' => $praesens_stamm . '',
'5_pl2' => $praesens_stamm . 'te',
];
// Partizip Präsens Aktiv (PPA)
$this->buildPPA($praesens_stamm . 'ns', $praesens_stamm . 'nt');
// Perfekt, Plusquamperfekt, Futur II
$this->buildPerfektStamm($perfekt_stamm);
// PPP und PPF (Passiv-Perfekt-Formen)
$this->buildPPP($ppp_stamm);
}5. JSON-Struktur
Hierarchie-Konzept
Die morphologischen Daten werden in einer konsistenten Hierarchie gespeichert:
Ebene 1: Numerus/Steigerung/Tempus
└─ Ebene 2: Genus/Modus
└─ Ebene 3: Kasus/Person
└─ Ebene 4: Form (String)Schlüssel-System
Alle Schlüssel folgen dem Muster: [Position]_[Name]
Numeri:
1_sg= Singular2_pl= Plural
Genera:
1_mask= Maskulinum2_fem= Femininum3_neutr= Neutrum
Kasus:
1_nom= Nominativ2_gen= Genitiv3_dat= Dativ4_akk= Akkusativ5_vok= Vokativ6_abl= Ablativ
Tempora (Verb):
1_praesens2_imperfekt3_perfekt4_plusquamperfekt5_futur6_futurII
Modi (Verb):
1_indikativ2_konjunktiv3_imperativ4_partizip
Personen (Verb):
1_sg1= 1. Person Singular2_sg2= 2. Person Singular3_sg3= 3. Person Singular4_pl1= 1. Person Plural5_pl2= 2. Person Plural6_pl3= 3. Person Plural
Steigerungsstufen (Adjektiv):
1_pos= Positiv2_komp= Komparativ3_superl= Superlativ4_adv= Adverb
Genera Verbi:
1_aktiv2_passiv
Unterschiede zwischen Wortarten
Nomen: Einfache zweidimensionale Struktur
{
"1_sg": {
"1_nom": "puella",
"2_gen": "puellae",
"3_dat": "puellae",
"4_akk": "puellam",
"5_vok": "puella",
"6_abl": "puella"
},
"2_pl": {
"1_nom": "puellae",
"2_gen": "puellarum",
"3_dat": "puellis",
"4_akk": "puellas",
"5_vok": "puellae",
"6_abl": "puellis"
}
}Adjektiv: Mit drei Genera und Steigerung
{
"1_pos": {
"1_sg": {
"1_mask": {
"1_nom": "bonus",
"2_gen": "boni",
"3_dat": "bono",
"4_akk": "bonum",
"5_vok": "bone",
"6_abl": "bono"
},
"2_fem": {
"1_nom": "bona",
"2_gen": "bonae",
"3_dat": "bonae",
"4_akk": "bonam",
"5_vok": "bona",
"6_abl": "bona"
},
"3_neutr": {
"1_nom": "bonum",
"2_gen": "boni",
"3_dat": "bono",
"4_akk": "bonum",
"5_vok": "bonum",
"6_abl": "bono"
}
},
"2_pl": {
"1_mask": {
"1_nom": "boni",
"2_gen": "bonorum",
"3_dat": "bonis",
"4_akk": "bonos",
"5_vok": "boni",
"6_abl": "bonis"
},
"2_fem": {
"1_nom": "bonae",
"2_gen": "bonarum",
"3_dat": "bonis",
"4_akk": "bonas",
"5_vok": "bonae",
"6_abl": "bonis"
},
"3_neutr": {
"1_nom": "bona",
"2_gen": "bonorum",
"3_dat": "bonis",
"4_akk": "bona",
"5_vok": "bona",
"6_abl": "bonis"
}
}
},
"2_komp": {
"1_sg": {
"1_mask": {
"1_nom": "melior",
"2_gen": "melioris",
"3_dat": "meliori",
"4_akk": "meliorem",
"5_vok": "melior",
"6_abl": "meliore"
},
"2_fem": {
"1_nom": "melior",
"2_gen": "melioris",
"3_dat": "meliori",
"4_akk": "meliorem",
"5_vok": "melioris",
"6_abl": "meliore"
},
"3_neutr": {
"1_nom": "melius",
"2_gen": "melioris",
"3_dat": "meliori",
"4_akk": "melius",
"5_vok": "melius",
"6_abl": "meliore"
}
},
"2_pl": {
"1_mask": {
"1_nom": "meliores",
"2_gen": "meliorum",
"3_dat": "melioribus",
"4_akk": "meliores",
"5_vok": "meliores",
"6_abl": "melioribus"
},
"2_fem": {
"1_nom": "meliores",
"2_gen": "meliorum",
"3_dat": "melioribus",
"4_akk": "meliores",
"5_vok": "meliores",
"6_abl": "melioribus"
},
"3_neutr": {
"1_nom": "meliora",
"2_gen": "meliorum",
"3_dat": "melioribus",
"4_akk": "meliora",
"5_vok": "meliora",
"6_abl": "melioribus"
}
}
},
"3_superl": {
"1_sg": {
"1_mask": {
"1_nom": "optimus",
"2_gen": "optimi",
"3_dat": "optimo",
"4_akk": "optimum",
"5_vok": "optime",
"6_abl": "optimo"
},
"2_fem": { "..." },
"3_neutr": { "..." }
},
"2_pl": { "..." }
},
"4_adv": {
"1_pos": "bene",
"2_komp": "melius",
"3_superl": "optime"
}
}Verb: Hochkomplexe Struktur mit Tempus, Modus, Genus Verbi
{
"1_aktiv": {
"1_praesens": {
"0_infinitiv": "amare",
"1_indikativ": {
"1_sg1": "amo",
"2_sg2": "amas",
"3_sg3": "amat",
"4_pl1": "amamus",
"5_pl2": "amatis",
"6_pl3": "amant"
},
"2_konjunktiv": {
"1_sg1": "amem",
"2_sg2": "ames",
"3_sg3": "amet",
"4_pl1": "amemus",
"5_pl2": "ametis",
"6_pl3": "ament"
},
"3_imperativ": {
"2_sg2": "ama",
"5_pl2": "amate"
},
"4_partizip": {
"1_sg": {
"1_mask": {
"1_nom": "amans",
"2_gen": "amantis",
"3_dat": "amanti",
"4_akk": "amantem",
"5_vok": "amans",
"6_abl": "amante"
},
"2_fem": { "..." },
"3_neutr": { "..." }
},
"2_pl": { "..." }
}
},
"2_imperfekt": {
"1_indikativ": {
"1_sg1": "amabam",
"2_sg2": "amabas",
"3_sg3": "amabat",
"4_pl1": "amabamus",
"5_pl2": "amabatis",
"6_pl3": "amabant"
},
"2_konjunktiv": {
"1_sg1": "amarem",
"2_sg2": "amares",
"3_sg3": "amaret",
"4_pl1": "amaremus",
"5_pl2": "amaretis",
"6_pl3": "amarent"
}
},
"3_perfekt": {
"0_infinitiv": "amavisse",
"1_indikativ": {
"1_sg1": "amavi",
"2_sg2": "amavisti",
"3_sg3": "amavit",
"4_pl1": "amavimus",
"5_pl2": "amavistis",
"6_pl3": "amaverunt"
},
"2_konjunktiv": {
"1_sg1": "amaverim",
"2_sg2": "amaveris",
"3_sg3": "amaverit",
"4_pl1": "amaverimus",
"5_pl2": "amaveritis",
"6_pl3": "amaverint"
}
},
"4_plusquamperfekt": { "..." },
"5_futur": {
"1_indikativ": {
"1_sg1": "amabo",
"2_sg2": "amabis",
"3_sg3": "amabit",
"4_pl1": "amabimus",
"5_pl2": "amabitis",
"6_pl3": "amabunt"
},
"3_imperativ": {
"2_sg2": "amato",
"3_sg3": "amato",
"5_pl2": "amatote",
"6_pl3": "amanto"
},
"4_partizip": {
"1_sg": {
"1_mask": {
"1_nom": "amaturus",
"2_gen": "amaturi",
"..." : "..."
},
"2_fem": { "..." },
"3_neutr": { "..." }
},
"2_pl": { "..." }
}
},
"6_futurII": { "..." }
},
"2_passiv": {
"1_praesens": {
"0_infinitiv": "amari",
"1_indikativ": {
"1_sg1": "amor",
"2_sg2": "amaris",
"3_sg3": "amatur",
"4_pl1": "amamur",
"5_pl2": "amamini",
"6_pl3": "amantur"
},
"2_konjunktiv": { "..." },
"3_imperativ": {
"2_sg2": "amare",
"5_pl2": "amamini"
}
},
"2_imperfekt": { "..." },
"3_perfekt": {
"0_infinitiv": "amatus esse",
"1_indikativ": {
"1_sg1": "amatus sum",
"2_sg2": "amatus es",
"..." : "..."
},
"2_konjunktiv": { "..." },
"4_partizip": {
"1_sg": {
"1_mask": {
"1_nom": "amatus",
"2_gen": "amati",
"..." : "..."
},
"2_fem": { "..." },
"3_neutr": { "..." }
},
"2_pl": { "..." }
}
},
"4_plusquamperfekt": { "..." },
"5_futur": { "..." },
"6_futurII": { "..." }
},
"3_gerundium": {
"2_gen": "amandi",
"3_dat": "amando",
"4_akk": "amandum",
"6_abl": "amando"
},
"4_gerundivum": {
"1_sg": {
"1_mask": {
"1_nom": "amandus",
"..." : "..."
},
"2_fem": { "..." },
"3_neutr": { "..." }
},
"2_pl": { "..." }
},
"5_supinum": {
"1_supI": "amatum",
"2_supII": "amatu"
}
}6. Morphologie-Modi
Automatisch (morph_mode = 0)
Der Standardmodus. Das System generiert alle Formen vollautomatisch basierend auf:
- Lemma
- Stamm (fb_stamm)
- Deklinationsklasse (fb_dklasse) / Konjugationsklasse (fb_kklasse)
- Genus (fb_genus)
- Weitere linguistische Angaben
Verwendung:
$nomen = Nomen::create([
'lemma' => 'dominus',
'fb_stamm' => 'domin',
'fb_dklasse' => 'o',
'fb_genus' => 'm',
'morph_mode' => 0, // Standard
'bedeutung' => 'Herr'
]);
$nomen->morph();Manuell (morph_mode = 1)
Für Sonderformen, die nicht den Regeln folgen oder für die der Algorithmus keine korrekte Lösung generiert.
Bei manuellem Modus:
autoMorph()wird übersprungen- Formen müssen manuell im Frontend eingegeben werden
- JSON wird direkt gespeichert ohne Regelanwendung
Verwendung:
$nomen = Nomen::create([
'lemma' => 'vis',
'morph_mode' => 1, // Manuell
'bedeutung' => 'Kraft, Gewalt'
]);
// Formen werden manuell über das Frontend eingetragenSonderformen
Einige Lemmata sind als bekannte Sonderformen hinterlegt:
public $sonderformen = array(
'familia', 'deus', 'dea', 'filia', 'filius',
'domus', 'locus', 'Iuppiter', 'bos', 'vis'
);Diese werden automatisch erkannt und erhalten Spezialbehandlung innerhalb von autoMorph().
Wann welcher Modus?
Automatisch verwenden bei:
- Regulären Vokabeln, die den Deklinationsregeln folgen
- Häufigen Vokabeln mit bekannten Mustern
- Produktiven Wortbildungen
Manuell verwenden bei:
- Stark defektiven Vokabeln (z.B. "opes" - nur bestimmte Formen existieren)
- Anomalien mit unvorhersehbaren Formen
- Rekonstruierten oder seltenen Formen
- Wenn der Algorithmus nachweislich falsche Formen generiert
Sonderformen-Automatik nutzen bei:
- Bekannten Anomalien wie "domus", "Iuppiter", "bos"
- Frequenten irregulären Vokabeln
- Lemmata mit dokumentierten Besonderheiten
7. Status-Verwaltung
Das Morphologie-System nutzt ein dreistufiges Status-System:
Status 0: Unmorphologisiert
Bedeutung: Das Lemma wurde erstellt, aber noch nicht morphologisiert.
Eigenschaften:
morph-Spalte istNULLoder leer- Keine Formen verfügbar
- Lemma kann bearbeitet werden
Übergang zu Status 1: Durch Aufruf von $lemma->morph()
Status 1: In Bearbeitung
Bedeutung: Die Morphologisierung läuft gerade oder ist fehlgeschlagen.
Eigenschaften:
- Wird zu Beginn von
morph()gesetzt - Signalisiert, dass das System gerade arbeitet
- Bei Fehler bleibt Status 1 erhalten (als Warnsignal)
Code:
public function morph(bool $SyncWithLemmabank = true): void
{
try {
$this->update(['status' => 1]); // Statuswechsel zu "in Bearbeitung"
$VocabMorpher = new ($this::MORPHER)($this);
$VocabMorpher->autoMorph();
$VocabMorpher->writeJSON();
// Status wird beim Speichern automatisch auf 2 gesetzt
} catch (\Exception $exception) {
// Status bleibt 1 (Fehler)
Log::error("Morphologisierung fehlgeschlagen");
}
}Status 2: Fertig morphologisiert
Bedeutung: Die Morphologisierung war erfolgreich, alle Formen sind verfügbar.
Eigenschaften:
morph-Spalte enthält vollständiges JSON- Alle Formen abrufbar
- Lemma kann weiterverwendet werden
Automatischer Übergang: Der Status wird nicht explizit im Code auf 2 gesetzt, sondern erfolgt durch ein Model-Event (Observer oder Mutator), das beim Speichern des morph-Attributs ausgelöst wird.
Statusübergänge
┌─────────────┐
│ Status 0 │ Neu erstellt
│ Unmorphol. │
└──────┬──────┘
│ $lemma->morph()
▼
┌─────────────┐
│ Status 1 │ Morphologisierung läuft
│ In Bearb. │
└──────┬──────┘
│ writeJSON() erfolgreich
▼
┌─────────────┐
│ Status 2 │ Fertig
│ Morphol. │
└─────────────┘
│
│ Bei Fehler: bleibt Status 1
▼
┌─────────────┐
│ Status 1 │ Fehler! Erneut versuchen
│ Fehler │ oder manuell korrigieren
└─────────────┘8. Besonderheiten
Alternativformen im JSON (mit " / ")
Viele lateinische Wörter haben alternative Formen. Diese werden im JSON mit " / " getrennt gespeichert:
// Beispiel: bos (Rind)
$this->Substitutes['2_pl']['3_dat'] = 'bubus / bobus';
$this->Substitutes['2_pl']['6_abl'] = 'bubus / bobus';Resultierendes JSON:
{
"2_pl": {
"3_dat": "bubus / bobus",
"6_abl": "bubus / bobus"
}
}Frontend-Verarbeitung: Bei der Anzeige oder Auswertung werden diese Alternativen mit .split(' / ') getrennt.
Formverweise vs. Inline-Alternativen
Inline-Alternativen: Mehrere gleichwertige Formen im selben Feld (siehe oben).
Formverweise (bei Verben): Bei zusammengesetzten Formen (z.B. Passiv-Perfekt) wird nur das Partizip gespeichert, die esse-Formen werden zur Laufzeit ergänzt:
{
"2_passiv": {
"3_perfekt": {
"1_indikativ": {
"1_sg1": "amatus sum",
"2_sg2": "amatus es",
"3_sg3": "amatus est"
}
}
}
}Hier wird "amatus" + konjugierte Form von "esse" kombiniert.
Sonderformen und hardcodierte Listen
Der NomenMorpher kennt mehrere Listen:
public $dritte_dekl_genitiv_ium = array(
'ovis', 'hostis', 'civis', 'navis', 'avis',
'nubes', 'mons', 'pons', 'mors', 'piscis',
'mortalis', 'orbis', 'urbs', 'axis', 'vulpes',
'pars', 'parens', 'os', 'imber', 'venter',
'testis', 'ignis'
);
public $dritte_dekl_vokalisch_gruppe2 = array(
'turris', 'febris', 'puppis', 'securis',
'sitis', 'Tiberis', 'Neapolis'
);Diese steuern, welche Endungen verwendet werden:
if ($this->HasGenitivAufIum()) {
$this->kasusendungen['pl_gen'] = 'ium'; // statt 'um'
}Anomalien (z.B. bei Nomina)
Anomale Nomina folgen keiner regulären Deklination. Sie erhalten Spezialbehandlung:
if ($this->IsAnomale()) {
switch ($this->lemma) {
case 'Iuppiter':
$this->clearPlural(); // Nur Singular
break;
case 'bos':
$this->Substitutes['2_pl']['2_gen'] = 'boum';
$this->Substitutes['2_pl']['3_dat'] = 'bubus / bobus';
$this->Substitutes['2_pl']['6_abl'] = 'bubus / bobus';
break;
case 'supellex':
$this->clearPlural();
break;
case 'vas':
$this->Substitutes['2_pl']['2_gen'] = 'vasorum';
$this->Substitutes['2_pl']['3_dat'] = 'vasis';
$this->Substitutes['2_pl']['6_abl'] = 'vasis';
break;
}
}Defektiva
Defektive Verben oder Nomina existieren nur in bestimmten Formen:
public $defectiva = ['vis', 'opes', 'fruges', 'preces', 'fas', 'nefas'];
if ($this->IsDefectivum()) {
switch ($this->lemma) {
case 'vis':
$this->Substitutes['1_sg']['2_gen'] = ''; // Kein Genitiv Sg.
$this->Substitutes['1_sg']['3_dat'] = ''; // Kein Dativ Sg.
$this->Substitutes['1_sg']['4_akk'] = 'vim';
$this->Substitutes['1_sg']['6_abl'] = 'vi';
$this->Substitutes['2_pl']['2_gen'] = 'virium';
break;
case 'opes':
$this->Substitutes['1_sg']['1_nom'] = ''; // Kein Nominativ Sg.
$this->Substitutes['1_sg']['2_gen'] = 'opis';
$this->Substitutes['1_sg']['3_dat'] = '';
$this->Substitutes['1_sg']['4_akk'] = 'opem';
$this->Substitutes['1_sg']['6_abl'] = 'ope';
$this->Substitutes['2_pl']['2_gen'] = 'opum';
break;
}
}Leere Strings signalisieren "Form existiert nicht".
9. RequiredFields
Wie werden erforderliche Felder definiert?
Jedes morphologisierbare Model definiert ein $RequiredFields-Array:
// Nomen.php
public array $RequiredFields = [
'lemma',
'fb_stamm',
'fb_dklasse',
'fb_genus',
'bedeutung'
];
// Verb.php
public array $RequiredFields = [
'lemma',
'fb_1pssgpr',
'fb_kklasse',
'fb_praesens_stamm',
'fb_perfekt_stamm',
'fb_ppp_stamm',
'bedeutung'
];
// Adjektiv.php
public array $RequiredFields = [
'lemma',
'fb_stamm',
'fb_dklasse',
'fb_genera',
'fb_komparativ',
'fb_superlativ',
'bedeutung'
];Validierung vor Morphologie
Die IsValid()-Methode prüft, ob alle erforderlichen Felder ausgefüllt sind:
public function IsValid(): bool
{
if (
!empty($this->lemma) &&
!empty($this->fb_stamm) &&
!empty($this->fb_dklasse) &&
!empty($this->fb_genus) &&
!empty($this->bedeutung)
) {
return true;
}
return false;
}Verwendung vor Morphologisierung:
$nomen = Nomen::find($id);
if (!$nomen->IsValid()) {
return response()->json([
'error' => 'Nicht alle erforderlichen Felder sind ausgefüllt.',
'required_fields' => $nomen->RequiredFields
], 422);
}
$nomen->morph();IsValid()-Methoden
Jede morphologisierbare Wortart implementiert IsValid() individuell:
Verb:
public function IsValid(): bool
{
if (
!empty($this->lemma) &&
!empty($this->fb_1pssgpr) &&
!empty($this->fb_kklasse) &&
!empty($this->fb_praesens_stamm) &&
!empty($this->fb_perfekt_stamm) &&
!empty($this->fb_ppp_stamm) &&
!empty($this->bedeutung)
) {
return true;
}
return false;
}Adjektiv:
public function IsValid(): bool
{
if (
!empty($this->lemma) &&
!empty($this->fb_stamm) &&
!empty($this->fb_dklasse) &&
!empty($this->fb_genera) &&
!empty($this->bedeutung)
) {
// Komparativ und Superlativ sind optional
return true;
}
return false;
}10. MorphInfo und Debugging
MorphInfoHandler-Klassen
MorphInfoHandler liefern detaillierte Informationen über den Morphologisierungsprozess. Sie sind primär für Entwickler und erweiterte Benutzer gedacht.
Struktur:
class NomenMorphInfoHandler
{
public $info;
public $status;
public $warning;
public function __construct(NomenMorpher $MorphingNomen)
{
$this->MorphingNomen = $MorphingNomen;
$this->Nomen = $MorphingNomen->Nomen;
}
public function getMorphInfo()
{
$MorphInfo = array();
$info = array();
$status = array();
$warning = array();
// Analyse durchführen
switch ($this->MorphingNomen->Nomen->fb_dklasse) {
case 'a':
$info[] = 'A-Deklination';
break;
case '3dekl':
$info[] = 'Konsonantische Deklination: ';
if ($this->MorphingNomen->IsIn3DeklNeutraVokalischGruppe1()) {
$info[] = 'Vokalischer Stamm (Gruppe 1: Neutrum) nach RH §38.1 erkannt!';
}
break;
}
if ($this->MorphingNomen->IsPluraleTantum()) {
$status[] = 'Plurale tantum: Kein Singular gebildet!';
}
if ($this->MorphingNomen->IsAnomale()) {
$info[] = 'Verbum Anomale nach RB §41,1 erkannt!';
}
$MorphInfo['getInfo'] = $info;
$MorphInfo['status'] = $status;
$MorphInfo['warning'] = $warning;
return $MorphInfo;
}
}morphInfo()-Methode
Aufruf über das Model:
$nomen = Nomen::find($id);
$morphInfo = $nomen->morphInfo();
// Response-Struktur:
[
'lemma' => 'mare',
'info' => [
'Konsonantische Deklination',
'Vokalischer Stamm (Gruppe 1: Neutrum) nach RH §38.1 erkannt!'
],
'status' => [
'Neutrum: Akkusativ = Nominativ'
],
'warning' => []
]Frontend-Integration:
haxiosAPIClient.get(`/glossarium/nomina/${id}/morph-info`)
.then(response => {
console.log('Morphologie-Infos:', response.data.info);
console.log('Status:', response.data.status);
console.log('Warnungen:', response.data.warning);
});Fehlerbehandlung und Logging
In der morph()-Methode:
try {
$this->update(['status' => 1]);
$VocabMorpher = new ($this::MORPHER)($this);
$VocabMorpher->autoMorph();
$VocabMorpher->writeJSON();
if ($SyncWithLemmabank) {
HermeneusLemmaBank::sync($this, true);
}
} catch (\Exception $exception) {
if (app()->environment(['local', 'testing', 'development'])) {
dd($exception->getMessage() . "\n" . $exception->getTraceAsString());
}
Log::error("Vokabel $this->id ($this->wortart) konnte nicht morphologisiert werden!: \n"
. $exception->getMessage());
}Log-Einträge finden:
tail -f storage/logs/laravel.log | grep "morphologisiert"GlossariumReporter
Der GlossariumReporter (app/ServiceClasses/Glossarium/GlossariumReporter.php) sammelt systematisch Fehler und Probleme:
use App\ServiceClasses\Glossarium\GlossariumReporter;
// In Morpher-Klassen:
GlossariumReporter::reportMorphError($this->Nomen, 'Unbekannte Deklinationsklasse');11. Verwendungsbeispiele
Neues Lemma erstellen und morphologisieren
use App\Models\Nomen;
// Nomen erstellen
$nomen = Nomen::create([
'lemma' => 'amicus',
'fb_stamm' => 'amic',
'fb_dklasse' => 'o',
'fb_genus' => 'm',
'bedeutung' => 'Freund'
]);
// Validierung
if (!$nomen->IsValid()) {
throw new \Exception('Erforderliche Felder fehlen: '
. implode(', ', $nomen->RequiredFields));
}
// Morphologisierung durchführen
$nomen->morph();
// Erfolgskontrolle
if ($nomen->status === 2) {
echo "Morphologisierung erfolgreich!";
echo "Genitiv Singular: " . json_decode($nomen->morph, true)['1_sg']['2_gen'];
} else {
echo "Morphologisierung fehlgeschlagen!";
}Zugriff auf morphologische Daten
$nomen = Nomen::find($id);
// JSON dekodieren
$formen = json_decode($nomen->morph, true);
// Spezifische Form abrufen
$genitivSingular = $formen['1_sg']['2_gen'];
$dativPlural = $formen['2_pl']['3_dat'];
// Alle Singularformen
foreach ($formen['1_sg'] as $kasus => $form) {
echo "$kasus: $form\n";
}
// Alle Formen eines Kasus
$nominative = [
'Sg.' => $formen['1_sg']['1_nom'],
'Pl.' => $formen['2_pl']['1_nom']
];Alle Formen abrufen
Der HasAlleFormenAttribute-Trait stellt ein virtuelles alle_formen-Attribut bereit:
$nomen = Nomen::find($id);
// Alle Formen als Array
$alleForms = $nomen->alle_formen;
// Beispiel-Struktur:
[
'Nominativ Sg.' => 'amicus',
'Genitiv Sg.' => 'amici',
'Dativ Sg.' => 'amico',
// ...
'Nominativ Pl.' => 'amici',
'Genitiv Pl.' => 'amicorum',
// ...
]Verwendung im Frontend:
haxiosAPIClient.get(`/glossarium/nomina/${id}`)
.then(response => {
const formen = JSON.parse(response.data.morph);
// Tabelle erstellen
Object.entries(formen['1_sg']).forEach(([kasus, form]) => {
console.log(`${kasus}: ${form}`);
});
});Fehlerbehandlung
try {
$nomen = Nomen::create([
'lemma' => 'testword',
'fb_stamm' => 'testwor',
'fb_dklasse' => 'invalid', // Ungültige Deklinationsklasse
'fb_genus' => 'm',
'bedeutung' => 'Testwort'
]);
$nomen->morph();
} catch (\Exception $e) {
Log::error('Morphologisierung fehlgeschlagen: ' . $e->getMessage());
// Benutzer-Feedback
return response()->json([
'error' => 'Die Vokabel konnte nicht morphologisiert werden.',
'details' => $e->getMessage()
], 500);
}
// Status prüfen
if ($nomen->status === 1) {
// Morphologisierung ist fehlgeschlagen oder läuft noch
$morphInfo = $nomen->morphInfo();
return response()->json([
'error' => 'Morphologisierung unvollständig',
'info' => $morphInfo
], 422);
}Performance-Optimierung
// SCHLECHT: Lädt alle morph-Daten unnötig
$nomina = Nomen::all();
foreach ($nomina as $nomen) {
echo $nomen->lemma;
}
// GUT: Exkludiert morph-Spalte
$nomina = Nomen::scopeExclude(['morph'])->get();
foreach ($nomina as $nomen) {
echo $nomen->lemma;
}
// Nur wenn morph wirklich benötigt wird
$nomen = Nomen::find($id); // Inkludiert morph
$formen = json_decode($nomen->morph, true);12. Performance und Best Practices
scopeExclude(['morph'])
Die morph-Spalte enthält sehr große JSON-Strings (bis zu 50 KB bei Verben). Wenn diese Daten nicht benötigt werden, sollten sie exkludiert werden:
// In Model-Klasse definiert:
public function scopeExclude($query, $columns)
{
return $query->select(array_diff($this->getTableColumns(), (array) $columns));
}
// Verwendung:
$nomina = Nomen::scopeExclude(['morph'])->get();
$nomina = Nomen::scopeExclude(['morph', 'deleted_at'])->paginate(50);Wann verwenden?
- Listen-Ansichten (Index-Seiten)
- Suchergebnisse
- Dropdown-Optionen
- Jeder Kontext, wo nur Lemma/Bedeutung benötigt wird
Wann morph-Daten laden, wann nicht?
Laden:
- Detail-Ansicht einer Vokabel
- Formabfrage-Tools
- Export-Funktionen
- Übungsgenerierung
Nicht laden:
- Listen-Übersichten
- Autocomplete-Suche
- Navigation
- Statistiken
Intelligentes Lazy Loading:
// Controller
public function index()
{
return Nomen::scopeExclude(['morph'])->paginate(50);
}
public function show($id)
{
return Nomen::findOrFail($id); // Mit morph
}
public function forms($id)
{
$nomen = Nomen::select('id', 'lemma', 'morph')->findOrFail($id);
return json_decode($nomen->morph, true);
}Caching-Strategien
Für häufig abgefragte Formen:
use Illuminate\Support\Facades\Cache;
public function getCachedMorph($nomenId)
{
$cacheKey = "nomen_morph_{$nomenId}";
return Cache::remember($cacheKey, 3600, function () use ($nomenId) {
$nomen = Nomen::find($nomenId);
return json_decode($nomen->morph, true);
});
}
// Cache invalidieren bei Update
public function updateNomen($id, $data)
{
$nomen = Nomen::find($id);
$nomen->update($data);
$nomen->morph(); // Neu morphologisieren
Cache::forget("nomen_morph_{$id}"); // Cache löschen
}Datenbank-Queries optimieren
Problem:
// N+1-Problem
$lerneinheit = Lerneinheit::find($id);
foreach ($lerneinheit->nomina as $nomen) {
echo json_decode($nomen->morph, true)['1_sg']['1_nom'];
}Lösung:
// Eager Loading mit Select
$lerneinheit = Lerneinheit::with(['nomina' => function($query) {
$query->select('id', 'lemma', 'morph');
}])->find($id);
foreach ($lerneinheit->nomina as $nomen) {
echo json_decode($nomen->morph, true)['1_sg']['1_nom'];
}Bulk-Morphologisierung:
// Mehrere Vokabeln auf einmal morphologisieren
$nomina = Nomen::where('status', 0)->limit(100)->get();
foreach ($nomina as $nomen) {
if ($nomen->IsValid()) {
try {
$nomen->morph(false); // Ohne Lemmabank-Sync
} catch (\Exception $e) {
Log::error("Nomen {$nomen->id} failed: " . $e->getMessage());
}
}
}
// Lemmabank-Sync einmal am Ende
HermeneusLemmaBank::syncAll();Weitere Best Practices
Immer IsValid() prüfen vor morph()
phpif ($nomen->IsValid()) { $nomen->morph(); }Status-Checks nach Morphologisierung
php$nomen->morph(); if ($nomen->fresh()->status !== 2) { // Fehlerbehandlung }Logging bei Bulk-Operationen
phpLog::info("Starte Morphologisierung von {$count} Vokabeln"); // ... Morphologisierung Log::info("Morphologisierung abgeschlossen: {$success} erfolgreich, {$failed} fehlgeschlagen");Transaktionen bei kritischen Operationen
phpDB::transaction(function () use ($nomen) { $nomen->morph(); HermeneusLemmaBank::sync($nomen); });JSON-Validierung nach writeJSON()
php$formen = json_decode($nomen->morph, true); if (json_last_error() !== JSON_ERROR_NONE) { throw new \Exception('Ungültiges JSON in morph-Spalte'); }
Querverweise
Wortart-Dokumentationen:
Verwandte Systeme:
- Glossarium-Übersicht
- HermeneusLemmaBank (zu dokumentieren)
- GlossariumReporter (zu dokumentieren)
Literatur und Referenzen
RH = Rubenbauer-Hofmann: Lateinische Grammatik RB = Rubenbauer: Lateinische Grammatik (ältere Ausgabe)
Die Morphologie-Implementierung orientiert sich an wissenschaftlich anerkannten lateinischen Grammatiken und berücksichtigt sowohl klassische als auch nachklassische Formen.