Aller au contenu principal

SQL dans un repository

Comment utiliser le langage SQL dans une méthode d'un repository pour faire des requêtes dans la base de données

Notions théoriques

Bien que Doctrine offre des méthodes puissantes pour interroger la base de données, il peut parfois être nécessaire d'écrire des requêtes SQL brutes pour des cas d'utilisation spécifiques ou pour des raisons de performance.

Symfony permet d'exécuter facilement des requêtes SQL personnalisées au sein d'un repository.

Les principales raisons d'utiliser du SQL brut sont :

  • Optimisation des performances pour des requêtes simples
  • Utilisation de fonctions spécifiques à la base de données
  • Exécution de requêtes qui ne correspondent pas au modèle objet de Doctrine

Exemple

Voici un exemple de requête dans la base de données en utilisant DQL (Doctrine Query Language) dans un repository Symfony :

use App\Entity\Player;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

class PlayerRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, Player::class);
}

public function findTopPlayers($limit = 10)
{
$entityManager = $this->getEntityManager();

$dql = "SELECT p.id, p.name, p.score
FROM App\Entity\Player p
ORDER BY p.score DESC";

$query = $entityManager->createQuery($dql)
->setMaxResults($limit);

return $query->getResult();
}
}

Etudions ce code DQL

$entityManager = $this->getEntityManager();

Nous récupérons l'EntityManager directement depuis le repository.

$dql = "SELECT p.id, p.name, p.score
FROM App\Entity\Player p
ORDER BY p.score DESC";

Nous définissons notre requête DQL. Notez que nous utilisons le nom complet de l'entité (App\Entity\Player) et un alias (p). La syntaxe est très similaire à SQL, mais nous travaillons avec des entités plutôt que des tables.

$query = $entityManager->createQuery($dql)
->setMaxResults($limit);

Nous créons la requête à partir du DQL et définissons le nombre maximum de résultats avec setMaxResults(). C'est l'équivalent de LIMIT en SQL.

return $query->getResult();

Nous exécutons la requête et retournons les résultats. Par défaut, getResult() retourne un tableau d'objets hydratés (dans ce cas, des objets partiels contenant seulement id, name et score).

L'avantage de DQL est qu'il est indépendant de la base de données et travaille directement avec les entités. Il offre également une meilleure intégration avec l'ORM Doctrine, permettant par exemple de retourner facilement des objets hydratés plutôt que de simples tableaux associatifs.

La même chose en SQL

Il est parfois nécessaire (ou plus simple) d'écrire sa requête au format SQL.

Voici un exemple de repository utilisant une requête SQL brute simple dans Symfony :

use App\Entity\Player;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Doctrine\ORM\EntityManagerInterface;

class PlayerRepository extends ServiceEntityRepository
{
private $entityManager;

public function __construct(ManagerRegistry $registry, EntityManagerInterface $entityManager)
{
parent::__construct($registry, Player::class);
$this->entityManager = $entityManager;
}

public function findTopPlayers($limit = 10)
{
$conn = $this->entityManager->getConnection();

$sql = '
SELECT id, name, score
FROM player
ORDER BY score DESC
LIMIT :limit
';

$stmt = $conn->prepare($sql);
$result = $stmt->executeQuery(['limit' => $limit]);

return $result->fetchAllAssociative();
}
}

Etudions ce code

$conn = $this->entityManager->getConnection();

Cette ligne récupère la connexion à la base de données à partir de l'EntityManager injecté.

$sql = '
SELECT id, name, score
FROM player
ORDER BY score DESC
LIMIT :limit
';

Nous définissons notre requête SQL personnalisée. Elle sélectionne les joueurs, les trie par score décroissant et limite le nombre de résultats.

$stmt = $conn->prepare($sql);
$result = $stmt->executeQuery(['limit' => $limit]);

Nous préparons et exécutons la requête en passant le paramètre limit.

return $result->fetchAllAssociative();

Enfin, nous récupérons tous les résultats sous forme de tableau associatif.

Comparatif DQL / SQL

Voici un comparatif des avantages et inconvénients de DQL (Doctrine Query Language) par rapport à SQL :

Avantages de DQL

  • Indépendance de la base de données : DQL est traduit en SQL spécifique à chaque système de base de données, permettant une portabilité du code entre différents SGBD.

  • Travail avec des objets : DQL manipule directement les entités et leurs propriétés, ce qui s'intègre mieux avec le modèle objet de l'application.

  • Abstraction du schéma : Les requêtes DQL sont indépendantes du schéma de base de données sous-jacent, facilitant les changements de structure sans impacter le code.

  • Sécurité accrue : DQL gère automatiquement l'échappement des paramètres, réduisant les risques d'injection SQL.

  • Intégration avec l'ORM : DQL s'intègre parfaitement avec les fonctionnalités de Doctrine comme le chargement différé (lazy loading) ou le cache.

Inconvénients de DQL

  • Courbe d'apprentissage : DQL nécessite d'apprendre une nouvelle syntaxe, même si elle est proche de SQL.

  • Fonctionnalités limitées : Certaines fonctionnalités avancées de SQL peuvent ne pas être disponibles en DQL.

  • Performance potentiellement moindre : Dans certains cas complexes, les requêtes SQL brutes peuvent être optimisées plus finement que leur équivalent DQL.

Avantages de SQL

  • Contrôle total : SQL offre un contrôle complet sur les requêtes et permet d'utiliser toutes les fonctionnalités spécifiques à la base de données.

  • Performance optimale : Pour des requêtes très complexes, SQL peut offrir de meilleures performances car il est directement exécuté par la base de données.

  • Familiarité : SQL est largement connu et utilisé, facilitant la collaboration entre développeurs et administrateurs de bases de données.

Inconvénients de SQL

  • Dépendance à la base de données : Les requêtes SQL sont souvent spécifiques à un système de base de données particulier, limitant la portabilité.

  • Risques de sécurité : L'utilisation de SQL brut augmente les risques d'injection SQL si les paramètres ne sont pas correctement échappés.

  • Découplage du modèle objet : SQL travaille directement avec les tables et les colonnes, ce qui peut créer un décalage avec le modèle objet de l'application.

En conclusion, DQL est généralement préféré dans le contexte de Doctrine et des applications orientées objet pour sa portabilité et son intégration avec l'ORM, tandis que SQL reste incontournable pour des requêtes très spécifiques ou des optimisations poussées.

Test de mémorisation/compréhension


Comment injecte-t-on l'EntityManager dans un repository Symfony ?


Comment obtient-on la connexion à la base de données dans un repository Symfony ?


Comment passe-t-on des paramètres à une requête SQL préparée dans Symfony ?


Quelle clause SQL utilise-t-on pour limiter le nombre de résultats retournés ?


Quelle méthode utilise-t-on pour récupérer tous les résultats d'une requête SQL sous forme de tableau associatif dans Symfony ?


Quelle méthode permet de récupérer l'EntityManager dans un repository Symfony ?


Quel est l'avantage principal de DQL par rapport à SQL ?


Quel type de résultats retourne la méthode getResult() en DQL ?


Quelle est la syntaxe correcte pour limiter les résultats d'une requête SQL ?


Quel est un inconvénient de DQL mentionné dans le cours ?


Quel est l'usage de la méthode fetchAllAssociative() dans un repository utilisant SQL ?


Pourquoi utiliser des requêtes SQL brutes dans un repository Symfony ?


Quelle méthode est utilisée pour exécuter une requête préparée en SQL dans Symfony ?


Quel est un inconvénient de l'utilisation de SQL brut mentionné dans le cours ?


Quel type de requêtes est DQL principalement conçu pour exécuter ?


TP pour réfléchir et résoudre des problèmes

Votre défi pour aujourd'hui consiste à écrire une requête SQL brute dans le repository.

Créez une méthode dans le PlayerRepository qui utilise une requête SQL brute pour récupérer les joueurs d'une catégorie spécifique, triés par score décroissant.

La méthode doit accepter la catégorie et une limite optionnelle comme paramètres.

Une solution