refactor: rename keywords→tags M2M (migration 001)

- migration 001_rename_keywords_to_tags.sql: CREATE tags/thesis_tags from keywords/thesis_keywords,
  copy data, drop old tables, rebuild indexes and views
- schema.sql: tags table, thesis_tags junction, updated indexes and v_theses_full/v_theses_public
- Database.php: findOrCreateTag(), getUsedTags() with proper JOIN; backwards-compat aliases;
  buildSearchConditions uses EXISTS subquery on thesis_tags+tags with vp. alias throughout
- admin/actions/formulaire.php: INSERT OR IGNORE INTO thesis_tags
- admin/edit.php: DELETE FROM thesis_tags + findOrCreateTag
- search.php: $kw['name'] (was $kw['keyword'])
- fixtures/CreateTestDatabase.php: tags/thesis_tags table names
This commit is contained in:
Pontoporeia
2026-03-24 13:30:53 +01:00
parent cefceb046c
commit 0933137540
9 changed files with 226 additions and 130 deletions

View File

@@ -105,13 +105,15 @@ INSERT OR IGNORE INTO format_types (name) VALUES
('Installation'),
('Autre');
-- Keywords (expandable list)
CREATE TABLE IF NOT EXISTS keywords (
-- Tags (keywords — canonical M2M table; formerly 'keywords'/'keyword' column)
CREATE TABLE IF NOT EXISTS tags (
id INTEGER PRIMARY KEY AUTOINCREMENT,
keyword TEXT NOT NULL UNIQUE,
name TEXT NOT NULL UNIQUE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_tags_name ON tags(name);
-- Access authorization types
CREATE TABLE IF NOT EXISTS access_types (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -250,13 +252,13 @@ CREATE TABLE IF NOT EXISTS thesis_formats (
FOREIGN KEY (format_id) REFERENCES format_types(id) ON DELETE CASCADE
);
-- Keywords per thesis (max 10 as per specs)
CREATE TABLE IF NOT EXISTS thesis_keywords (
-- Tags per thesis (max 10 as per specs; formerly thesis_keywords)
CREATE TABLE IF NOT EXISTS thesis_tags (
tag_id INTEGER NOT NULL,
thesis_id INTEGER NOT NULL,
keyword_id INTEGER NOT NULL,
PRIMARY KEY (thesis_id, keyword_id),
PRIMARY KEY (tag_id, thesis_id),
FOREIGN KEY (thesis_id) REFERENCES theses(id) ON DELETE CASCADE,
FOREIGN KEY (keyword_id) REFERENCES keywords(id) ON DELETE CASCADE
FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
);
-- ============================================================================
@@ -311,8 +313,8 @@ CREATE INDEX IF NOT EXISTS idx_theses_access_type ON theses(access_type_id);
CREATE INDEX IF NOT EXISTS idx_authors_email ON authors(email);
CREATE INDEX IF NOT EXISTS idx_thesis_authors_thesis ON thesis_authors(thesis_id);
CREATE INDEX IF NOT EXISTS idx_thesis_authors_author ON thesis_authors(author_id);
CREATE INDEX IF NOT EXISTS idx_thesis_keywords_thesis ON thesis_keywords(thesis_id);
CREATE INDEX IF NOT EXISTS idx_thesis_keywords_keyword ON thesis_keywords(keyword_id);
CREATE INDEX IF NOT EXISTS idx_thesis_tags_thesis ON thesis_tags(thesis_id);
CREATE INDEX IF NOT EXISTS idx_thesis_tags_tag ON thesis_tags(tag_id);
-- ============================================================================
-- TRIGGERS for automatic timestamp updates
@@ -380,7 +382,7 @@ SELECT
GROUP_CONCAT(DISTINCT CASE WHEN ts.role = 'lecteur' THEN s.name END) as jury_lecteurs,
GROUP_CONCAT(DISTINCT l.name) as languages,
GROUP_CONCAT(DISTINCT fmt.name) as formats,
GROUP_CONCAT(DISTINCT k.keyword) as keywords
GROUP_CONCAT(DISTINCT tg.name) as keywords
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
@@ -395,8 +397,8 @@ LEFT JOIN thesis_languages tl ON t.id = tl.thesis_id
LEFT JOIN languages l ON tl.language_id = l.id
LEFT JOIN thesis_formats tf ON t.id = tf.thesis_id
LEFT JOIN format_types fmt ON tf.format_id = fmt.id
LEFT JOIN thesis_keywords tk ON t.id = tk.thesis_id
LEFT JOIN keywords k ON tk.keyword_id = k.id
LEFT JOIN thesis_tags tt ON t.id = tt.thesis_id
LEFT JOIN tags tg ON tt.tag_id = tg.id
GROUP BY t.id;
-- Published theses only (for public view)