mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 19:19:19 +02:00
refactor: extract buildSearchConditions, add getThesesList, remove dead code, fix SearchTest
- Database: extract private buildSearchConditions(array $params): array shared by searchTheses() and countSearchResults(), eliminating ~80 lines of duplication; add array type hints to both public methods - Database: add getThesesList(array $filters) and getAllYears() so admin/index.php no longer builds raw SQL inline - admin/index.php: replace inline PDO query block with $db->getThesesList() / $db->getAllYears(); drop the now-unused $pdo local - config/bootstrap.php: remove dead include_template() helper and the vendor/autoload.php Composer stub (no vendor/ directory exists) - apps/: delete entire directory (leftover artefact, no code references it) - tests/Integration/SearchTest.php: fix three searchTheses() calls from bare strings to proper array params to match the method signature (prevented TypeError)
This commit is contained in:
146
src/Database.php
146
src/Database.php
@@ -257,13 +257,13 @@ class Database {
|
||||
}
|
||||
|
||||
/**
|
||||
* Search theses with filters (secure implementation)
|
||||
* Build WHERE conditions and named bindings from validated search params.
|
||||
* Always includes the `is_published = 1` guard.
|
||||
*
|
||||
* @param array $params Already-validated params (output of validateSearchParams)
|
||||
* @return array{0: string[], 1: array<string,mixed>} [$conditions, $bindings]
|
||||
*/
|
||||
public function searchTheses($params = [], $limit = 20, $offset = 0) {
|
||||
$params = $this->validateSearchParams($params);
|
||||
$limit = max(1, min(100, intval($limit)));
|
||||
$offset = max(0, intval($offset));
|
||||
|
||||
private function buildSearchConditions(array $params): array {
|
||||
$conditions = ["is_published = 1"];
|
||||
$bindings = [];
|
||||
|
||||
@@ -319,6 +319,19 @@ class Database {
|
||||
$bindings[':is_doctoral'] = $params['is_doctoral'] ? 1 : 0;
|
||||
}
|
||||
|
||||
return [$conditions, $bindings];
|
||||
}
|
||||
|
||||
/**
|
||||
* Search theses with filters (secure implementation)
|
||||
*/
|
||||
public function searchTheses(array $params = [], $limit = 20, $offset = 0) {
|
||||
$params = $this->validateSearchParams($params);
|
||||
$limit = max(1, min(100, intval($limit)));
|
||||
$offset = max(0, intval($offset));
|
||||
|
||||
[$conditions, $bindings] = $this->buildSearchConditions($params);
|
||||
|
||||
$whereClause = implode(' AND ', $conditions);
|
||||
$sql = "SELECT * FROM v_theses_public WHERE $whereClause ORDER BY year DESC, title ASC LIMIT :limit OFFSET :offset";
|
||||
|
||||
@@ -336,62 +349,10 @@ class Database {
|
||||
/**
|
||||
* Count search results
|
||||
*/
|
||||
public function countSearchResults($params = []) {
|
||||
public function countSearchResults(array $params = []) {
|
||||
$params = $this->validateSearchParams($params);
|
||||
$conditions = ["is_published = 1"];
|
||||
$bindings = [];
|
||||
|
||||
if (!empty($params['query'])) {
|
||||
$conditions[] = "(
|
||||
title LIKE :query ESCAPE '\\' OR
|
||||
subtitle LIKE :query ESCAPE '\\' OR
|
||||
synopsis LIKE :query ESCAPE '\\' OR
|
||||
authors LIKE :query ESCAPE '\\' OR
|
||||
supervisors LIKE :query ESCAPE '\\' OR
|
||||
keywords LIKE :query ESCAPE '\\'
|
||||
)";
|
||||
$bindings[':query'] = '%' . $params['query'] . '%';
|
||||
}
|
||||
|
||||
if (!empty($params['year'])) {
|
||||
$conditions[] = "year = :year";
|
||||
$bindings[':year'] = $params['year'];
|
||||
}
|
||||
|
||||
if (!empty($params['orientation'])) {
|
||||
$conditions[] = "orientation LIKE :orientation ESCAPE '\\'";
|
||||
$bindings[':orientation'] = '%' . $params['orientation'] . '%';
|
||||
}
|
||||
|
||||
if (!empty($params['ap_program'])) {
|
||||
$conditions[] = "ap_program LIKE :ap_program ESCAPE '\\'";
|
||||
$bindings[':ap_program'] = '%' . $params['ap_program'] . '%';
|
||||
}
|
||||
|
||||
if (!empty($params['finality'])) {
|
||||
$conditions[] = "finality_type LIKE :finality ESCAPE '\\'";
|
||||
$bindings[':finality'] = '%' . $params['finality'] . '%';
|
||||
}
|
||||
|
||||
if (!empty($params['keyword'])) {
|
||||
$conditions[] = "keywords LIKE :keyword ESCAPE '\\'";
|
||||
$bindings[':keyword'] = '%' . $params['keyword'] . '%';
|
||||
}
|
||||
|
||||
if (!empty($params['format'])) {
|
||||
$conditions[] = "formats LIKE :format ESCAPE '\\'";
|
||||
$bindings[':format'] = '%' . $params['format'] . '%';
|
||||
}
|
||||
|
||||
if (!empty($params['language'])) {
|
||||
$conditions[] = "languages LIKE :language ESCAPE '\\'";
|
||||
$bindings[':language'] = '%' . $params['language'] . '%';
|
||||
}
|
||||
|
||||
if (isset($params['is_doctoral'])) {
|
||||
$conditions[] = "is_doctoral = :is_doctoral";
|
||||
$bindings[':is_doctoral'] = $params['is_doctoral'] ? 1 : 0;
|
||||
}
|
||||
[$conditions, $bindings] = $this->buildSearchConditions($params);
|
||||
|
||||
$whereClause = implode(' AND ', $conditions);
|
||||
$sql = "SELECT COUNT(*) as count FROM v_theses_public WHERE $whereClause";
|
||||
@@ -508,6 +469,71 @@ class Database {
|
||||
return $this->getLanguages();
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// ADMIN LIST METHOD
|
||||
// ========================================================================
|
||||
|
||||
/**
|
||||
* Return theses for the admin list view, with optional filters.
|
||||
*
|
||||
* Filters (all optional):
|
||||
* 'search' string – matches title, subtitle, or author name (LIKE)
|
||||
* 'year' int – exact year match
|
||||
* 'orientation' int – orientation_id exact match
|
||||
*
|
||||
* @param array $filters
|
||||
* @return array
|
||||
*/
|
||||
public function getThesesList(array $filters = []): array {
|
||||
$sql = "SELECT
|
||||
t.id, t.identifier, t.title, t.subtitle, t.year,
|
||||
o.name as orientation,
|
||||
ap.name as ap_program,
|
||||
GROUP_CONCAT(DISTINCT a.name) as authors,
|
||||
t.submitted_at,
|
||||
t.is_published
|
||||
FROM theses t
|
||||
LEFT JOIN orientations o ON t.orientation_id = o.id
|
||||
LEFT JOIN ap_programs ap ON t.ap_program_id = ap.id
|
||||
LEFT JOIN thesis_authors ta ON t.id = ta.thesis_id
|
||||
LEFT JOIN authors a ON ta.author_id = a.id
|
||||
WHERE 1=1";
|
||||
|
||||
$params = [];
|
||||
|
||||
if (!empty($filters['search'])) {
|
||||
$sql .= " AND (t.title LIKE ? OR t.subtitle LIKE ? OR a.name LIKE ?)";
|
||||
$searchParam = '%' . $filters['search'] . '%';
|
||||
$params[] = $searchParam;
|
||||
$params[] = $searchParam;
|
||||
$params[] = $searchParam;
|
||||
}
|
||||
|
||||
if (!empty($filters['year'])) {
|
||||
$sql .= " AND t.year = ?";
|
||||
$params[] = intval($filters['year']);
|
||||
}
|
||||
|
||||
if (!empty($filters['orientation'])) {
|
||||
$sql .= " AND t.orientation_id = ?";
|
||||
$params[] = intval($filters['orientation']);
|
||||
}
|
||||
|
||||
$sql .= " GROUP BY t.id ORDER BY t.year DESC, t.submitted_at DESC";
|
||||
|
||||
$stmt = $this->pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get distinct years present in the theses table (admin, includes unpublished).
|
||||
*/
|
||||
public function getAllYears(): array {
|
||||
$stmt = $this->pdo->query("SELECT DISTINCT year FROM theses ORDER BY year DESC");
|
||||
return $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// CRUD METHODS (from formulaire)
|
||||
// ========================================================================
|
||||
|
||||
Reference in New Issue
Block a user