Retour

Introduction à Doctrine Query Builder avec MongoDB

6 oct. 2015
8mn

Bonjour à tous,

Je vais parler du query builder (constructeur de requête) Doctrine pour faire des requêtes vers une base de données MongoDB.

Si vous voulez suivre les exemples et les tester, il est nécessaire d'installer le bundle DoctrineMongoDBBundle.

Qu'est-ce que c'est ?

Le query builder est une classe qui va permettre de créer des requêtes à la base de données en passant par des objets et méthodes. Il facilite l'écriture de requête complexe.

Prenons un exemple avec une liste d'articles dans la collection "articles" :

[ { "title": "Mon article", "tags": [ { "label": "article" }, { "label": "test" } ], "publication": { "status": true, "startDate": "2015-10-04T11:00:00+0200", "endDate": "2016-10-04T11:00:00+0200" } }, { "title": "Mon second article", "tags": [ { "label": "article" }, { "label": "second" } ], "publication": { "status": true, "startDate": "2015-01-04T11:00:00+0200", "endDate": "2015-02-04T11:00:00+0200" } } ]

Je veux avoir l'article avec le titre "Mon second article" en simple query mongo:

db.articles.find({"title":"Mon second article"});

Avec le query builder:

<?php $article = $this->createQueryBuilder() ->find() ->field('title')->equals('Mon second article') ->getQuery() ->execute();

Avec le query builder, on va rester dans le monde de l'objet et manipuler exclusivement des objets.

Le query builder et Symfony

Dans Symfony, toutes les méthodes qui vont effectuer des requêtes à la base de données se situent dans les repository.

Je veux créer une méthode pour retrouver les articles avec un tag spécifique. Je vais donc créer une méthode "getArticleByTag" dans le repository tag.

<?php namespace App\Appbundle\Repository; use Doctrine\ODM\MongoDB\DocumentRepository; class ArticleRepository extends DocumentRepository { public function getArticleByTag($tag) { return $this->createQueryBuilder() ->find() ->field('tag')->equals($tag) ->getQuery() ->execute(); } }

$this->createQueryBuilder() va me donner une instance de Doctrine\ODM\MongoDB\Query\Builder . Avec cette instance, je vais avoir accès à un ensemble d'expressions pour construire ma requête. Les expressions, ce sont les différents opérateurs Mongo. Dans cet exemple, ->find() ->field() ->equal() sont des expressions. Chacune d'elles sont des instances de Doctrine\ODM\MongoDB\Query\Expr .

Pour mettre à jour un document, j'utilise le même principe.

<?php namespace App\Appbundle\Repository; use Doctrine\ODM\MongoDB\DocumentRepository; class ArticleRepository extends DocumentRepository { public function updateTagArticle($title, array $tags) { return $this->createQueryBuilder() ->update() ->field('title')->equals($title) ->field('tag')->set($tags) ->getQuery() ->execute(); } }

Pour mettre à jour un article qui a pour titre "Mon article", je dois indiquer que je veux le document avec un titre égal à "Mon article" : ->field('title')->equals("Mon article") . Ensuite, je mets ->field('tags')->set($tags) pour mettre à jour mon champs "tags".

Ajouter des expressions

Le builder de base donne un bon nombre d'expressions. Mais parfois, ce n'est pas suffisant. Pour reprendre l'exemple avec les articles, je veux avoir tous les articles publiés à la date d'aujourd'hui. Je vais donc ajouter une expression isPublished(\DateTime $datetime) .

Je vais étendre la classe Doctrine\ODM\MongoDB\Query\Expr et ajouter ma méthode.

<?php namespace App\AppBundle\Query\Expr; use Doctrine\ODM\MongoDB\Query\Expr as BaseExpr; class Expr extends BaseExpr { public function isPublished(\DateTime $datetime) { $this->query['$and'] = [ ['publication.status' => ['$equals' => true]], ['publication.startDate' => ['$lte' => $datetime->format(\DateTime::ISO8601)]], ['publication.endDate' => ['$gte' => $datetime->format(\DateTime::ISO8601)]] ] } }

Je n'oublie pas de surcharger la création du query builder pour pouvoir utiliser cette nouvelle classe expression.

<?php namespace App\AppBundle\Query; use Doctrine\ODM\MongoDB\Query\Builder as BaseBuilder; use App\AppBundle\Query\Expr; class Builder extends BaseBuilder { public function __construct(DocumentManager $dm, $documentName = null) { $this->expr = new Expr($dm); parent::__construct($dm, $documentName); } }
<?php namespace App\AppBundle\Repository; use App\AppBundle\Query\Builder; use Doctrine\ODM\MongoDB\DocumentRepository as BaseDocumentRepository; classe DocumentRepository extends BaseDocumentRepository { public function createQueryBuilder($documentName = null) { return new Builder($this->dm, $this->documentName); } }

Et je peux utiliser ma nouvelle expression dans mon query builder.

<?php namespace App\Appbundle\Repository; use App\AppBundle\Repository\DocumentRepository; class ArticleRepository extends DocumentRepository { public function getPublishedArticle() { return $this->createQueryBuilder() ->find() ->isPublished(new \DateTime("2015-10-02T11:00:00+0200")) ->getQuery() ->execute(); } }

Cette requête doit me retourner les articles qui sont publiés en date du 02 octobre 2015 à 11h00.

La requête Mongo générée est la suivante :

{ "$and": [ { "publication.status": { "$equals": true } }, { "publication.startDate": { "$lte": new ISODate("2015-10-02T11:00:00+0200") } }, { "publication.endDate": { "$gte": new ISODate("2015-10-02T11:00:00+0200") } } ] }

Quick tip

Le query builder va hydrater les objets Doctrine avec les données. Sur des objets complexes, ce processus est gourmand en ressource. Pour gagner en performance, il est possible de désactiver cette hydratation.

<?php

$this->createQueryBuilder()
    ->hydrate(false)
    ->find()
    ->getQuery()
    ->execute();

Conclusion

Cet article vous a montré comment utiliser le query builder de Doctrine sur une base de données MongoDB. Il facilite l'écriture de requêtes plus ou moins complexes tout en restant dans un style objet. Étendre et ajouter des expressions permet de simplifier des requêtes métier complexes.

Référence : http://docs.doctrine-project.org/projects/doctrine-mongodb-odm/en/latest/reference/query-builder-api.html


Articles sur le même thème

Comment créer de la dette technique dès le début d’un nouveau projet ?

Quand on arrive sur un projet existant, on doit souvent subir une dette technique qui nous fait perdre du temps et qui nous rend fou au point de vérifier qui a fait le code. Vous aussi vous voulez entrer dans la postérité lors d’un git blame et mal concevoir votre produit ?

9 août 20238mnMarianne Joseph-Géhannin

Découvrez Eleven Labs

Notre site pour mieux nous connaître

J'y vais

Contact

Eleven Labs - Paris

102, rue du Faubourg Saint Honoré

75008 Paris

Eleven Labs - Nantes

42, rue la Tour d'Auvergne

44200 Nantes

Eleven Labs - Montréal

1155, Metcalfe St Suite 1500

Montréal, QC H3B 2V6, Canada

business@eleven-labs.com

01.82.83.11.75