-
Notifications
You must be signed in to change notification settings - Fork 0
QueryBuilder
Rôle de la classe : Le QueryBuilder ne se connecte jamais à la base de données. Son unique fonction est de construire dynamiquement une chaîne de requête ($query) et un tableau de paramètres sécurisés ($params) pour les transmettre au Layer.
Namespace : Magepattern\Component\Database\QueryBuilder
- Lecture de données (SELECT) A. Récupérer un enregistrement unique (fetch) Idéal pour afficher une fiche produit détaillée ou vérifier l'existence d'un utilisateur. La méthode fetch() du Layer retourne un tableau simple à une dimension (ou false en cas d'erreur/absence).
use Magepattern\Component\Database\QueryBuilder;
use Magepattern\Component\Database\Layer;
$qb = (new QueryBuilder())
->select(['id_user', 'firstname', 'email', 'role'])
->from('mc_users')
->where('id_user = :id', ['id' => 42])
->limit(1);
$query = $qb->getSql();
$params = $qb->getParams();
$user = Layer::getInstance()->fetch($query, $params);
if ($user) {
echo "Bonjour " . $user['firstname'];
}B. Récupérer une liste d'enregistrements (fetchAll) Indispensable pour les catalogues, les listes avec filtres dynamiques (whereIn) et les tris spécifiques (orderByField). Le Layer retourne un tableau multidimensionnel.
use Magepattern\Component\Database\QueryBuilder;
use Magepattern\Component\Database\Layer;
$categoriesRequises = [10, 15, 22]; // Tableau PHP classique
$qb = (new QueryBuilder())
->select(['p.id_product', 'p.reference_p', 'pc.name_p', 'c.id_cat'])
->from('mc_catalog_product', 'p')
->join('mc_catalog_product_content', 'pc', 'p.id_product = pc.id_product')
->join('mc_catalog', 'c', 'p.id_product = c.id_product')
->where('pc.published_p = 1')
->whereIn('c.id_cat', $categoriesRequises) // Génère IN (:in_0, :in_1, :in_2)
->orderBy('p.price_p', 'DESC');
$query = $qb->getSql();
$params = $qb->getParams();
$products = Layer::getInstance()->fetchAll($query, $params);
foreach ($products as $product) {
echo $product['name_p'] . "<br>";
}- Modification de données (CRUD) Pour l'écriture, vous passez directement vos tableaux PHP associatifs (['colonne' => 'valeur']) au constructeur. Le QueryBuilder crée automatiquement les marqueurs (ex: :ins_0, :upd_1) pour bloquer toute injection SQL.
C. Insérer des données (insert) Utilisez cette méthode pour créer de nouvelles entrées. Le Layer retourne un booléen (true ou false).
use Magepattern\Component\Database\QueryBuilder;
use Magepattern\Component\Database\Layer;
$nouveauProduit = [
'price_p' => 29.99,
'reference_p' => 'TSHIRT-RED',
'price_promo_p' => 24.99,
'date_register' => date('Y-m-d H:i:s')
];
$qb = (new QueryBuilder())->insert('mc_catalog_product', $nouveauProduit);
$query = $qb->getSql();
$params = $qb->getParams();
if (Layer::getInstance()->insert($query, $params)) {
// Optionnel : Récupérer l'ID généré par la base de données
$newId = Layer::getInstance()->lastInsertId();
echo "Produit créé avec l'ID $newId";
}D. Mettre à jour des données (update) Modifie une ou plusieurs lignes existantes. N'oubliez jamais d'ajouter une condition where() pour ne pas altérer toute la table.
use Magepattern\Component\Database\QueryBuilder;
use Magepattern\Component\Database\Layer;
$miseAJour = [
'published_p' => 0,
'seo_title_p' => 'T-shirt Rouge (Rupture)'
];
$qb = (new QueryBuilder())
->update('mc_catalog_product_content', $miseAJour)
->where('id_product = :id_product', ['id_product' => 105])
->where('id_lang = :id_lang', ['id_lang' => 1]);
$query = $qb->getSql();
$params = $qb->getParams();
if (Layer::getInstance()->update($query, $params)) {
echo "Contenu mis à jour avec succès.";
}E. Supprimer des données (delete) Supprime des lignes de la base de données. Très pratique à combiner avec whereIn pour des nettoyages de masse en toute sécurité.
use Magepattern\Component\Database\QueryBuilder;
use Magepattern\Component\Database\Layer;
$imagesObsolètes = [401, 402, 403, 405];
$qb = (new QueryBuilder())
->delete('mc_catalog_product_img')
->whereIn('id_img', $imagesObsolètes);
$query = $qb->getSql();
$params = $qb->getParams();
if (Layer::getInstance()->delete($query, $params)) {
echo "Images supprimées de la base.";
}- Les Transactions (Garantir l'intégrité) Les transactions sont indispensables dès que vous devez exécuter plusieurs requêtes liées. Elles garantissent le principe du "Tout ou rien" : si une seule étape échoue, la base de données revient à son état initial.
A. Cas Classique : Création de données liées (Parent/Enfant) C'est le scénario le plus courant : insérer un produit, récupérer son ID, puis insérer ses déclinaisons ou contenus.
$layer = Layer::getInstance();
if (!$layer->beginTransaction()) return false;
try {
// 1. Insertion du Parent
$qb = (new QueryBuilder())->insert('mc_product', ['ref' => 'ABC']);
if (!$layer->insert($qb->getSql(), $qb->getParams())) throw new \Exception("Parent error");
$parentId = $layer->lastInsertId(); // Récupération de l'ID généré
// 2. Insertion de l'Enfant utilisant l'ID du parent
$qbDesc = (new QueryBuilder())->insert('mc_product_lang', [
'id_product' => $parentId,
'name' => 'Mon Produit'
]);
if (!$layer->insert($qbDesc->getSql(), $qbDesc->getParams())) throw new \Exception("Child error");
$layer->commit(); // Tout est OK
} catch (\Exception $e) {
$layer->rollBack(); // On annule tout
Logger::getInstance()->log($e->getMessage(), 'db');
}B. Cas Complexe : Traitement Hybride (Tables Temporaires) Ce scénario montre comment mélanger du SQL brut (pour la structure système) et le QueryBuilder (pour les données dynamiques). Utile pour les calculs lourds ou les tris aléatoires.
$layer = Layer::getInstance();
if (!$layer->beginTransaction()) return false;
try {
// 1. Structure : SQL Brut (Exec)
if ($layer->exec('CREATE TEMPORARY TABLE page_map (...)') === false) throw new \Exception('TMP table failed');
if ($layer->exec('SET @id = 0') === false) throw new \Exception('Variable failed');
// 2. Données Dynamiques : QueryBuilder + SQL Brut
$qb = (new QueryBuilder())
->select(['@id := @id + 1', 'p.id_pages'])
->from('mc_cms_page', 'p')
->where('p.active = 1');
$query = 'INSERT INTO page_map ' . $qb->getSql();
if (!$layer->insert($query, $qb->getParams())) throw new \Exception('Insert map failed');
// 3. Récupération des résultats
$results = $layer->fetchAll("SELECT * FROM page_map JOIN ...");
if ($results === false) throw new \Exception('Fetch failed');
$layer->commit();
return $results;
} catch (\Exception $e) {
$layer->rollBack();
return [];
}C. Cas Hybride : Insertion par SELECT et liaison d'ID Ce scénario est utile lorsque la première insertion ne provient pas d'un formulaire (tableau de données), mais d'une autre table, tout en ayant besoin de l'ID généré pour une table tierce.
$layer = Layer::getInstance();
if (!$layer->beginTransaction()) return false;
try {
// 1. Première insertion en SQL Brut (Logique spécifique d'archivage ou de copie)
// On copie un produit d'une table "archive" vers "mc_product"
$queryCopy = "INSERT INTO mc_catalog_product (reference_p, price_p, date_register)
SELECT reference_p, price_p, NOW()
FROM mc_archive_products WHERE id_archive = :id";
if (!$layer->insert($queryCopy, ['id' => $params['id_archive']])) {
throw new \Exception("Erreur lors de la copie de l'archive.");
}
// 2. Récupération de l'ID créé par la requête brute
$newId = $layer->lastInsertId();
// 3. Seconde insertion avec QueryBuilder (Données provenant du script)
// On lie ce nouveau produit à une catégorie spécifique reçue en paramètre
$qbLink = (new QueryBuilder())->insert('mc_catalog', [
'id_product' => $newId,
'id_cat' => $params['id_cat'],
'default_c' => 1
]);
if (!$layer->insert($qbLink->getSql(), $qbLink->getParams())) {
throw new \Exception("Erreur lors de la liaison à la catégorie.");
}
$layer->commit();
return $newId;
} catch (\Exception $e) {
$layer->rollBack();
Logger::getInstance()->log($e->getMessage(), 'db');
return false;
}D. Cas Typique : Insertion avec liaison immédiate (Relations) Ce scénario remplace l'ancienne méthode qui consistait à utiliser des variables MySQL (SET @id = LAST_INSERT_ID()). Désormais, PHP récupère l'ID généré en temps réel pour l'injecter dans la requête suivante. C'est plus propre, plus rapide, et beaucoup plus facile à déboguer.
use Magepattern\Component\Database\QueryBuilder;
use Magepattern\Component\Database\Layer;
use Magepattern\Component\Debug\Logger;
// Paramètres reçus : $params['id_lang'], $params['name_tag'], $params['id_news']
$layer = Layer::getInstance();
if (!$layer->beginTransaction()) return false;
try {
// 1. Insertion du nouveau Tag (QueryBuilder)
$qbTag = (new QueryBuilder())->insert('mc_news_tag', [
'id_lang' => $params['id_lang'],
'name_tag' => $params['name_tag']
]);
if (!$layer->insert($qbTag->getSql(), $qbTag->getParams())) {
throw new \Exception("Erreur lors de la création du tag.");
}
// 2. Récupération immédiate de l'ID généré (Remplace SET @tag_id = LAST_INSERT_ID())
$newTagId = $layer->lastInsertId();
// 3. Création de la relation (QueryBuilder)
// On utilise l'ID de la news (reçu) et le nouvel ID du tag (généré)
$qbRel = (new QueryBuilder())->insert('mc_news_tag_rel', [
'id_news' => $params['id_news'],
'id_tag' => $newTagId
]);
if (!$layer->insert($qbRel->getSql(), $qbRel->getParams())) {
throw new \Exception("Erreur lors de la liaison du tag à la news.");
}
// 4. Validation finale
$layer->commit();
return true;
} catch (\Exception $e) {
// 5. Annulation propre
$layer->rollBack();
Logger::getInstance()->log("Transaction annulée : " . $e->getMessage(), 'database', 'error');
return false;
}