Logo IDCI
Drapeau français Drapeau anglais

Bundle PHP / Symfony : aller plus loin

Introduction

Un bundle est une extension applicative (plugin), il a pour objectif d'ajouter des fonctionnalités techniques qui répondent à un périmètre défini. Il est composé d'un ensemble de fichiers structurés, voir notre précédent article.

Dans cet article, nous allons vous montrer comment rendre paramétrable un bundle et afficher son nom et sa version dans le Profiler Symfony de votre application. Bundle profiler



Pour ce faire, nous devrons tout d'abord modifier la classe bundle afin d'y ajouter une partie configuration. Bien que vide par défaut, cette classe est pratique et permet de personnaliser le comportement du bundle. Il nous faudra ensuite créer un fichier de configuration dans notre application qui sera utilisé pour le bundle. Enfin nous terminerons par l'utilisation des DataCollector de Symfony afin de pouvoir afficher les informations que nous voulons dans le profiler.



Dans un premier temps, notre objectif va être de définir la configuration possible de notre bundle, nous allons donc utiliser la fonction "configure" :

// src/HelloWorldBundle.php
<?php

namespace IDCI\Bundle\HelloWorldBundle;

use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;

class HelloWorldBundle extends AbstractBundle
{
	public function configure(DefinitionConfigurator $definition): void
	{
		$definition->rootNode()
			->children()
				->arrayNode('app')
					->isRequired()
					->children()
						->scalarNode('name')->isRequired()->end()
						->scalarNode('version')->isRequired()->end()
					->end()
				->end()
			->end()
		;
	}
}

Github logo Voir l'exemple

Plusieurs notions sont assez importantes dans le code ci-dessus, nous allons les détailler.

La méthode configure() sert à définir ou importer depuis un ou plusieurs fichiers, la configuration possible du bundle. Cette méthode utilise le TreeBuilder de Symfony, qui permet d'organiser de manière hiérarchique les paramètres attendus par notre bundle.

Dans notre cas, nous indiquons que le bundle requiert plusieurs paramètres :

   - name sous la clé de configuration app

   - version sous la clé de configuration app



Nous pouvons donc ajouter le fichier de configuration du bundle dans notre application :

// YourApp/config/packages/idci_hello_world.yaml
hello_world:
	app:
		name: HelloWorld
		version: 1.0.0

Les valeurs attribuées aux paramètres sont libres, cependant nous utilisons la convention de nommage Semantic Versioning. L'idée générale est de nommer sa version d'après la strucutre X.Y.Z où X représente un changement majeur, Y un changement mineur et Z un correctif. Pour la première version nous choississons donc 1.0.0 .



Nous allons maintenant associer les valeurs définies de la configuration du bundle à des paramètres dans le conteneur d'injection de dépendances :

// src/HelloWorldBundle.php
<?php

namespace IDCI\Bundle\HelloWorldBundle;

use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;

class HelloWorldBundle extends AbstractBundle
{
	public function configure(DefinitionConfigurator $definition): void
	{
		$definition->rootNode()
			->children()
				->arrayNode('app')
					->isRequired()
					->children()
						->scalarNode('name')->isRequired()->end()
						->scalarNode('version')->isRequired()->end()
					->end()
				->end()
			->end()
		;
	}

	public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
	{
		$builder->setParameter('hello_world_bundle.app_name', $config['app']['name']);
		$builder->setParameter('hello_world_bundle.app_version', $config['app']['version']);
	}
}

Github logo Voir l'exemple

   loadExtension() : Méthode appelée au moment de la compilation, permet de charger la définition de services à partir de fichiers de configuration.



Maintenant que nous avons ajouté la configuration, nous voulons nous assurer que les paramètres sont bien pris en compte. Pour ce faire, nous pouvons utiliser la commande suivante qui permet d'afficher les paramètres des services enregistrés :

php bin/console debug:container --parameters



On peux constater que nos 2 paramètres ont bien été pris en compte :

Debug container parameters



Pour la suite, nous allons voir comment utiliser les DataCollector de Symfony afin d'afficher des informations dans le Profiler :

<?php

namespace IDCI\Bundle\HelloWorldBundle\DataCollector;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\DataCollector\DataCollector;

class HelloWorldDataCollector extends DataCollector
{
	public function __construct(
		private string $appName,
		private string $appVersion,
	) {
	}

	public function collect(Request $request, Response $response, \Throwable $exception = null): void
	{
		$this->data['hello_world.app_name'] = $this->appName;
		$this->data['hello_world.app_version'] = $this->appVersion;
	}

	public function reset(): void
	{
		$this->data = [];
	}

	public function getName(): string
	{
		return 'hello_world.data_collector';
	}

	public function getAppName(): string
	{
		return $this->data['hello_world.app_name'];
	}

	public function getAppVersion(): string
	{
		return $this->data['hello_world.app_version'];
	}
}

Github logo Voir l'exemple

   collect(): Collecte les données dans les propriétés locales

   reset(): Cette méthode est appelée entre chaque requêtes pour vider les informations contenues dans le Profiler.

   getName(): Renvoie le nom du Collector (son identifiant). Ce nom doit être unique et il est préférable de le faire convenir aux conventions (court, en minuscule et sans espaces).

Nous ajoutons aussi 2 méthodes qui nous servirons à récupérer les 2 paramètres que nous voulons afficher dans le Profiler.

Il nous faut ensuite ajouter un fichier de template qui nous permettra de structurer l'affichage de ces informations.


	{% extends '@WebProfiler/Profiler/layout.html.twig' %}

	{% block toolbar %}
		{% set icon %}
			<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAdnJLH8AAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB+kJEAoFNPHCUCIAAAXSSURBVHja7ZptaFV1HMe/3985t213jq6GZCBkUZQUWSiolCFihYT04CTR9O7BueseYps4kCmb0nB6Qa6ic2uKGQXTNyk90JvIIOtFUPZGWL1IiCICIXS7ez6/XnSPHI7neB98OnecL1zu2f/swL6f3+/3/f93NyBUqFChQoUKFarIdfz4cbOQ51jsxo8dO/a0iNSTXArgxUQiofk8bxaj6UOHDpki8oqINAJ4VVUjJIfI/OtZVACSyeQckpsBJAA8BYCqCpJQVajqzASwf//+Z0kmLMvaZBhGDMANw/aL5MT27dt1xgDYt29fhOQaEWlW1ZUATGelndeFVj+QALq6uuaSrAKwDcATtlmvqrvXihZAR0cHSS4SkQZV3QCgwhloTnOutr/pflEBaG9vLxGRtaraBGCFqgqAG+19Y692wfCYf1iWVTwA2tra5olILYCtqrrAWU3bpF8HuLc6dzcEFkBDQ4OYprmYZKOqVqpqudccu9va7oZcciCQABKJRBnJN0k2qepyZpzcKs29usEPwu3mwF0DUFNTM19EtlqWVSsi871M5rKleY2C6/tHVfU8gCP3HUBVVZVMT08vE5FGVX1LVcu8Qsures7g86usa+2Kqp4A8GFra+uf93UX2LBhQznJ9RMTEw0iskRV6TerPqc4z3ePDpgCcAHAcQCft7e3T9zuz35bANavX7+AZJ2q1pCc57VV+c1ytkONq9pXSX6iqv27d+++fCe71izAtFiWtYJkE4C1AErc1fVLbD+DPlAUwCWSfQAG9+zZc+1uZFVeAKqrq1cMDw8fAbDI/k3Maz5vFW7ZwhDAKIBzqtpL8oeurq7pu7lL5QUgGo0uKy0tfX5sbAzpdBrT09PZEjqnV+aZ31X1pKqe6u7u/utenU/yHYGIiCAajaK8vByTk5NIp9MYHx/3PMX5haAz1FT1QqbaXx48eHD8Xp9K8wKgqrSrTBIlJSUoKyuDZVlIp9MYGRm56WTm0+5XVfVjkh+kUqnLuI/KOwRt886XaZqIxWKIxWIYGxvD9evXMTU15d7CVFUvqWqfZVmDvb291xAAFfZJasaYG4SIYNasWaioqMDExASGh4cxOjo6YlnWeZJ9lmVdHBgYsBAgmbdj3t0VzuuysjJEo1EA+NcwjF9I/tbZ2Rko80CeH4s3NjZ2kHxfRCAiN6puv9vXzq8dayMkz5HsJ3mxo6MjEDCk0AzwGgX3WLieKye5ieS3IvJTT09Pw4EDB2YX1Qj4GXWb9lvP3CPJRQCOkexOJpNnM13x844dOzTQHXCrintV36ML3PdiJLeR/JHkd6lUasvhw4dnBTYDmpubF4vIQRFZSVI85tw3E7zu+6z/Q/JjkgORSGSovr5eAwMAAFpaWigiS0i2isjbJEuyBaI7GHMEMkXyGxHpJ/lFfX39WCAAOLVz587HRKSJZLWIzPYyc4eu/yB5iuSpurq6K4EBYGvXrl1zRKSWZCPJR/0qnetIuK8B2GvjJL8SkT4R+Toej08GAoCtzs7OUgCVJFtJvsD/gyLbzHuu53D9K8mTJD/avHnz34EAYGvv3r0GyVUk20iuFhHTywhJGIbhrHBW8x730iQ/Jdm7cePG7wMBwKnu7u7nSLaIyDsko/m0vt+4+EAZikQiCysrKzVQAGz19PTMJ9kgIltJzi0kFLPAG4pEIgvXrVsXTAC2ksnkgyS3iEgzySdzNZ7DiBQHAFupVOoBAG9kAnOpiEihIDI7RXEBsHX06FGq6ksk20TkdZKRbKZ9xmDINM3iA+BUb2/vwsxovEuyIs9RKH4Atvr7+x8mWS8iCZKP5LhFDhmGMTMA2Dpx4kQ5yU0i8h7JZ7J0w8wDYOv06dMmgDWZnHiZpLgBACgIgBQDgHg8PhWPxz+zLGsVgOUABkmOO/8WEaij8L3Q4ODg4wCaRaQKQKzQDij6/xU+e/bsQwBqVXW1YRiv5XsUnjE6c+aMiVChQoUKFSpUqFC56z8JEpv6DPlW1AAAAABJRU5ErkJggg==">
		{% endset %}

		{% set text %}
			<div class="sf-toolbar-info-group">
				<div class="sf-toolbar-info-piece">
					<p>Nom : {{ collector.appName }}</p>
					<p>Version : {{ collector.appVersion }}</p>
				</div>
			</div>
		{% endset %}

		{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: true, name: 'hello_world.data_collector', additional_classes: 'sf-toolbar-block-right' }) }}
	{% endblock %}

Nous allons maintenant pouvoir faire le lien entre notre DataCollector et notre template. Pour cela nous allons définir le service dans le fichiers services.yaml du bundle en précisant l'emplacement du template et en passant en arguments les paramètres du conteneur d'injection de dépendances créés précedemment.

// config/services.yaml
services:
	IDCI\Bundle\HelloWorldBundle\DataCollector\HelloWorldDataCollector:
		tags:
		-
			name: 'data_collector'
			template: '@HelloWorld/collector/hello_world.html.twig'
			id: 'hello_world.data_collector'
		arguments:
			$appName: '%hello_world_bundle.app_name%'
			$appVersion: '%hello_world_bundle.app_version%'

Finalement, nous chargerons ce fichier de configuration via la classe bundle.

// src/HelloWorldBundle.php
<?php

namespace IDCI\Bundle\HelloWorldBundle;

use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;

class HelloWorldBundle extends AbstractBundle
{
	public function configure(DefinitionConfigurator $definition): void
	{
		$definition->rootNode()
			->children()
				->arrayNode('app')
					->isRequired()
					->children()
						->scalarNode('name')->isRequired()->end()
						->scalarNode('version')->isRequired()->end()
					->end()
				->end()
			->end()
		;
	}

	public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
	{
		$builder->setParameter('hello_world_bundle.app_name', $config['app']['name']);
		$builder->setParameter('hello_world_bundle.app_version', $config['app']['version']);

		$container->import('../config/services.yaml');
	}
}

Avec ceci vous aurez accès aux informations de votre bundle dans le profiler de Symfony !