# 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. --- ## 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