Files
xamxam/docs/search.md
T
Pontoporeia a31e4ab307 Recherche insensible aux accents sur toutes les barres de recherche (publique + backoffice).
Ajoute une fonction SQLite UDF accfold() (app/src/AccentFolding.php) qui plie les caractères accentués latin vers leur base ASCII (é→e, ç→c, …), et l'applique à chaque condition de recherche côté colonne ET côté terme (accfold(column) LIKE accfold(:term)). Comme le runtime n'a ni intl (Normalizer) ni iconv, le repli passe par une map de translitération manuelle (Western Latin-1 + Latin Extended-A, NULL-safe).

Couvert :
- recherche publique (searchTheses / countSearchResults) : titre, sous-titre, synopsis, auteurs, promoteurs, tags ;
- popover étudiant (getThesesByAuthorName / getThesesForAuthors) : a.name ;
- recherche backoffice (getThesesList / getThesesListCount) : titre, sous-titre, identifiant, auteur.

L'UDF est enregistré dans Database::registerSqliteFunctions() (appelé par le constructeur et par le harnais de test TestDatabaseInstance). Évite aussi la dépréciation PHP 8.5 de PDO::sqliteCreateFunction() : la connexion est créée via \Pdo\Sqlite quand disponible (createFunction()) avec repli sur PDO (sqliteCreateFunction()) sur les anciens runtimes ; les tests passent de @dataProvider docblock à l'attribut #[DataProvider] (fin de la dépréciation PHPUnit). Détache .php-cs-fixer.cache (gitignoré) pour ne plus polluer le working copy.

294 tests PHPUnit verts sans dépréciation, phpstan niveau 5 OK.
2026-09-18 16:26:36 +02:00

119 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Search & Répertoire — Documentation
Two public browsing surfaces, both handled by `app/src/Controllers/SearchController.php`
and routed by `app/src/Dispatcher.php`:
| Route | Handler | Purpose |
|-------|---------|---------|
| `/search`, `/search.php` | `handleSearch()` | Full-text query + classic single filters |
| `/repertoire`, `/repertoire.php` | `handleRepertoire()` | Browseable directory with multi-select filters |
Both only ever expose **published** theses (`is_published = 1`).
---
## HandleSearch (`/search`)
`handleSearch()` reads from `$_GET` and renders `app/templates/public/search.php`
with the results fragment (`app/templates/partials/search-results.php`).
Searchable text fields (via `Database::searchTheses()`):
- Title, subtitle, synopsis, author names, supervisor names, tags/keywords.
Single-value filters (`collectSearchParams()`):
- `query` — free text
- `year` — exact year
- `orientation` — artistic orientation
- `ap_program` — AP program
- `finality` — finality type
- `format` — format
- `keyword` — tag/keyword
Results are paginated (`limit = 20` default); the **search bar**
(`app/templates/partials/search-bar.php`) submits a GET form to `/search`.
---
## HandleRepertoire (`/repertoire`)
`handleRepertoire()` reads multi-select filter arrays from `$_GET` and renders
`app/templates/public/repertoire.php`, which uses the shared results partial
and `app/templates/partials/repertoire-index.php`.
Multi-select filters (`collectFilterParams()`, each an array, `_GET` keys):
- `fy[]` — years (validated to 1900–2100)
- `ap[]` — AP program names
- `or[]` — orientations
- `fi[]` — finalities
- `kw[]` — keywords/tags
Each value is trimmed, length-capped (≤ 100), de-duplicated, and passed through
as sanitised strings — no direct user input reaches SQL.
There is also an HTMX **student preview** popover at
`/repertoire/student-preview` (`handleStudentPreview()` → `student-preview.php`).
---
## Rate limiting
Search is rate-limited via `app/src/RateLimit.php`. See the nginx config
(`nginx/xamxam.conf`) for the matching server-side limits.
---
## Database access
- **Full-text + single filters:** `Database::searchTheses(array $params, $limit, $offset)`
and `Database::countSearchResults(array $params)`.
- **Keyword/tag autocomplete:** `Database::searchTags(string $query)`.
- **Supervisor autocomplete:** `Database::searchSupervisors($query, $role)`.
- **Language autocomplete:** `Database::searchLanguages(string $query)`.
Queries operate on `v_theses_public`; keyword matching joins the `thesis_tags` /
`tags` tables (keywords are stored as lowercase-normalised **tags**, not a
`keywords`/`thesis_keywords` set — see [database.md](database.md)).
All queries use PDO prepared statements and escape `%`/`_` for `LIKE`
(`Database::escapeLikeString`) to prevent wildcard injection.
### Accent-insensitive matching
Text searches on the public **search bar**, the **student-preview** popover, and
the **admin list** search are all **diacritic-insensitive**: `Théophile` and
`Theophile`, `sévère` and `severe` return the same results.
This is implemented via a SQLite user-defined function `accfold()`
(`app/src/AccentFolding.php`, registered in `Database::registerSqliteFunctions()`), which
strips precomposed accented Latin characters to their ASCII base (`é` → `e`,
`ç` → `c`, …). Search conditions compare the folded column against the folded
term (`accfold(column) LIKE accfold(:term)`), so both sides are normalised the
same way. The function is NULL-safe and registered on the production and test
connections alike; the public path uses `searchTheses()`/`countSearchResults()`,
the student popover `getThesesByAuthorName()`, and the admin list
`getThesesList()`/`getThesesListCount()`.
Note: `intl`/`Normalizer` and `iconv` are **not** available on the runtime, so
folding uses a hand-maintained transliteration map (Western Latin-1 +
Latin Extended-A).
---
## Performance notes
- Critical text/filter columns and the junction tables are indexed
(`idx_theses_pub_year`, `idx_theses_*`, `idx_thesis_tags_*`, …).
- `v_theses_public` pre-computes the joins for the common read path.
- The repertoire filters operate on indexed lookup columns.
---
## Future enhancements (not yet implemented)
The historical `search.md` listed potential automplete/faceted-search/export
ideas. Status:
- Auto-complete for tags exists at the form level (`searchTags`)
- Faceted counts, saved searches, result export, and advanced boolean operators
are **not** implemented