Skip to content

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:

php
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 Eigennamen
  • AdjektivMorpher.php - Dekliniert Adjektive und bildet Komparation
  • VerbMorpher.php - Konjugiert Verben (komplexeste Klasse mit ca. 2000 Zeilen)
  • PronomenMorpher.php - Dekliniert Pronomina
  • NumeraleMorpher.php - Dekliniert Numeralia

Jeder Morpher besitzt:

  • Konstruktor zur Initialisierung
  • autoMorph()-Methode mit Regellogik
  • Build-Methoden für spezifische Paradigmen
  • writeJSON()-Methode zum Speichern
  • getArray()-Methode zur Datenausgabe

MorphInfoHandler-Klassen

Diese Klassen liefern benutzerfreundliche Informationen über den Morphologisierungsprozess:

  • NomenMorphInfoHandler.php
  • AdjektivMorphInfoHandler.php
  • VerbMorphInfoHandler.php
  • PronomenMorphInfoHandler.php
  • NumeraleMorphInfoHandler.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 NomenMorphInfoHandler

3. Der Morphologie-Workflow

Schritt-für-Schritt-Ablauf

  1. Erstellung eines Lemmas mit linguistischen Angaben

    php
    $nomen = Nomen::create([
        'lemma' => 'puella',
        'fb_stamm' => 'puell',
        'fb_dklasse' => 'a',
        'fb_genus' => 'f',
        'bedeutung' => 'Mädchen'
    ]);
  2. Validierung der erforderlichen Felder

    php
    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;
    }
  3. Aufruf der morph()-Methode

    php
    $nomen->morph(); // Mit LemmaBank-Sync
    // oder
    $nomen->morph(false); // Ohne LemmaBank-Sync
  4. Instantiierung des Morphers

    php
    $VocabMorpher = new ($this::MORPHER)($this);
    // Dynamische Instantiierung basierend auf Klassen-Konstante
  5. autoMorph()-Methode und Regelanwendung

    Der Morpher analysiert die linguistischen Angaben und wendet die entsprechenden Regeln an:

    php
    public 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();
    }
  6. JSON-Generierung

    Die generierten Formen werden in ein strukturiertes Array übertragen:

    php
    public function getArray()
    {
        return $this->formen; // Hierarchisches Array
    }
  7. Speicherung in der morph-Spalte

    php
    public function writeJSON()
    {
        $Formen = $this->getArray();
        $this->Nomen->morph = json_encode($Formen);
        $this->Nomen->save();
    }
  8. Status-Update

    Nach erfolgreicher Morphologisierung wird der Status automatisch auf 2 gesetzt (indirekt durch das Model-Saving-Event).

  9. Synchronisation mit HermeneusLemmaBank

    php
    if ($SyncWithLemmabank) {
        HermeneusLemmaBank::sync($this, true);
    }

4. Morpher-Klassen im Detail

NomenMorpher

Konstruktor und Initialisierung

php
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:

php
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:

php
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:

php
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:

php
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:

php
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()

php
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

php
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

php
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

php
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

php
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

php
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

php
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

php
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 = Singular
  • 2_pl = Plural

Genera:

  • 1_mask = Maskulinum
  • 2_fem = Femininum
  • 3_neutr = Neutrum

Kasus:

  • 1_nom = Nominativ
  • 2_gen = Genitiv
  • 3_dat = Dativ
  • 4_akk = Akkusativ
  • 5_vok = Vokativ
  • 6_abl = Ablativ

Tempora (Verb):

  • 1_praesens
  • 2_imperfekt
  • 3_perfekt
  • 4_plusquamperfekt
  • 5_futur
  • 6_futurII

Modi (Verb):

  • 1_indikativ
  • 2_konjunktiv
  • 3_imperativ
  • 4_partizip

Personen (Verb):

  • 1_sg1 = 1. Person Singular
  • 2_sg2 = 2. Person Singular
  • 3_sg3 = 3. Person Singular
  • 4_pl1 = 1. Person Plural
  • 5_pl2 = 2. Person Plural
  • 6_pl3 = 3. Person Plural

Steigerungsstufen (Adjektiv):

  • 1_pos = Positiv
  • 2_komp = Komparativ
  • 3_superl = Superlativ
  • 4_adv = Adverb

Genera Verbi:

  • 1_aktiv
  • 2_passiv

Unterschiede zwischen Wortarten

Nomen: Einfache zweidimensionale Struktur

json
{
  "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

json
{
  "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

json
{
  "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:

php
$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:

php
$nomen = Nomen::create([
    'lemma' => 'vis',
    'morph_mode' => 1, // Manuell
    'bedeutung' => 'Kraft, Gewalt'
]);
// Formen werden manuell über das Frontend eingetragen

Sonderformen

Einige Lemmata sind als bekannte Sonderformen hinterlegt:

php
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 ist NULL oder 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:

php
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:

php
// Beispiel: bos (Rind)
$this->Substitutes['2_pl']['3_dat'] = 'bubus / bobus';
$this->Substitutes['2_pl']['6_abl'] = 'bubus / bobus';

Resultierendes JSON:

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:

json
{
  "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:

php
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:

php
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:

php
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:

php
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:

php
// 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:

php
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:

php
$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:

php
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:

php
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:

php
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:

php
$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:

javascript
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:

php
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:

bash
tail -f storage/logs/laravel.log | grep "morphologisiert"

GlossariumReporter

Der GlossariumReporter (app/ServiceClasses/Glossarium/GlossariumReporter.php) sammelt systematisch Fehler und Probleme:

php
use App\ServiceClasses\Glossarium\GlossariumReporter;

// In Morpher-Klassen:
GlossariumReporter::reportMorphError($this->Nomen, 'Unbekannte Deklinationsklasse');

11. Verwendungsbeispiele

Neues Lemma erstellen und morphologisieren

php
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

php
$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:

php
$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:

javascript
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

php
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

php
// 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:

php
// 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:

php
// 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:

php
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:

php
// N+1-Problem
$lerneinheit = Lerneinheit::find($id);
foreach ($lerneinheit->nomina as $nomen) {
    echo json_decode($nomen->morph, true)['1_sg']['1_nom'];
}

Lösung:

php
// 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:

php
// 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

  1. Immer IsValid() prüfen vor morph()

    php
    if ($nomen->IsValid()) {
        $nomen->morph();
    }
  2. Status-Checks nach Morphologisierung

    php
    $nomen->morph();
    if ($nomen->fresh()->status !== 2) {
        // Fehlerbehandlung
    }
  3. Logging bei Bulk-Operationen

    php
    Log::info("Starte Morphologisierung von {$count} Vokabeln");
    // ... Morphologisierung
    Log::info("Morphologisierung abgeschlossen: {$success} erfolgreich, {$failed} fehlgeschlagen");
  4. Transaktionen bei kritischen Operationen

    php
    DB::transaction(function () use ($nomen) {
        $nomen->morph();
        HermeneusLemmaBank::sync($nomen);
    });
  5. 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

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.