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 ?
Progression
A présent implémentons la classe Acme\VersionChanges\ChangesFactory
.
Créez donc le fichier suivant :
<?php
namespace Acme\VersionChanges;
use Symfony\Component\HttpFoundation\RequestStack;
class ChangesFactory
{
/**
* @var array
*/
private $versions;
/**
* @var RequestStack
*/
private $request;
/**
* @param array $versions
* @param RequestStack $requestStack
*/
public function __construct(array $versions, RequestStack $requestStack)
{
$this->versions = $versions;
$this->requestStack = $requestStack;
$this->prepare();
}
/**
* @param string $version
*
* @return bool
*/
public function has($version)
{
return isset($this->versions[$version]);
}
/**
* @param string $version
*
* @return AbstractVersionChanges|null
*/
public function get($version)
{
if (!$this->has($version)) {
return;
}
return $this->versions[$version];
}
}
Cette classe prend donc en entrée le tableau de versions déclaré en tant que parameters
Symfony ainsi que le RequestStack
que nous irons injecter dans nos fichiers d'application de changements de versions.
Notez que, par la suite, vous pourrez avoir besoin d'injecter Doctrine afin de récupérer des données en base de données et pas simplement de les remodeler.
Nous avons également écrit deux méthodes, has($version)
et get($version)
, assez simples pour retourner une version.
Cependant, les yeux les plus aguerris auront remarqué la présence dans le constructeur de l'appel à la méthode prepare()
qui va nous permettre d'instancier les namespaces fournis dans la configuration en classes PHP utilisables.
La méthode à ajouter est la suivante :
/**
* Prepares class instances from class name.
*
* @throws \RuntimeException When version changes class does not exist or does not implement VersionChangesInterface.
*/
protected function prepare()
{
foreach ($this->versions as $version => $class) {
if (!class_exists($class)) {
throw new \RuntimeException(sprintf('Unable to find class "%s".', $class));
}
if (!$class instanceof VersionChangesInterface) {
throw new \RuntimeException(sprintf('Class "%s" does not implement VersionChangesInterface.', $class));
}
$instance = new $class($this->requestStack);
$this->versions[$version] = $instance;
}
}
Enfin, le listener implémenté dans l'étape précédente avait besoin d'une méthode getHistory($version)
qui avait pour objectif de nous retourner les fichiers de changements de version (instanciés) à jouer en fonction de la version courante.
Nous ajoutons donc la méthode :
/**
* Returns compatibility changes history for a given version.
*
* @param string $version
*
* @return array|null
*/
public function getHistory($version)
{
if (!$this->has($version)) {
return;
}
$index = array_search($version, array_keys($this->versions));
return array_slice($this->versions, 0, $index + 1);
}
Ainsi, dans le cas ou une version 1.0.0
est demandée, seuls les fichiers de changements 1.0.1
et 1.0.0
seront joués. Les versions précédentes tel que 0.0.9
seront ignorées.
Pour vous aider à mieux comprendre la façon dont cet historique de version est récupéré, voici comment serait testé unitairement (avec PHPUnit) cette méthode :
<?php
namespace Tests\Acme\VersionChanges;
use Acme\VersionChanges\ChangesFactory;
use Symfony\Component\HttpFoundation\RequestStack;
class ChangesFactoryTest extends \PHPUnit_Framework_TestCase
{
/**
*
* @var array
*/
protected $versions;
/**
*
* @var RequestStack
*/
protected $requestStack;
/**
*
* @var ChangesFactory
*/
protected $changesFactory;
/**
* {@inheritdoc}
*/
protected function setUp()
{
$this->request = $this->getMockBuilder('Symfony\Component\HttpFoundation\RequestStack')
->disableOriginalConstructor()
->getMock();
$this->versions = [
'1.1.0' => 'Acme\VersionChanges\VersionChange110',
'1.0.0' => 'Acme\VersionChanges\VersionChange100',
'0.9.0' => 'Acme\VersionChanges\VersionChange090',
'0.8.0' => 'Acme\VersionChanges\VersionChange080',
];
$this->changesFactory = new ChangesFactory($this->versions, $this->requestStack);
}
/**
* {@inheritdoc}
*/
protected function tearDown()
{
$this->request = null;
$this->versions = null;
$this->changesFactory = null;
}
/**
* Test getHistory() when version 1.1.0
*/
public function testGetHistoryWithVersion110()
{
$history = $this->versionChanges->getHistory('1.1.0');
$this->assertCount(1, $history);
$this->assertInstanceOf('Acme\VersionChanges\VersionChange110', $history[0]);
}
/**
* Test getHistory() when version 1.0.0
*/
public function testGetHistoryWithVersion100()
{
$history = $this->versionChanges->getHistory('1.0.0');
$this->assertCount(2, $history);
$this->assertInstanceOf('Acme\VersionChanges\VersionChange110', $history[0]);
$this->assertInstanceOf('Acme\VersionChanges\VersionChange100', $history[1]);
}
}
Pour rappel, n'oubliez pas de vous assurer du comportement de vos méthodes en écrivant des tests unitaires.
Afin que ce service soit injecté par l'injection de dépendance de Symfony, nous devons également déclarer le service :
acme.version.changes_factory:
class: Acme\VersionChanges\ChangesFactory
arguments: ["%versions%", "@request_stack"]
Notre structure est prête, il ne nous reste plus qu'à implémenter les fichiers de changements, dans l'étape suivante.
Auteur(s)
Vincent Composieux
Architecte passionné par les technologies web depuis de longues années, je pratique principalement du PHP (Symfony) / Javascript mais aussi du Python ou Golang.
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