🗄️ MySQL Class

Documentation et exemples d'utilisation de la classe PHP pour MariaDB/MySQL

v2.0 - Avec gestion d'erreurs, pagination, Query Builder avancé

📑 Table des matières

⚙️ Configuration des Serveurs

La classe permet de gérer plusieurs serveurs MySQL/MariaDB. Utilisez addServer() pour configurer chaque connexion.

Paramètres

Paramètre Type Requis Description
$name string Oui Nom identifiant du serveur
$host string Oui Adresse du serveur (ex: localhost)
$database string Oui Nom de la base de données
$user string Oui Nom d'utilisateur
$password string Oui Mot de passe
$port int Non Port (défaut: 3306)
$options array Non Options PDO supplémentaires

Exemple : Configuration de plusieurs serveurs

require_once 'MySQL.php';

// Configuration du serveur principal (local)
MySQL::addServer(
    'default',           // Nom du serveur
    'localhost',         // Hôte
    'ma_base',           // Base de données
    'root',              // Utilisateur
    'mon_password',      // Mot de passe
    3306                 // Port
);

// Configuration d'un second serveur (distant)
MySQL::addServer(
    'remote',
    '192.168.1.100',
    'base_distante',
    'admin',
    'password_secure',
    3306
);

// Activer le mode debug (affiche les détails des erreurs SQL)
MySQL::setDebug(true);

// Basculer entre les serveurs
MySQL::use('default');
$users = MySQL::select("SELECT * FROM users");

MySQL::use('remote');
$products = MySQL::select("SELECT * FROM products");

🛡️ Gestion des Erreurs

La classe utilise une exception personnalisée MySQLException avec des types d'erreur spécifiques et un système de logging automatique.

Nouveau ! Gestion avancée des erreurs avec MySQLException, log d'erreurs, et mode debug.

Types d'erreur

Type Description
SERVER_NOT_FOUND Serveur non configuré
CONNECTION_FAILED Échec de connexion à la base
SQL_ERROR Erreur d'exécution SQL
EMPTY_DATA Données vides pour INSERT/UPDATE
MISSING_WHERE Clause WHERE manquante pour DELETE
TRANSACTION_ROLLBACK Transaction annulée

Capturer et gérer les erreurs

try {
    $user = MySQL::selectOne(
        "SELECT * FROM userss WHERE id = ?",  // ⚠️ 'userss' n'existe pas
        [42]
    );
} catch (MySQLException $e) {
    // Informations de base
    echo "Message: " . $e->getMessage() . "<br>";
    echo "Type: " . $e->getErrorType() . "<br>";
    echo "Code: " . $e->getCode() . "<br>";

    // Vérifier le type d'erreur
    if ($e->isConnectionError()) {
        echo "Problème de connexion au serveur !";
    } elseif ($e->isSqlError()) {
        echo "Erreur dans la requête SQL.";
    }

    // Récupérer tous les détails sous forme de tableau
    $details = $e->toArray();
    print_r($details);
}

Log d'erreurs automatique

// Toutes les erreurs SQL sont automatiquement loguées
// Récupérer le log complet
$errors = MySQL::getErrorLog();
foreach ($errors as $error) {
    echo "[{$error['timestamp']}] Erreur SQL : " . $error['error'] . "<br>";
    echo "Requête : " . $error['query'] . "<br>";
    echo "Serveur : " . $error['server'] . "<br>";
    echo "Temps : " . round($error['time'] * 1000, 2) . " ms<br><br>";
}

// Récupérer la dernière erreur
$lastError = MySQL::getLastError();
if ($lastError) {
    echo "Dernière erreur : " . $lastError['error'];
}

// Vider le log
MySQL::clearErrorLog();

Mode Debug

// Activer le mode debug (affiche la requête dans le message d'erreur)
MySQL::setDebug(true);

try {
    MySQL::select("SELECT * FROM table_inexistante");
} catch (MySQLException $e) {
    // En mode debug, le message contient la requête complète
    echo $e->getMessage();
    // "Erreur SQL [42S02]: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'test.table_inexistante' doesn't exist
    // Requête: SELECT * FROM table_inexistante"
}

// En production, désactiver le debug pour ne pas exposer les requêtes
MySQL::setDebug(false);
// Le message sera simplement : "Erreur d'exécution de la requête SQL."

Gestion des erreurs de connexion

try {
    MySQL::addServer('bad', 'wrong.host', 'db', 'user', 'pass');
    MySQL::use('bad');
    MySQL::select("SELECT 1");
} catch (MySQLException $e) {
    if ($e->isConnectionError()) {
        echo "Impossible de se connecter au serveur. Vérifiez la configuration.";
        // Logger l'erreur, envoyer une alerte, etc.
    }
}

🔍 SELECT - Lecture de données

select() - Récupérer plusieurs lignes

Exécute une requête SELECT et retourne toutes les lignes sous forme de tableau associatif.

// Récupérer tous les utilisateurs
$users = MySQL::select("SELECT * FROM users");

foreach ($users as $user) {
    echo $user['name'] . " - " . $user['email'] . "<br>";
}

// Avec des paramètres (requête préparée)
$activeUsers = MySQL::select(
    "SELECT * FROM users WHERE status = ? AND created_at > ?",
    ['active', '2024-01-01']
);

// Avec LIKE
$search = '%john%';
$results = MySQL::select(
    "SELECT * FROM users WHERE name LIKE ?",
    [$search]
);

selectOne() - Récupérer une seule ligne

// Récupérer un utilisateur spécifique
$user = MySQL::selectOne(
    "SELECT * FROM users WHERE id = ?",
    [42]
);

if ($user) {
    echo "Nom : " . $user['name'];
} else {
    echo "Utilisateur non trouvé";
}

selectValue() - Récupérer une seule valeur

// Compter le nombre d'utilisateurs
$count = MySQL::selectValue("SELECT COUNT(*) FROM users");

// Somme des ventes
$total = MySQL::selectValue(
    "SELECT SUM(amount) FROM orders WHERE status = ?",
    ['completed']
);

count() - Compter rapidement

Nouveau ! Méthode count() pour compter les enregistrements.
// Compter tous les utilisateurs
$total = MySQL::count('users');

// Compter avec condition
$activeCount = MySQL::count('users', 'status = ?', ['active']);

// Compter les valeurs distinctes
$uniqueCities = MySQL::count('users', null, [], 'DISTINCT city');

➕ INSERT - Insertion de données

insert() - Insérer une ligne

$newId = MySQL::insert('users', [
    'name'     => 'Jean Dupont',
    'email'    => 'jean@example.com',
    'status'   => 'active',
]);

echo "Nouvel ID : " . $newId;

// Gestion de l'erreur si données vides
try {
    MySQL::insert('users', []);  // ⚠️ Lance une MySQLException
} catch (MySQLException $e) {
    echo "Erreur : " . $e->getMessage();  // "Aucune donnée à insérer."
}

insertBatch() - Insérer plusieurs lignes

$users = [
    ['name' => 'Alice', 'email' => 'alice@example.com'],
    ['name' => 'Bob', 'email' => 'bob@example.com'],
    ['name' => 'Charlie', 'email' => 'charlie@example.com'],
];

$inserted = MySQL::insertBatch('users', $users);
echo "{$inserted} utilisateurs insérés";

✏️ UPDATE - Mise à jour de données

$affected = MySQL::update(
    'users',
    ['name' => 'Jean Martin', 'email' => 'jean.m@example.com'],
    'id = ?',
    [42]
);

// Erreur si données vides
try {
    MySQL::update('users', [], 'id = 1', []);
} catch (MySQLException $e) {
    echo $e->getMessage();  // "Aucune donnée à mettre à jour."
}

🗑️ DELETE - Suppression de données

$deleted = MySQL::delete(
    'users',
    'id = ?',
    [42]
);

// ⚠️ WHERE obligatoire
try {
    MySQL::delete('users', '', []);  // Lance une exception
} catch (MySQLException $e) {
    echo $e->getMessage();  // "Clause WHERE requise pour la suppression."
}

⚡ RAW - Requêtes SQL brutes

// Requête complexe avec JOIN
$results = MySQL::raw("
    SELECT u.*, COUNT(o.id) as order_count
    FROM users u
    LEFT JOIN orders o ON u.id = o.user_id
    WHERE u.status = ?
    GROUP BY u.id
    HAVING order_count > ?
    ORDER BY order_count DESC
    LIMIT ?
", ['active', 5, 10])->fetchAll();

// CREATE TABLE
MySQL::raw("
    CREATE TABLE IF NOT EXISTS logs (
        id INT AUTO_INCREMENT PRIMARY KEY,
        message TEXT,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    )
");

🔧 Query Builder Avancé

La méthode query() permet de construire des requêtes complexes avec JOIN, GROUP BY, HAVING, ORDER BY et LIMIT sans écrire de SQL brut.

Nouveau ! Query Builder avec support complet des clauses SQL avancées.

Paramètres de query()

Option Type Description
columns string|array Colonnes à sélectionner (défaut: *)
join string|array Clauses JOIN
where string Clause WHERE
whereParams array Paramètres du WHERE
groupBy string|array GROUP BY
having string Clause HAVING
havingParams array Paramètres du HAVING
orderBy string|array ORDER BY
limit int LIMIT
offset int OFFSET

JOIN - Jointures

// INNER JOIN simple
$usersWithOrders = MySQL::query('users', [
    'columns' => ['users.id', 'users.name', 'orders.total'],
    'join'    => 'INNER JOIN orders ON users.id = orders.user_id',
    'where'   => 'users.status = ?',
    'whereParams' => ['active'],
]);

// Multiple JOINs
$results = MySQL::query('orders', [
    'columns' => [
        'orders.id',
        'users.name as user_name',
        'products.name as product_name',
        'order_items.quantity'
    ],
    'join' => [
        'INNER JOIN users ON orders.user_id = users.id',
        'INNER JOIN order_items ON orders.id = order_items.order_id',
        'INNER JOIN products ON order_items.product_id = products.id',
    ],
    'where' => 'orders.status = ?',
    'whereParams' => ['completed'],
]);

// LEFT JOIN avec alias
$users = MySQL::query('users', [
    'columns' => [
        'users.*',
        'COUNT(orders.id) as order_count'
    ],
    'join' => 'LEFT JOIN orders ON users.id = orders.user_id',
    'groupBy' => 'users.id',
]);

GROUP BY et HAVING

// GROUP BY simple
$byStatus = MySQL::query('users', [
    'columns' => ['status', 'COUNT(*) as count'],
    'groupBy' => 'status',
]);

// GROUP BY avec HAVING
$popularProducts = MySQL::query('order_items', [
    'columns' => [
        'product_id',
        'SUM(quantity) as total_sold',
        'AVG(price) as avg_price'
    ],
    'groupBy' => 'product_id',
    'having'  => 'total_sold > ?',
    'havingParams' => [100],
    'orderBy' => 'total_sold DESC',
    'limit'   => 10,
]);

// GROUP BY multiple colonnes
$monthlySales = MySQL::query('orders', [
    'columns' => [
        'YEAR(created_at) as year',
        'MONTH(created_at) as month',
        'COUNT(*) as order_count',
        'SUM(total) as revenue'
    ],
    'where'   => 'status = ?',
    'whereParams' => ['completed'],
    'groupBy' => ['YEAR(created_at)', 'MONTH(created_at)'],
    'having'  => 'revenue > ?',
    'havingParams' => [1000],
    'orderBy' => ['year DESC', 'month DESC'],
]);

ORDER BY et LIMIT

// ORDER BY simple
$sorted = MySQL::query('products', [
    'orderBy' => 'price ASC',
    'limit'   => 20,
]);

// ORDER BY multiple
$users = MySQL::query('users', [
    'where'   => 'status = ?',
    'whereParams' => ['active'],
    'orderBy' => ['created_at DESC', 'name ASC'],
    'limit'   => 50,
    'offset'  => 100,  // Page 3 avec 50 éléments par page
]);

// Combiné : JOIN + WHERE + GROUP BY + HAVING + ORDER BY + LIMIT
$topCustomers = MySQL::query('users', [
    'columns' => [
        'users.id',
        'users.name',
        'users.email',
        'COUNT(orders.id) as total_orders',
        'SUM(orders.total) as total_spent'
    ],
    'join'    => 'INNER JOIN orders ON users.id = orders.user_id',
    'where'   => 'orders.status = ? AND orders.created_at >= ?',
    'whereParams' => ['completed', '2024-01-01'],
    'groupBy' => 'users.id',
    'having'  => 'total_spent >= ?',
    'havingParams' => [500],
    'orderBy' => 'total_spent DESC',
    'limit'   => 10,
]);

🔒 Transactions

Les transactions permettent d'exécuter plusieurs opérations de manière atomique.

Méthode avec callback (recommandée)

try {
    MySQL::transaction(function() {
        $orderId = MySQL::insert('orders', [
            'user_id' => 42,
            'total'   => 299.99,
        ]);

        foreach ($cartItems as $item) {
            MySQL::insert('order_items', [
                'order_id'   => $orderId,
                'product_id' => $item['id'],
                'quantity'   => $item['qty'],
            ]);
        }
    });
} catch (MySQLException $e) {
    echo "Échec : " . $e->getMessage();
}

📊 Statistiques de Performance

// Afficher les statistiques
$stats = MySQL::getStats();
echo "Requêtes : " . $stats['queries'] . "<br>";
echo "Temps : " . round($stats['time'] * 1000, 2) . " ms";

// Vérifier l'état de la connexion
if (MySQL::isConnected()) {
    echo "Connecté au serveur : " . MySQL::current();
}

📝 Exemple Complet : Système de Commandes

Exemple complet combinant toutes les fonctionnalités pour un système de gestion de commandes avec pagination et statistiques.

require_once 'MySQL.php';

// Configuration
MySQL::addServer('default', 'localhost', 'shop', 'root', 'password');
MySQL::setDebug(true);

// ========== CRUD COMMANDES ==========

// Créer une commande avec transaction
function createOrder($userId, $items) {
    return MySQL::transaction(function() use ($userId, $items) {
        $total = array_sum(array_column($items, 'subtotal'));

        $orderId = MySQL::insert('orders', [
            'user_id' => $userId,
            'total'   => $total,
            'status'  => 'pending',
        ]);

        foreach ($items as $item) {
            MySQL::insert('order_items', [
                'order_id'   => $orderId,
                'product_id' => $item['product_id'],
                'quantity'   => $item['quantity'],
                'price'      => $item['price'],
            ]);
        }

        return $orderId;
    });
}

// Lister les commandes avec pagination et JOIN
function getOrders($page = 1, $status = null) {
    $options = [
        'columns' => [
            'orders.id',
            'orders.total',
            'orders.status',
            'orders.created_at',
            'users.name as user_name',
            'users.email',
        ],
        'join'    => 'INNER JOIN users ON orders.user_id = users.id',
        'orderBy' => 'orders.created_at DESC',
    ];

    if ($status) {
        $options['where'] = 'orders.status = ?';
        $options['whereParams'] = [$status];
    }

    return MySQL::paginateQuery('orders', $page, 20, $options);
}

// Statistiques par catégorie
function getCategoryStats() {
    return MySQL::query('products', [
        'columns' => [
            'categories.name as category',
            'COUNT(products.id) as product_count',
            'AVG(products.price) as avg_price',
            'MIN(products.price) as min_price',
            'MAX(products.price) as max_price',
        ],
        'join'    => 'INNER JOIN categories ON products.category_id = categories.id',
        'groupBy' => 'categories.id',
        'having'  => 'product_count >= ?',
        'havingParams' => [5],
        'orderBy' => 'avg_price DESC',
    ]);
}

// ========== UTILISATION ==========

// Créer une commande
try {
    $orderId = createOrder(42, [
        ['product_id' => 1, 'quantity' => 2, 'price' => 29.99, 'subtotal' => 59.98],
        ['product_id' => 3, 'quantity' => 1, 'price' => 99.99, 'subtotal' => 99.99],
    ]);
    echo "Commande #{$orderId} créée !";
} catch (MySQLException $e) {
    echo "Erreur : " . $e->getMessage();
}

// Lister les commandes (page 1)
$orders = getOrders(1, 'completed');
foreach ($orders['data'] as $order) {
    echo "Commande #{$order['id']} - {$order['user_name']} - {$order['total']}€<br>";
}

// Stats
$stats = getCategoryStats();
foreach ($stats as $stat) {
    echo "{$stat['category']}: {$stat['product_count']} produits, moyenne {$stat['avg_price']}€<br>";
}

// Afficher les performances
$perf = MySQL::getStats();
echo "Requêtes SQL : {$perf['queries']} en " . round($perf['time']*1000,2) . "ms";

📖 Référence Rapide

Méthode Description Retour
addServer() Configure un serveur MySQL void
setDebug() Active/désactive le mode debug void
use() Sélectionne le serveur actif void
current() Nom du serveur actif string
isConnected() Vérifie la connexion bool
select() SELECT multiple lignes array
selectOne() SELECT une ligne array|null
selectValue() SELECT une valeur mixed
count() Compter les enregistrements int
insert() INSERT une ligne int (ID)
insertBatch() INSERT multiple lignes int (count)
update() UPDATE des lignes int (affected)
delete() DELETE des lignes int (deleted)
raw() Requête SQL brute PDOStatement
query() Query Builder (JOIN, GROUP BY, etc.) array
paginate() Pagination d'une requête SQL array
paginateQuery() Pagination avec Query Builder array
transaction() Exécute dans une transaction mixed
getStats() Statistiques de performance array
getErrorLog() Log des erreurs SQL array
getLastError() Dernière erreur array|null
clearErrorLog() Vide le log void