mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 19:19:19 +02:00
Move all data-fetching and view-variable assembly out of public/index.php into a new src/HomeController.php, following the same pattern as SearchController, TfeController, SystemController, and ThesisEditController. HomeController::create() builds the Database singleton dependency. HomeController::handle() encapsulates: - GET param parsing (page, year) with safe type coercion - Display-mode detection: default random-latest view / year-filtered / paginated-all theses - All DB calls: getLatestPublishedYear, getLatestYearTheses, searchTheses, countSearchResults, getPublishedTheses, countPublishedTheses, getCoverPathsForTheses, getAvailableYears - Batch cover-image loading for theses without a banner_path - baseParams assembly for the pagination partial - OG / meta tag array construction - Graceful error handling (logs exception, returns safe empty state) - Returns a flat array of view variables public/index.php is now a 6-line dispatcher (require + create + handle + extract) followed by a pure view template. Reduced from 100 to 71 lines. All error-handling and data logic removed from the view layer entirely.
141 lines
5.3 KiB
PHP
141 lines
5.3 KiB
PHP
<?php
|
||
/**
|
||
* HomeController
|
||
*
|
||
* Handles all data-fetching and view-variable assembly for the public home page
|
||
* (public/index.php).
|
||
*
|
||
* Responsibilities:
|
||
* - Parse and validate GET parameters (`page`, `year`)
|
||
* - Determine the display mode (default random-latest / year-filtered / paginated all)
|
||
* - Run the appropriate Database queries
|
||
* - Batch-load cover images for theses without a banner_path
|
||
* - Assemble OG / meta tag array
|
||
* - Return a flat array of view variables ready for template extraction
|
||
*
|
||
* The class has NO output side-effects; all template rendering stays in
|
||
* public/index.php so the view layer remains easy to inspect and modify.
|
||
*/
|
||
class HomeController
|
||
{
|
||
private const ITEMS_PER_PAGE = 24;
|
||
|
||
private Database $db;
|
||
|
||
public function __construct(Database $db)
|
||
{
|
||
$this->db = $db;
|
||
}
|
||
|
||
// ── Factory ───────────────────────────────────────────────────────────────
|
||
|
||
/**
|
||
* Convenience factory: loads the Database singleton and returns a ready
|
||
* controller instance.
|
||
*/
|
||
public static function create(): self
|
||
{
|
||
require_once APP_ROOT . '/src/Database.php';
|
||
|
||
return new self(Database::getInstance());
|
||
}
|
||
|
||
// ── Main entry point ─────────────────────────────────────────────────────
|
||
|
||
/**
|
||
* Process the current request and return all variables needed by the view.
|
||
*
|
||
* @return array<string, mixed>
|
||
*/
|
||
public function handle(): array
|
||
{
|
||
$page = isset($_GET['page']) ? max(1, (int) $_GET['page']) : 1;
|
||
$year = isset($_GET['year']) ? (int) $_GET['year'] : null;
|
||
// Normalise zero (e.g. ?year=0) to null so it is treated as "no filter"
|
||
if ($year === 0) {
|
||
$year = null;
|
||
}
|
||
|
||
// Default home view: random theses from latest year (no year filter, no explicit page)
|
||
$isDefaultView = ($year === null && $page === 1);
|
||
|
||
$itemsToLoad = [];
|
||
$totalItems = 0;
|
||
$availableYears = [];
|
||
$latestYear = null;
|
||
$coverMap = [];
|
||
|
||
try {
|
||
$offset = ($page - 1) * self::ITEMS_PER_PAGE;
|
||
$availableYears = $this->db->getAvailableYears();
|
||
|
||
if ($year !== null) {
|
||
$itemsToLoad = $this->db->searchTheses(['year' => $year], self::ITEMS_PER_PAGE, $offset);
|
||
$totalItems = $this->db->countSearchResults(['year' => $year]);
|
||
} elseif ($isDefaultView) {
|
||
$latestYear = $this->db->getLatestPublishedYear();
|
||
$itemsToLoad = $this->db->getLatestYearTheses(self::ITEMS_PER_PAGE);
|
||
$totalItems = count($itemsToLoad); // no multi-page on default view
|
||
} else {
|
||
$itemsToLoad = $this->db->getPublishedTheses(self::ITEMS_PER_PAGE, $offset);
|
||
$totalItems = $this->db->countPublishedTheses();
|
||
}
|
||
|
||
// Batch-load cover images for theses that have no banner_path
|
||
if (!empty($itemsToLoad)) {
|
||
$needCover = array_column(
|
||
array_filter($itemsToLoad, static fn($t) => empty($t['banner_path'])),
|
||
'id'
|
||
);
|
||
if (!empty($needCover)) {
|
||
$coverMap = $this->db->getCoverPathsForTheses($needCover);
|
||
}
|
||
}
|
||
} catch (Exception $e) {
|
||
error_log('HomeController: ' . $e->getMessage());
|
||
// Return safe empty state; view will show "Aucun mémoire trouvé"
|
||
$isDefaultView = false;
|
||
}
|
||
|
||
$totalPages = $isDefaultView ? 1 : (int) ceil($totalItems / self::ITEMS_PER_PAGE);
|
||
// Avoid division by zero on empty DB
|
||
if ($totalPages < 1) {
|
||
$totalPages = 0;
|
||
}
|
||
|
||
$baseParams = array_filter(['year' => $year]);
|
||
|
||
return [
|
||
// Pagination / filter state
|
||
'page' => $page,
|
||
'year' => $year,
|
||
'isDefaultView' => $isDefaultView,
|
||
'totalItems' => $totalItems,
|
||
'totalPages' => $totalPages,
|
||
'baseParams' => $baseParams,
|
||
|
||
// Thesis data
|
||
'itemsToLoad' => $itemsToLoad,
|
||
'latestYear' => $latestYear,
|
||
'availableYears' => $availableYears,
|
||
'coverMap' => $coverMap,
|
||
|
||
// Page meta
|
||
'pageTitle' => 'Posterg – Mémoires de l\'ERG',
|
||
'metaDescription' => 'Posterg répertorie et valorise les mémoires de fin d\'études (TFE) de l\'erg – École de Recherches Graphiques de Bruxelles.',
|
||
'ogTags' => [
|
||
'type' => 'website',
|
||
'title' => 'Posterg – Mémoires de l\'ERG',
|
||
'description' => 'Posterg répertorie et valorise les mémoires de fin d\'études (TFE) de l\'erg – École de Recherches Graphiques de Bruxelles.',
|
||
'url' => 'https://posterg.erg.be/',
|
||
'site_name' => 'Posterg – ERG',
|
||
],
|
||
|
||
// Layout
|
||
'currentNav' => '',
|
||
'extraCss' => ['/assets/css/main.css'],
|
||
'bodyClass' => 'home-body',
|
||
];
|
||
}
|
||
}
|