mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-05-06 19:19:19 +02:00
Add comprehensive thesis management system with database migration
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>
This commit is contained in:
277
formulaire/Database.php
Normal file
277
formulaire/Database.php
Normal file
@@ -0,0 +1,277 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user