mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 11:09:18 +02:00
This commit introduces a complete thesis management interface and migrates the system from YAML-based storage to SQLite: Core Changes: - Add Database.php helper class with PDO connection and entity management - Add list.php for viewing all theses with filtering and sorting - Add edit.php for modifying existing thesis records - Add import.php for migrating legacy YAML data to SQLite - Add justfile with development tasks (serve, init-test-db, etc.) Documentation: - Add MIGRATION.md with complete migration guide and architecture docs - Update README.md with database setup and Just recipe instructions - Update .gitignore to exclude test databases and error logs Modified Forms: - Enhanced formulaire.php with transaction-based SQLite processing - Updated index.php with database-driven form options - Improved thanks.php to read from database views The new architecture provides: - Normalized database schema (19 tables, 2 views) - Transaction safety and referential integrity - CRUD operations for thesis management - Filtering by year, orientation, AP program, publication status - Secure file handling with metadata tracking 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
278 lines
8.4 KiB
PHP
278 lines
8.4 KiB
PHP
<?php
|
|
/**
|
|
* Database connection and helper class for Post-ERG thesis database
|
|
*/
|
|
class Database {
|
|
private $pdo;
|
|
private $dbPath;
|
|
|
|
/**
|
|
* Constructor - establishes database connection
|
|
* @param string $dbPath Path to SQLite database file
|
|
*/
|
|
public function __construct($dbPath = null) {
|
|
if ($dbPath === null) {
|
|
// Check for test database first (for local development)
|
|
$testDb = __DIR__ . '/test.db';
|
|
if (file_exists($testDb)) {
|
|
$this->dbPath = $testDb;
|
|
} else {
|
|
// Default to parent directory's db folder (production)
|
|
$this->dbPath = __DIR__ . '/../db/posterg.db';
|
|
}
|
|
} else {
|
|
$this->dbPath = $dbPath;
|
|
}
|
|
|
|
try {
|
|
$this->pdo = new PDO('sqlite:' . $this->dbPath);
|
|
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
$this->pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
|
|
|
// Enable foreign key constraints
|
|
$this->pdo->exec('PRAGMA foreign_keys = ON');
|
|
} catch (PDOException $e) {
|
|
error_log("Database connection failed: " . $e->getMessage());
|
|
throw new Exception("Impossible de se connecter à la base de données.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get PDO instance for direct queries if needed
|
|
* @return PDO
|
|
*/
|
|
public function getPDO() {
|
|
return $this->pdo;
|
|
}
|
|
|
|
/**
|
|
* Begin a transaction
|
|
*/
|
|
public function beginTransaction() {
|
|
return $this->pdo->beginTransaction();
|
|
}
|
|
|
|
/**
|
|
* Commit a transaction
|
|
*/
|
|
public function commit() {
|
|
return $this->pdo->commit();
|
|
}
|
|
|
|
/**
|
|
* Rollback a transaction
|
|
*/
|
|
public function rollback() {
|
|
return $this->pdo->rollback();
|
|
}
|
|
|
|
/**
|
|
* Find or create an author
|
|
* @param string $name Author name
|
|
* @param string $email Author email (optional)
|
|
* @return int Author ID
|
|
*/
|
|
public function findOrCreateAuthor($name, $email = null) {
|
|
// Try to find existing author by name
|
|
$stmt = $this->pdo->prepare("SELECT id FROM authors WHERE name = ?");
|
|
$stmt->execute([$name]);
|
|
$author = $stmt->fetch();
|
|
|
|
if ($author) {
|
|
// Update email if provided and different
|
|
if ($email && $email !== '') {
|
|
$updateStmt = $this->pdo->prepare("UPDATE authors SET email = ? WHERE id = ?");
|
|
$updateStmt->execute([$email, $author['id']]);
|
|
}
|
|
return $author['id'];
|
|
}
|
|
|
|
// Create new author
|
|
$stmt = $this->pdo->prepare("INSERT INTO authors (name, email) VALUES (?, ?)");
|
|
$stmt->execute([$name, $email]);
|
|
return $this->pdo->lastInsertId();
|
|
}
|
|
|
|
/**
|
|
* Find or create a supervisor
|
|
* @param string $name Supervisor name
|
|
* @return int Supervisor ID
|
|
*/
|
|
public function findOrCreateSupervisor($name) {
|
|
// Try to find existing supervisor
|
|
$stmt = $this->pdo->prepare("SELECT id FROM supervisors WHERE name = ?");
|
|
$stmt->execute([$name]);
|
|
$supervisor = $stmt->fetch();
|
|
|
|
if ($supervisor) {
|
|
return $supervisor['id'];
|
|
}
|
|
|
|
// Create new supervisor
|
|
$stmt = $this->pdo->prepare("INSERT INTO supervisors (name) VALUES (?)");
|
|
$stmt->execute([$name]);
|
|
return $this->pdo->lastInsertId();
|
|
}
|
|
|
|
/**
|
|
* Find or create a keyword
|
|
* @param string $keyword Keyword text
|
|
* @return int Keyword ID
|
|
*/
|
|
public function findOrCreateKeyword($keyword) {
|
|
$keyword = trim($keyword);
|
|
if (empty($keyword)) {
|
|
return null;
|
|
}
|
|
|
|
// Try to find existing keyword
|
|
$stmt = $this->pdo->prepare("SELECT id FROM keywords WHERE keyword = ?");
|
|
$stmt->execute([$keyword]);
|
|
$kw = $stmt->fetch();
|
|
|
|
if ($kw) {
|
|
return $kw['id'];
|
|
}
|
|
|
|
// Create new keyword
|
|
$stmt = $this->pdo->prepare("INSERT INTO keywords (keyword) VALUES (?)");
|
|
$stmt->execute([$keyword]);
|
|
return $this->pdo->lastInsertId();
|
|
}
|
|
|
|
/**
|
|
* Get orientation ID by name
|
|
* @param string $name Orientation name
|
|
* @return int|null Orientation ID or null if not found
|
|
*/
|
|
public function getOrientationId($name) {
|
|
$stmt = $this->pdo->prepare("SELECT id FROM orientations WHERE name = ?");
|
|
$stmt->execute([$name]);
|
|
$result = $stmt->fetch();
|
|
return $result ? $result['id'] : null;
|
|
}
|
|
|
|
/**
|
|
* Get AP program ID by name
|
|
* @param string $name AP program name
|
|
* @return int|null AP program ID or null if not found
|
|
*/
|
|
public function getAPProgramId($name) {
|
|
$stmt = $this->pdo->prepare("SELECT id FROM ap_programs WHERE name = ?");
|
|
$stmt->execute([$name]);
|
|
$result = $stmt->fetch();
|
|
return $result ? $result['id'] : null;
|
|
}
|
|
|
|
/**
|
|
* Get finality type ID by name
|
|
* @param string $name Finality type name
|
|
* @return int|null Finality type ID or null if not found
|
|
*/
|
|
public function getFinalityId($name) {
|
|
$stmt = $this->pdo->prepare("SELECT id FROM finality_types WHERE name = ?");
|
|
$stmt->execute([$name]);
|
|
$result = $stmt->fetch();
|
|
return $result ? $result['id'] : null;
|
|
}
|
|
|
|
/**
|
|
* Get language ID by name
|
|
* @param string $name Language name
|
|
* @return int|null Language ID or null if not found
|
|
*/
|
|
public function getLanguageId($name) {
|
|
$stmt = $this->pdo->prepare("SELECT id FROM languages WHERE name = ?");
|
|
$stmt->execute([$name]);
|
|
$result = $stmt->fetch();
|
|
return $result ? $result['id'] : null;
|
|
}
|
|
|
|
/**
|
|
* Get format type ID by name
|
|
* @param string $name Format type name
|
|
* @return int|null Format type ID or null if not found
|
|
*/
|
|
public function getFormatId($name) {
|
|
$stmt = $this->pdo->prepare("SELECT id FROM format_types WHERE name = ?");
|
|
$stmt->execute([$name]);
|
|
$result = $stmt->fetch();
|
|
return $result ? $result['id'] : null;
|
|
}
|
|
|
|
/**
|
|
* Get all orientations
|
|
* @return array Array of orientations
|
|
*/
|
|
public function getAllOrientations() {
|
|
$stmt = $this->pdo->query("SELECT id, name FROM orientations ORDER BY name");
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
/**
|
|
* Get all AP programs
|
|
* @return array Array of AP programs
|
|
*/
|
|
public function getAllAPPrograms() {
|
|
$stmt = $this->pdo->query("SELECT id, name, code FROM ap_programs ORDER BY name");
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
/**
|
|
* Get all finality types
|
|
* @return array Array of finality types
|
|
*/
|
|
public function getAllFinalityTypes() {
|
|
$stmt = $this->pdo->query("SELECT id, name FROM finality_types ORDER BY name");
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
/**
|
|
* Get all languages
|
|
* @return array Array of languages
|
|
*/
|
|
public function getAllLanguages() {
|
|
$stmt = $this->pdo->query("SELECT id, name FROM languages ORDER BY name");
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
/**
|
|
* Get all format types
|
|
* @return array Array of format types
|
|
*/
|
|
public function getAllFormatTypes() {
|
|
$stmt = $this->pdo->query("SELECT id, name FROM format_types ORDER BY name");
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
/**
|
|
* Get thesis by ID
|
|
* @param int $id Thesis ID
|
|
* @return array|null Thesis data or null if not found
|
|
*/
|
|
public function getThesis($id) {
|
|
$stmt = $this->pdo->prepare("SELECT * FROM v_theses_full WHERE id = ?");
|
|
$stmt->execute([$id]);
|
|
return $stmt->fetch();
|
|
}
|
|
|
|
/**
|
|
* Insert a thesis file record
|
|
* @param int $thesisId Thesis ID
|
|
* @param string $fileType File type ('main', 'annex', 'written_part', 'other')
|
|
* @param string $filePath Server path to file
|
|
* @param string $fileName Original filename
|
|
* @param int $fileSize File size in bytes
|
|
* @param string $mimeType MIME type
|
|
* @return int File ID
|
|
*/
|
|
public function insertThesisFile($thesisId, $fileType, $filePath, $fileName, $fileSize, $mimeType) {
|
|
$stmt = $this->pdo->prepare("
|
|
INSERT INTO thesis_files (thesis_id, file_type, file_path, file_name, file_size, mime_type)
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
");
|
|
$stmt->execute([$thesisId, $fileType, $filePath, $fileName, $fileSize, $mimeType]);
|
|
return $this->pdo->lastInsertId();
|
|
}
|
|
}
|