Aller au contenu

Conception du schéma de base de données

Une conception appropriée du schéma de base de données est cruciale pour les performances et la maintenabilité des modules XOOPS. Ce guide couvre les meilleures pratiques en matière de conception de table, de relations, d’indexation et de migrations.

{prefix}_{modulename}_{tablename}

Exemples:

  • xoops_mymodule_articles
  • xoops_mymodule_categories
  • xoops_mymodule_article_category (table de jonction)

Utiliser l’espace réservé {PREFIX} :

CREATE TABLE `{PREFIX}_mymodule_articles` (
...
);
DonnéesType MySQLType PHPDescription
ID (ULID)VARCHAR(26)stringIdentifiants ULID
ID (Auto)INT UNSIGNED AUTO_INCREMENTintIDs séquentiels
Texte courtVARCHAR(n)stringJusqu’à 255 caractères
Texte longTEXTstringTexte illimité
Texte richeMEDIUMTEXTstringContenu HTML
BooléenTINYINT(1)boolVrai/faux
ÉnumérationENUM(...)stringOptions fixes
DateDATEDateTimeImmutableDate uniquement
DateTimeDATETIMEDateTimeImmutableDate et heure
TimestampINT UNSIGNEDintTimestamp Unix
PrixDECIMAL(10,2)floatValeurs de devise
JSONJSONarrayDonnées structurées
CREATE TABLE `{PREFIX}_mymodule_articles` (
`id` VARCHAR(26) NOT NULL COMMENT 'ULID identifier',
`title` VARCHAR(255) NOT NULL,
`slug` VARCHAR(255) NOT NULL,
`content` MEDIUMTEXT,
`summary` TEXT,
`status` ENUM('draft', 'pending', 'published', 'archived') DEFAULT 'draft',
`author_id` INT UNSIGNED NOT NULL,
`category_id` INT UNSIGNED,
`views` INT UNSIGNED DEFAULT 0,
`is_featured` TINYINT(1) DEFAULT 0,
`published_at` DATETIME DEFAULT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_slug` (`slug`),
KEY `idx_status` (`status`),
KEY `idx_author` (`author_id`),
KEY `idx_category` (`category_id`),
KEY `idx_published` (`published_at`),
KEY `idx_featured` (`is_featured`, `published_at`),
CONSTRAINT `fk_article_author`
FOREIGN KEY (`author_id`) REFERENCES `{PREFIX}_users` (`uid`)
ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT `fk_article_category`
FOREIGN KEY (`category_id`) REFERENCES `{PREFIX}_mymodule_categories` (`id`)
ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Catégories (un)
CREATE TABLE `{PREFIX}_mymodule_categories` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(255) NOT NULL
);
-- Articles (plusieurs)
CREATE TABLE `{PREFIX}_mymodule_articles` (
`id` VARCHAR(26) PRIMARY KEY,
`category_id` INT UNSIGNED,
FOREIGN KEY (`category_id`) REFERENCES `{PREFIX}_mymodule_categories` (`id`)
);
-- Articles
CREATE TABLE `{PREFIX}_mymodule_articles` (
`id` VARCHAR(26) PRIMARY KEY,
`title` VARCHAR(255) NOT NULL
);
-- Tags
CREATE TABLE `{PREFIX}_mymodule_tags` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(100) NOT NULL,
UNIQUE KEY (`name`)
);
-- Table de jonction
CREATE TABLE `{PREFIX}_mymodule_article_tags` (
`article_id` VARCHAR(26) NOT NULL,
`tag_id` INT UNSIGNED NOT NULL,
PRIMARY KEY (`article_id`, `tag_id`),
FOREIGN KEY (`article_id`) REFERENCES `{PREFIX}_mymodule_articles` (`id`) ON DELETE CASCADE,
FOREIGN KEY (`tag_id`) REFERENCES `{PREFIX}_mymodule_tags` (`id`) ON DELETE CASCADE
);
CREATE TABLE `{PREFIX}_mymodule_categories` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`parent_id` INT UNSIGNED DEFAULT NULL,
`name` VARCHAR(255) NOT NULL,
`path` VARCHAR(1000) COMMENT 'Materialized path: /1/5/12/',
`depth` TINYINT UNSIGNED DEFAULT 0,
KEY `idx_parent` (`parent_id`),
KEY `idx_path` (`path`(255)),
FOREIGN KEY (`parent_id`) REFERENCES `{PREFIX}_mymodule_categories` (`id`)
ON DELETE SET NULL
);
ScénarioType d’index
Clé primairePRIMARY
Contrainte uniqueUNIQUE
Clé étrangèreKEY régulier
Colonne clause WHEREKEY régulier
Colonne ORDER BYKEY régulier
Recherche en texte intégralFULLTEXT

L’ordre est important - colonne la plus sélective d’abord :

-- Bien : correspond à WHERE status = 'published' ORDER BY created_at
KEY `idx_status_created` (`status`, `created_at`)
-- Optimisation de requête
SELECT * FROM articles
WHERE status = 'published'
ORDER BY created_at DESC

Inclure toutes les colonnes interrogées pour éviter la recherche de table :

-- Couvre : SELECT title, status FROM articles WHERE author_id = ?
KEY `idx_author_covering` (`author_id`, `title`, `status`)
migrations/001_create_articles.php
<?php
return new class {
public function up(\XoopsDatabase $db): void
{
$prefix = $db->prefix('mymodule_articles');
$sql = "CREATE TABLE IF NOT EXISTS `{$prefix}` (
`id` VARCHAR(26) NOT NULL,
`title` VARCHAR(255) NOT NULL,
`created_at` DATETIME NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
$db->queryF($sql);
}
public function down(\XoopsDatabase $db): void
{
$prefix = $db->prefix('mymodule_articles');
$db->queryF("DROP TABLE IF EXISTS `{$prefix}`");
}
};
migrations/002_add_status_column.php
public function up(\XoopsDatabase $db): void
{
$table = $db->prefix('mymodule_articles');
$db->queryF("ALTER TABLE `{$table}` ADD COLUMN `status` ENUM('draft','published') DEFAULT 'draft' AFTER `title`");
$db->queryF("CREATE INDEX `idx_status` ON `{$table}` (`status`)");
}
  1. Utiliser InnoDB - Prend en charge les transactions et les clés étrangères
  2. UTF8MB4 - Support Unicode complet incluant les emojis
  3. NOT NULL - Utiliser les valeurs par défaut au lieu de colonnes nullables si possible
  4. Types appropriés - Ne pas utiliser TEXT pour les chaînes courtes
  5. Indexer avec parcimonie - Chaque index ralentit les écritures
  6. Documenter le schéma - Ajouter un COMMENT aux colonnes
  7. Éviter les mots réservés - Ne pas utiliser order, group, key comme noms de colonne
  • ../Opérations-de-base-de-données - Exécution de requête
  • ../../04-API-Reference/Database/Critères - Construction de requête
  • Migrations - Versioning du schéma
  • ../../01-Getting-Started/Configuration/Optimisation-des-performances - Optimisation des requêtes