Split search into search.php; repertoire.php is index-only

This commit is contained in:
Pontoporeia
2026-04-07 15:01:30 +02:00
parent e96ec572be
commit 0c2276d5ad
7 changed files with 181 additions and 136 deletions

View File

@@ -2,9 +2,11 @@
/**
* SearchController
*
* Handles all data-fetching logic for the public search / répertoire page.
* The entry point (public/repertoire.php) delegates to this class and receives
* a plain array of view variables ready for template inclusion.
* Handles all data-fetching logic for the public search and répertoire pages.
*
* Entry points:
* - public/search.php calls handleSearch() — text-query results
* - public/repertoire.php calls handleRepertoire() — filter index + HTMX swaps
*
* Responsibilities:
* - Rate-limit enforcement (returns early HTTP 429 response when needed)
@@ -13,8 +15,8 @@
* - OG / meta tag assembly
* - HTMX partial response for repertoire filter swaps
*
* The class has NO output side-effects; all template rendering stays in
* public/repertoire.php so the view layer remains easy to inspect and modify.
* The class has NO output side-effects; all template rendering stays in the
* respective public/*.php files so the view layer remains easy to inspect.
* Exception: renderRepertoirePartial() exits early for HTMX requests.
*/
class SearchController
@@ -59,47 +61,93 @@ class SearchController
return new self(Database::getInstance(), $rateLimit);
}
// ── Main entry point ─────────────────────────────────────────────────────
// ── Entry points ──────────────────────────────────────────────────────────
/**
* Process the current request and return all variables needed by the view.
* Handle the search results page (public/search.php).
* Requires a ?query= parameter; always returns search-result view variables.
*
* @return array<string, mixed>
*/
public function handle(): array
public function handleSearch(): array
{
$isHtmx = !empty($_SERVER['HTTP_HX_REQUEST']);
$searchParams = $this->collectSearchParams();
$hasSearch = !empty($searchParams);
$activeFilters = $this->collectFilterParams();
$searchParams = $this->collectSearchParams();
$page = isset($_GET['page']) ? max(1, (int) $_GET['page']) : 1;
$offset = ($page - 1) * self::ITEMS_PER_PAGE;
$validationError = null;
$results = [];
$totalItems = 0;
$totalPages = 0;
$repData = null;
// For search filter dropdowns (text search mode only)
$results = [];
$totalItems = 0;
$totalPages = 0;
$years = [];
$orientations = [];
$apPrograms = [];
try {
if ($hasSearch) {
$results = $this->db->searchTheses($searchParams, self::ITEMS_PER_PAGE, $offset);
$totalItems = $this->db->countSearchResults($searchParams);
$totalPages = (int) ceil($totalItems / self::ITEMS_PER_PAGE);
$years = $this->db->getAvailableYears();
$orientations = $this->db->getAllOrientations();
$apPrograms = $this->db->getAllAPPrograms();
} else {
// Repertoire index: compute filter data (all columns + matched flags)
$repData = $this->db->getRepertoireFilterData($activeFilters);
}
$results = $this->db->searchTheses($searchParams, self::ITEMS_PER_PAGE, $offset);
$totalItems = $this->db->countSearchResults($searchParams);
$totalPages = (int) ceil($totalItems / self::ITEMS_PER_PAGE);
$years = $this->db->getAvailableYears();
$orientations = $this->db->getAllOrientations();
$apPrograms = $this->db->getAllAPPrograms();
} catch (InvalidArgumentException $e) {
$validationError = $e->getMessage();
} catch (Exception $e) {
error_log('SearchController: ' . $e->getMessage());
$validationError = 'Une erreur est survenue.';
}
// Preserve all active params, strip 'page' (pagination partial adds it)
$baseParams = array_diff_key($_GET, ['page' => '']);
$query = $_GET['query'] ?? '';
return [
'searchParams' => $searchParams,
'page' => $page,
'totalItems' => $totalItems,
'totalPages' => $totalPages,
'results' => $results,
'validationError' => $validationError,
'baseParams' => $baseParams,
// Filter dropdowns
'years' => $years,
'orientations' => $orientations,
'apPrograms' => $apPrograms,
// Page meta
'searchBarValue' => $query,
'pageTitle' => $query !== '' ? 'Recherche : ' . $query . ' Posterg' : 'Recherche Posterg',
'metaDescription' => "Résultats de recherche dans le répertoire des TFE de l'erg.",
'ogTags' => [
'type' => 'website',
'title' => 'Recherche Posterg',
'description' => "Résultats de recherche dans le répertoire des TFE de l'erg.",
'url' => 'https://posterg.erg.be/search.php',
'site_name' => 'Posterg ERG',
],
'currentNav' => 'repertoire',
'extraCss' => ['/assets/css/search.css'],
'bodyClass' => 'search-body',
];
}
/**
* Handle the répertoire index page (public/repertoire.php).
* Serves the filter-column index; HTMX partial swaps are handled here too.
*
* @return array<string, mixed>
*/
public function handleRepertoire(): array
{
$isHtmx = !empty($_SERVER['HTTP_HX_REQUEST']);
$activeFilters = $this->collectFilterParams();
$repData = null;
$validationError = null;
try {
$repData = $this->db->getRepertoireFilterData($activeFilters);
} catch (InvalidArgumentException $e) {
$validationError = $e->getMessage();
} catch (Exception $e) {
@@ -108,36 +156,18 @@ class SearchController
}
// HTMX partial: render just the index div and exit
if ($isHtmx && !$hasSearch && $repData !== null) {
if ($isHtmx && $repData !== null) {
$this->renderRepertoirePartial($repData, $activeFilters);
}
// Preserve all active search/filter params, strip 'page' (pagination partial adds it)
$baseParams = array_diff_key($_GET, ['page' => '']);
return [
// Search state
'searchParams' => $searchParams,
'hasSearch' => $hasSearch,
'page' => $page,
'totalItems' => $totalItems,
'totalPages' => $totalPages,
'results' => $results,
'validationError' => $validationError,
'baseParams' => $baseParams,
// Repertoire filter state
'repData' => $repData,
'activeFilters' => $activeFilters,
'isHtmx' => $isHtmx,
// Search filter dropdowns (text search mode only)
'years' => $years,
'orientations' => $orientations,
'apPrograms' => $apPrograms,
'validationError' => $validationError,
// Page meta
'searchBarValue' => $_GET['query'] ?? '',
'searchBarValue' => '',
'pageTitle' => 'Répertoire Posterg',
'metaDescription' => "Parcourez le répertoire des mémoires de fin d'études (TFE) de l'erg École de Recherches Graphiques de Bruxelles.",
'ogTags' => [