# Projektstruktur

> Verzeichnislayout, Abhängigkeiten und Datenbank-Schema

---

## Verzeichnislayout

```
linkenhancer/
├── module.php            ← webtrees-Entry-Point
├── autoload.php          ← PSR-4-Autoloader
├── cron-jobs.php         ← Self-Registration-Manifest für cronjob
├── latest-version.txt    ← aktuelle Version (mit LinkEnhancerModule.php synchron)
├── composer.json         ← PHP-Abhängigkeiten (wt-shared-libs, phpstan)
├── package.json          ← JS-Abhängigkeiten (gulp, rollup, mocha)
├── gulpfile.mjs          ← Build-Tasks
├── phpstan.neon          ← Konfiguration der statischen Analyse
├── phpstan-bootstrap.php ← PSR-4-Bootstrap für PHPStan
├── phpstan-baseline.neon ← bekannte Probleme
├── src/                  ← PHP (PSR-4)
│   ├── LinkEnhancerModule.php
│   ├── LinkEnhancerModuleExt.php
│   ├── LinkEnhancerUtils.php
│   ├── SettingInterface.php
│   ├── CommonMark/       ← CommonMark-Extensionen
│   ├── DataFix/          ← Datenkorrektur-Handler + Dispatcher
│   ├── Factories/        ← CustomMarkdownFactory
│   ├── Http/             ← Request-Handler
│   ├── Schema/           ← Migrations + SeedHelpTable
│   └── Services/         ← Geschäftslogik
├── resources/
│   ├── js/               ← JS-Quellcode (src/) + Build-Ausgabe (bundle-*)
│   ├── css/              ← CSS-Quellcode + Build-Ausgabe
│   ├── lang/             ← .pot / .po / .php (Übersetzungen)
│   ├── views/            ← PHTML-Views
│   └── img/              ← Screenshots
├── cli/                  ← CLI-Wartungsskripte
├── tests/                ← Standalone- + Smoke-Tests
└── util/                 ← Build-/Wartungs-Shell-Skripte
```

## Abhängigkeiten

- **PHP** (<a href="https://codeberg.org/bschwede/linkenhancer/src/branch/main/composer.json" target="_blank" rel="noopener"><code>composer.json</code></a>
):
  - `bschwede/wt-shared-libs` (Path-Repo `../wt-shared-libs`)
  - [`phpstan`](./phpstan) (dev).
  - Konflikte mit:
    - `webtrees <2.2` und
    - [`elysch/webtrees-mitalteli-misc-features`](https://github.com/elysch/webtrees-mitalteli-misc-features)
- **JS** (<a href="https://codeberg.org/bschwede/linkenhancer/src/branch/main/package.json" target="_blank" rel="noopener"><code>package.json</code></a>
):
  - Laufzeit-Abhängigkeit [`tiny-markdown-editor`](<https://github.com/jefago/tiny-markdown-editor>)
  - Dev-Tooling — Gulp, Rollup, PostCSS (autoprefixer, cssnano), Mocha + chai + sinon, jsdom

## Datenbank-Schema

Das Modul besitzt fünf Tabellen (Präfix `le_`), die inkrementell durch `src/Schema/Migration#.php` erstellt werden. Jede Migration ist additiv und idempotent, sodass das Schema bei jedem Modul-Load konvergiert.

### `le_route_help_map`

Ordnet eine webtrees-Route (path / handler / method) einem Hilfe-Eintrag zu — der Speicher für die kontextsensitive Handbuch-Hilfe. Wird von `SeedHelpTable` befüllt.

| Spalte | Typ | Hinweise |
| --- | --- | --- |
| `id` | int, PK (auto) | |
| `path` | varchar(255) | Routen-Pfad |
| `handler` | varchar(150) | vollqualifizierte Handler-Klasse |
| `handler_key` | varchar(150), index | versionsneutrale Key (2.2.6 + 2.3 kompatibel) |
| `method` | varchar(20) | |
| `extras` | varchar(60) | |
| `subcontext` | varchar(250) | |
| `category` | varchar(60) | standard / custom module / generic … |
| `order` | int, Default 10 | |
| `url` | varchar(250) | |
| `updated_at` | timestamp, nullable | |

*Index auf `(path, handler)` und auf `handler_key`.*

### `le_record_scan`

Eine Zeile pro gescanntem GEDCOM-Datensatz, mit einem MD5-Fingerprint des Datensatztextes — lässt dem CLI-Index eine inkrementelle Diff-Prüfung zu.

| Spalte | Typ | Hinweise |
| --- | --- | --- |
| `file` | int | Baum-ID (Teil des PK) |
| `xref` | varchar(20) | (Teil des PK) |
| `rectype` | varchar(15) | GEDCOM-Tag, z.B. `INDI`, `OBJE` (Teil des PK) |
| `hash` | varchar(32) | MD5 des Datensatztextes |
| `scanned_at` | timestamp, nullable | |

*Primärschlüssel `(file, xref, rectype)`.*

### `le_link_index`

Eine Zeile pro (Link, Ziel) in einem Datensatz — das Fundament für die geplante Backlink-Funktion.

| Spalte | Typ | Hinweise |
| --- | --- | --- |
| `id` | int, PK (auto) | |
| `file` | int | Baum-ID |
| `xref` | varchar(20) | |
| `rectype` | varchar(15) | |
| `tag_path` | varchar(255) | wo der Link im Datensatz sitzt |
| `link_class` | varchar(10) | xref / ext / pic / classic / other |
| `token` | text | das rohe Link-Token |
| `snippet` | text, nullable | zum Build-Zeitpunkt erfasster Anzeigekontext |
| `target_xref` | varchar(20), nullable | XREF-Link-Ziel |
| `target_tree` | varchar(255), nullable | |
| `target_uid` | varchar(255), nullable, index | UID-Link-Ziel (per Längen-Heuristik befüllt) |

*Indizes auf `(file, xref, rectype)`, `target_xref` und `target_uid`.*

### `le_uid_index`

Eine Zeile pro in einem Datensatz gefundenem UID-Tag (Datensatz- und Fakt-Ebene). Kein Unique-Constraint — UIDs sind nicht eindeutig garantiert.

| Spalte | Typ | Hinweise |
| --- | --- | --- |
| `id` | int, PK (auto) | |
| `file` | int | Baum-ID |
| `xref` | varchar(20) | |
| `uid` | varchar(255), index | UID-Wert wörtlich (ohne Normalisierung) |
| `rectype` | varchar(15) | |
| `tag_path` | varchar(255) | |
| `hash` | varchar(32) | MD5-Fingerprint (inkrementelle Änderungsprüfung) |

*Indizes auf `uid` und `(file, xref, rectype)`.*

### `le_index_meta`

Eine einzelne Zeile (`id = 1`) mit dem Frische-Zustand von **beiden** — dem Link-Index und dem UID-Index.

| Spalte | Typ | Hinweise |
| --- | --- | --- |
| `id` | int, PK, Default 1 | einzelne Zeile |
| `last_run` | timestamp, nullable | letzter vollständiger Link-Index-Lauf |
| `rows` | int, Default 0 | Zeilenzahl des Link-Index |
| `uid_last_run` | timestamp, nullable | letzter vollständiger UID-Index-Lauf |
| `uid_rows` | int, Default 0 | Zeilenzahl des UID-Index |
