Retour

Créer un bundle Symfony autonome

16 déc. 2016
13mn

Dans ce post nous allons voir step-by-step comment créer de zéro un bundle symfony autonome. Ce bundle n'aura aucune responsabilité fonctionnelle, il servira simplement à dérouler les étapes de création d'un bundle autonome.


Les étapes


Bootstrap du projet

Commençons par créer un fichier composer.json pour notre projet.

{ "name": "acme/standalone-bundle", "description": "acme standalone bundle", "type": "bundle", "require": {} }

Afin d'optimiser les performances en production, nous séparons les sources et les tests. Reprenons notre composer.json et ajoutons-y ces règles d'autoload.

{ "name": "acme/standalone-bundle", "description": "acme standalone bundle", "type": "bundle", "require": {}, "autoload": { "psr-4": { "Acme\StandaloneBundle\": "src/" } }, "autoload-dev": { "psr-4": { "Acme\StandaloneBundle\Tests\": "tests/" } } }

Lançons un composer install afin d'initialiser le projet.

composer install

Composer crée un dossier vendor que nous ne souhaitons pas versionner. Pour indiquer à git que ce dossier doit être ignoré, il faut créer le fichier .gitignore à la racine du projet et y ajouter le path vers le dossier à ignorer.

# .gitingore /vendor

Bundle minimal

Pour créer notre bundle minimal, nous devons créer la classe \Acme\StandaloneBundle\StandaloneBundle qui étend \Symfony\Component\HttpKernel\Bundle\Bundle.

<?php // src/StandaloneBundle.php namespace Acme\StandaloneBundle; use Symfony\Component\HttpKernel\Bundle\Bundle; class StandaloneBundle extends bundle { }

La classe parente est disponible dans le composant symfony/http-kernel, ajoutons cette dépendance à notre projet avec composer.

composer require "symfony/http-kernel:3.1.*"

Notre bundle minimal est maintenant prêt, il peut être register dans une application Symfony.

Application embarquée

Afin d'être sûr que notre bundle fonctionne comme attendu au sein d'une application symfony, nous allons embarquer une application minimale dans notre projet.

Dans le kernel de cette application embarquée, nous allons surcharger les méthodes

  • registerBundles: pour y register notre bundle
  • registerContainerConfiguration: pour définir l'emplacement de notre fichier de configuration
<?php // tests/App/AppKernel.php namespace Acme\StandaloneBundle\Tests\App; use Symfony\Component\HttpKernel\Kernel; use Symfony\Component\Config\Loader\LoaderInterface; class AppKernel extends Kernel { public function registerBundles() { return [ new \Symfony\Bundle\FrameworkBundle\FrameworkBundle(), new \Acme\StandaloneBundle\StandaloneBundle(), ]; } public function registerContainerConfiguration(LoaderInterface $loader) { $loader->load(__DIR__.'/config/config.yml'); } }

On en profite pour ajouter en dépendance de dev le composant symfony/framework-bundle

composer require --dev "symfony/framework-bundle:3.1.*"

Le composant FrameworkBundle requiert au minimum la configuration d'un secret, que nous ajoutons au fichier tests/App/config/config.yml

# tests/App/config/config.yml # FrameworkBundle Configuration framework: secret: This is a secret, change me

Pour que ce fichier de configuration en yaml puisse être chargé par le kernel, nous devons ajouter en dépendance de dev le composant symfony/yaml.

composer require --dev "symfony/yaml"

Au boot, le kernel crée un dossier de cache et un dossier de logs que l'on ne souhaite pas versionner. On ajoute alors ces dossiers au .gitignore

# .gitingore /vendor /tests/App/cache /tests/App/logs

Utilisation autonome

Application console

Pour pouvoir créer dans notre bundle autonome des commandes symfony, nous devons dans un premier temps ajouter en dépendance de dev le composant symfony/console puis ajouter à notre application minimale le script console.

composer require --dev "symfony/console:3.1.*"
<?php // tests/App/console namespace Acme\StandaloneBundle\Tests\App; require_once __DIR__ . '/../../vendor/autoload.php'; use Symfony\Bundle\FrameworkBundle\Console\Application; $kernel = new AppKernel('dev', true); $application = new Application($kernel); $application->run();

Nous pouvons maintenant lancer des commandes en utilisant la console.

php tests/App/console

Application web

Maintenant que nous pouvant lancer des commandes, intéressons-nous au web. Afin de pouvoir appeler un contrôleur, nous devons ajouter à notre application minimale un frontal web.

<?php // tests/App/app_dev.php namespace Acme\StandaloneBundle\Tests\App; require_once __DIR__ . '/../../vendor/autoload.php'; use Symfony\Component\HttpFoundation\Request; $kernel = new AppKernel('dev', true); $request = Request::createFromGlobals(); $response = $kernel->handle($request); $response->send(); $kernel->terminate($request, $response);

Ajoutons un contrôleur et la configuration du routing afin de pouvoir tester notre frontal web.

<?php // src/Controller/FooController.php namespace Acme\StandaloneBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\JsonResponse; class FooController extends Controller { public function barAction() { return new JsonResponse(['foo' => 'bar']); } }
# src/Resources/config/routing.yml standalone_foobar: path: / defaults: _controller: StandaloneBundle:Foo:bar
# tests/App/config/routing.yml _main: resource: ../../../src/Resources/config/routing.yml
# tests/App/config/config.yml # FrameworkBundle Configuration framework: secret: This is a secret, change me router: resource: '%kernel.root_dir%/config/routing.yml' strict_requirements: ~

Maintenant que nous avons un frontal web disponible dans notre projet, voyons comment lancer un serveur web très simple qui dirige directement vers celui-ci.

Pour commencer ajoutons le composant symfony/process.

composer require --dev "symfony/process:3.1.*"

L'ajout de ce composant nous permet d'utiliser le serveur web interne de php.

php tests/App/console server:run -d tests/App

Ouvrez votre navigateur vers , le json {"foo": "bar"} doit s'afficher. Vous avez maintenant un bundle capable de recevoir des requêtes HTTP et d'y répondre sans avoir besoin de configurer un serveur web pour le développement.

Tests

Tests unitaires

Pour exécuter les tests unitaires sur notre projet, nous allons utiliser la librairie phpunit.

Ajoutons cette dépendance de dev

composer require --dev "phpunit/phpunit"

Ajoutons le fichier de configuration phpunit dans notre projet.

<?xml version="1.0" encoding="UTF-8"?> <!-- phpunit.xml --> <phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.3/phpunit.xsd" backupGlobals="false" colors="true" bootstrap="./vendor/autoload.php" > <php> <server name="KERNEL_DIR" value="./tests/App/" /> </php> <testsuites> <testsuite name="StandaloneBundle Suite"> <directory suffix="Test.php">./tests/</directory> </testsuite> </testsuites> <filter> <whitelist processUncoveredFilesFromWhitelist="true"> <directory suffix=".php">./src/</directory> </whitelist> </filter> <logging> <log type="coverage-clover" target="build/logs/clover.xml"/> <log type="coverage-html" target="build/coverage" /> </logging> </phpunit>

Ajoutons un test unitaire pour tester notre configuration.

<?php // tests/Unit/Controller/FooControllerTest.php namespace Acme\StandaloneBundle\Tests\Unit\Controller; use Acme\StandaloneBundle\Controller\FooController; use Symfony\Component\HttpFoundation\JsonResponse; class FooControllerTest extends \PHPUnit_Framework_TestCase { private $controller = null; protected function setUp() { $this->controller = new FooController(); } protected function tearDown() { $this->controller = null; } public function testBarAction() { $this->assertInstanceOf(JsonResponse::class, $this->controller->barAction()); } }

On lance ensuite la suite de test, qui nous affiche qu'un test et une assertion ont été exécutés sans problèmes.

./vendor/bin/phpunit

Tests fonctionnels

Pour lancer les tests fonctionnels, nous allons encore une fois utiliser la librairie phpunit.

Commençons par ajouter le composant symfony/brower-kit, puis nous étendrons le WebTestCase symfony pour override la méthode getKernelClass afin renvoyer le bon FQCN de notre Kernel embarqué, puis nous rajouterons de la configuration au framework bundle.

composer require "symfony/browser-kit:3.1.*"
<?php // tests/WebTestCase.php namespace Acme\StandaloneBundle\Tests; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as BaseWebTestCase; abstract class WebTestCase extends BaseWebTestCase { protected static function getKernelClass() { return 'Acme\StandaloneBundle\Tests\App\AppKernel'; } }
# tests/App/config/config.yml # FrameworkBundle Configuration framework: secret: This is a secret, change me router: resource: '%kernel.root_dir%/config/routing.yml' strict_requirements: ~ test: ~

Nous allons maintenant utiliser ce WebTestCase pour créer un client et parcourir les pages web de notre projet.

<?php // tests/Functional/Controller/FooControllerTest.php namespace Acme\StandaloneBundle\Tests\Functional\Controller; use Acme\StandaloneBundle\Tests\WebTestCase; class FooControllerTest extends WebTestCase { private $client = null; private $container = null; public function setUp() { $this->client = static::createClient(); $this->container = $this->client->getContainer(); } public function tearDown() { $this->client = null; $this->container = null; } public function testBarAction() { $this->client->request('GET', '/'); $this->assertTrue($this->client->getResponse()->isSuccessful()); } }

Nous pouvons maintenant relancer les tests

./vendor/bin/phpunit

Nous avons maintenant deux tests et deux assertions en succès.

Conclusion

En quelques étapes nous avons un bundle symfony autonome. Un bundle qui est capable de lancer ses propres tests unitaires et fonctionnels mais également de faire tourner son propre serveur web, très utile pour le développement.

J'espère que ce post vous aura donné l'envie de développer des bundles autonomes. N'hésitez pas à poser des questions dans les commentaires, et à me dire si vous voulez que je continue sur le sujet en expliquant comment rajouter des composants (twig, web-profiler, ...), comment faire de votre bundle autonome un projet open-source distribué via packagist aux quatre coins du monde.


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 202310mnMarianne Joseph-Géhannin

Dédoublonnez vos photos

Comme moi vous avez sur vos disques dur des copies, de copie, de copie de photos et vous souhaiteriez faire un peu de ménage ? C'est par ici.

25 janv. 202321mnJacques Moati

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