Symfony ExpressionLanguage : Comment utiliser ce composant ?
Le composant Symfony ExpressionLanguage : qu'est-ce que c'est ? Quand et comment l'utiliser ? Comment créer des expressions lors de cas plus complexes ?
Sommaire
Sur ce blog, nous avons déjà quelques articles autour de MongoDB, et même s’ils sont encore d’actualité, il n’y en avait pas sur MongoDB dans Symfony, d'où cet article !
MongoDB est une base de données en NoSQL orientée documents. On a plus souvent l’habitude de bases de données relationnelles comme MySQL pour enregistrer les entités sous Symfony. Les Bases orientées Document elles, sont pensées pour un besoin différent : stocker directement un objet complet, sans relation ou répartition sur plusieurs tables (par exemple des contenus comme des articles de journaux/blogs ou des catalogues de produits). Cela permet d’y accéder plus rapidement et il n’y a pas besoin de relationnelle : toutes les informations sont enregistrées dans un JSON ou XML dans une structure souple.
Avant de l’utiliser dans votre projet sous Symfony, il faut installer dans votre docker-compose l’image de MongoDB et le configurer.
On reste sur une configuration simple où les paramètres sont stockés dans le .env.
# .env ###> mongodb ### DATABASE_NAME=documents DATABASE_HOST=database DATABASE_PORT=27017 DATABASE_USER=user DATABASE_PASSWORD=password DATABASE_VERSION=x.x.xx ###< mongodb ###
# docker-compose.yml database: image: mongo:${DATABASE_VERSION} restart: always environment: MONGO_INITDB_ROOT_USERNAME: ${DATABASE_USER} MONGO_INITDB_ROOT_PASSWORD: ${DATABASE_PASSWORD} ports: - ${DATABASE_PORT}:27017 expose: - ${DATABASE_PORT} volumes: - data-documents:/data/db
Pour l’implémentation dans Symfony, je vais proposer deux approches : avec Doctrine, et comme c’est dans l’air du temps, SANS Doctrine !
Pourquoi proposer sans Doctrine ? Il y a certes du Doctrine Bashing depuis quelques années, mais comme MongoDB n’est pas du relationnel, et si vous n’avez pas besoin de l’utiliser comme un objet lors de la récupération, alors pourquoi s’embêter avec un modèle ?
Je ne vais pas répéter la documentation du Bundle qui est bien fait sur l’installation, mais je vais préciser la configuration pour être cohérent avec celle de docker-compose.
# config/packages/doctrine_mongodb.yaml doctrine_mongodb: connections: default: server: 'mongodb://%env(resolve:DOC_DATABASE_HOST)%:%env(resolve:DOC_DATABASE_PORT)%' options: username: '%env(resolve:DOC_DATABASE_USER)%' password: '%env(resolve:DOC_DATABASE_PASSWORD)%' default_database: '%env(resolve:DOC_DATABASE_NAME)%'
Pour cela, il faut créer un objet correspondant au document que vous voulez insérer. On va prendre l’exemple d’un article de blog.
La suite est comme pour une autre base de données : il faut créer l’article, le persister via le DocumentManager et le flusher.
// Article.php
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* @MongoDB\Document(collection="article")
*/
class Article
{
/**
* @MongoDB\Id()
*/
private string $id;
/** @MongoDB\Field(type="string") */
private string $title;
/** @MongoDB\Field(type="string") */
private string $content;
/** @MongoDB\Field(type="string") */
private string $status;
/** @MongoDB\Field(type="date") */
private \DateTime $createdDate;
public function getId(): string
{
return $this->id;
}
public function getTitle(): string
{
return $this->title;
}
public function setTitle(string $title): Article
{
$this->title = $title;
return $this;
}
public function getContent(): string
{
return $this->content;
}
public function setContent(string $content): Article
{
$this->content = $content;
return $this;
}
public function getStatus(): string
{
return $this->status;
}
public function setStatus(string $status): Article
{
$this->status = $status;
return $this;
}
public function getCreatedDate(): \DateTime
{
return $this->createdDate;
}
public function setCreatedDate(\DateTime $createdDate): Article
{
$this->createdDate = $createdDate;
return $this;
}
}
Après dans n'importe quelle classe, il faut déclarer Doctrine\ODM\MongoDB\DocumentManager
dans le __construct()
et appeler persist/flush.
[...]
public function __construct(
public readonly DocumentManager $documentManager
) {
}
public function process(array $data): void
{
$article = new Article();
$article
->setTitle($data['title'])
->setContent($data['content'])
->setStatus($data['status'])
->setCreatedDate($data['createdDate'])
;
$this->documentManager->persist($article);
$this->documentManager->flush();
}
Voici la documentation de MongoDB si vous voulez aller plus loin.
Pour récupérer un document, il y a plusieurs possibilités comme décrites dans la documentation. Ici ça va être simplement par id
et ça se fait en une ligne.
[...]
public function process(int $id): Article
{
return $this->documentManager->find(Article::class, $id);
}
On ne voit pas qu'il s'agit d'une base de données MongoDB, Doctrine masque l'information. Mais cela peut être inutile de créer des objets correspondants aux documents. Et pour cela, il faut s'affranchir de Doctrine.
Après avoir installé le bundle composer require facile-it/mongodb-bundle
, à vous de vérifier et de mettre à jour la configuration.
# config/packages/facile_it_mongodb.yaml mongo_db_bundle: data_collection: '%kernel.debug%' clients: default: hosts: - { host: '%env(resolve:DATABASE_HOST)%', port: '%env(int:DATABASE_PORT)%' } username: '%env(resolve:DATABASE_USER)%' password: '%env(resolve:DATABASE_PASSWORD)%' replicaSet: '' # default null (no replica) ssl: false connectTimeoutMS: 3000 readPreference: primaryPreferred connections: default: client_name: default database_name: '%env(resolve:DATABASE_NAME)%'
Contrairement à la précédente solution, ici, pas besoin d'objet à persister : on utilise un tableau avec les données. À vous de voir si vous voulez vérifier le format et les données dedans.
Ensuite, pour insérer les données, il faut sélectionner la collection (ici article
pour rester sur le thème) et tout simplement insérer le tableau.
[...]
use MongoDB\Database;
[...]
public function __construct(private readonly Database $database)
{
}
public function process(array $data): void
{
$collection = $this->database->selectCollection('article');
$collection->insertOne($data);
}
}
Pour récupérer un document, on procède aussi à la sélection de la collection et le findOne()
permet de rechercher sur n'importe quel champ.
[...]
public function process(int $id): array
{
$collection = $this->database->selectCollection('article');
return $collection->findOne(['id' => $id]);
}
Vous pouvez créer un repository avec l'ensemble des fonctions. Cela vous permettra d'être plus indépendant dans vos services vis-à-vis du choix de la base de données.
Nous sommes restés sur les bases qui sont l'insertion et la récupération d'un document, mais MongoDB a aussi un système d'indexation qui permet de gagner en efficacité pour la recherche de documents.
La documentation pour les deux possibilités est assez claire pour aller plus loin dans son utilisation.
Auteur(s)
Marianne Joseph-Géhannin
Architecte applicatif et lead développeuse PHP/Symfony 🦝
Vous souhaitez en savoir plus sur le sujet ?
Organisons un échange !
Notre équipe d'experts répond à toutes vos questions.
Nous contacterDécouvrez nos autres contenus dans le même thème
Le composant Symfony ExpressionLanguage : qu'est-ce que c'est ? Quand et comment l'utiliser ? Comment créer des expressions lors de cas plus complexes ?
Découvrez comment réaliser du typage générique en PHP : introduction et définition du concept, conseils et explications pas-à-pas de cas pratique.
Découvrez un cas d'usage d'intégration d'un CRM avec une application e-commerce, en asynchrone, avec Hubspot et RabbitMQ