fix: validation error messages hidden by generic fallback in ErrorHandler::userMessage

ErrorHandler::userMessage only handled RuntimeException, but all validation
throws in ThesisCreateController and ThesisEditController use plain Exception.
This caused user-friendly messages like 'Le champ Nom/Prénom/Pseudo est requis'
to fall through to the 'Une erreur inattendue est survenue…' generic message.

Fix: add Exception check (after PDOException, since PDOException extends it)
so all validation exceptions pass their message through.
This commit is contained in:
Pontoporeia
2026-05-11 17:03:22 +02:00
parent c3f6e8a033
commit df12af8423
6 changed files with 22 additions and 7 deletions

View File

@@ -0,0 +1,52 @@
-- Migration 034: Deduplicate all lookup tables that have no UNIQUE on name/key/slug.
-- Root cause: INSERT OR IGNORE is ineffective without a UNIQUE constraint,
-- and schema.sql was applied repeatedly during broken-migration-runner era.
-- ── access_types ──
DELETE FROM access_types WHERE id NOT IN (SELECT MIN(id) FROM access_types GROUP BY name);
CREATE UNIQUE INDEX IF NOT EXISTS idx_access_types_name_unique ON access_types(name);
-- ── ap_programs ──
DELETE FROM ap_programs WHERE id NOT IN (SELECT MIN(id) FROM ap_programs GROUP BY name);
CREATE UNIQUE INDEX IF NOT EXISTS idx_ap_programs_name_unique ON ap_programs(name);
-- ── finality_types ──
DELETE FROM finality_types WHERE id NOT IN (SELECT MIN(id) FROM finality_types GROUP BY name);
CREATE UNIQUE INDEX IF NOT EXISTS idx_finality_types_name_unique ON finality_types(name);
-- ── languages ──
DELETE FROM languages WHERE id NOT IN (SELECT MIN(id) FROM languages GROUP BY name);
CREATE UNIQUE INDEX IF NOT EXISTS idx_languages_name_unique ON languages(name);
-- ── license_types ──
DELETE FROM license_types WHERE id NOT IN (SELECT MIN(id) FROM license_types GROUP BY name);
CREATE UNIQUE INDEX IF NOT EXISTS idx_license_types_name_unique ON license_types(name);
-- ── orientations ──
DELETE FROM orientations WHERE id NOT IN (SELECT MIN(id) FROM orientations GROUP BY name);
CREATE UNIQUE INDEX IF NOT EXISTS idx_orientations_name_unique ON orientations(name);
-- ── pages ──
DELETE FROM pages WHERE id NOT IN (SELECT MIN(id) FROM pages GROUP BY slug);
CREATE UNIQUE INDEX IF NOT EXISTS idx_pages_slug_unique ON pages(slug);
-- ── apropos_contents ──
DELETE FROM apropos_contents WHERE id NOT IN (SELECT MIN(id) FROM apropos_contents GROUP BY key);
CREATE UNIQUE INDEX IF NOT EXISTS idx_apropos_contents_key_unique ON apropos_contents(key);
-- ── tags ──
DELETE FROM tags WHERE id NOT IN (SELECT MIN(id) FROM tags GROUP BY name);
CREATE UNIQUE INDEX IF NOT EXISTS idx_tags_name_unique ON tags(name);
-- ── supervisors ──
DELETE FROM supervisors WHERE id NOT IN (SELECT MIN(id) FROM supervisors GROUP BY name);
CREATE UNIQUE INDEX IF NOT EXISTS idx_supervisors_name_unique ON supervisors(name);
-- ── format_types ── (re-run in case any crept back)
DELETE FROM format_types WHERE id NOT IN (SELECT MIN(id) FROM format_types GROUP BY name);
CREATE UNIQUE INDEX IF NOT EXISTS idx_format_types_name_unique ON format_types(name);
-- ── Clean up authors with empty email (soft-deleted or dummy rows)
-- These come from CSV import artefacts. Keep only the first per email.
DELETE FROM authors WHERE id NOT IN (SELECT MIN(id) FROM authors GROUP BY email);
CREATE UNIQUE INDEX IF NOT EXISTS idx_authors_email_unique ON authors(email);