Explication : Bundle, Service et Configuration
Bonjour,
comme je viens de comprendre comment on faisait pour ajouter des éléments de configuration à son bundle, je tenais à expliquer rapidement le fonctionnement.
Les pré-requis sont de connaitre la notion de Bundle, et la notion de Service proposées par Symfony2.
Pour créer son bundle, il est possible de passer par la console via :
Code:
php console generate:bundle --namespace=Package/myBundle --format=yml
Ensuite on crée une classe de service. Si on veut qu'elle accède au ServiceContainer on peut faire ceci :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
class myService
{
protected $_container;
public function setContainer(Symfony\Component\DependencyInjection\Container $container)
{
$this->_container = $container;
}
public function whatever()
{
// par exemple : accès au service request
$this->_container->get('request');
}
} |
Mais pour que ça marche, il faut injecter automatiquement le container. Pour cela on passe par l'injection de dépendance et le fichier Resources/config/services.xml
Code:
1 2 3 4 5 6 7 8 9 10 11 12
|
<parameters>
<parameter key="package_my.myservice.class">Package\myBundle\myService</parameter>
</parameters>
<services>
<service id="package_my.myservice" class="%package_my.myservice.class%">
<call method="setContainer">
<argument type="service" id="service_container" />
</call>
</service>
</services> |
De fait Symfony2 se charge d'instancier la classe myService et de lui injecter le serviceContainer via la méthode setContainer. On aurait aussi pu choisir de passer le serviceContainer en paramètre du constructeur de la classe de service. Pour cela il faut l'implémenter dans la classe, et supprimer les noeuds call du fichier services.xml afin de ne laisser que le noeud argument sous le noeud service.
Votre service est désormais accessible depuis n'importe quel action de controller ou autre service disposant du serviceContainer.
Code:
1 2 3 4 5 6 7 8
|
//via une action
...
$this->get('package_my.myservice');
//via une classe
...
$this->myContainer->get('package_my.myservice'); |
Maintenant vous souhaitez que votre classe myService dispose d'une (ou plusieurs) propriété.
Code:
1 2 3 4 5 6 7 8 9 10 11
|
class myService
{
// nb de resultat a renvoyer
protected $_nbResult;
public function __construct($nbResult)
{
$this->_nbResult = $nbResult;
}
} |
Et bien là encore l'injection de dépendance va nous être utile :
Code:
1 2 3 4 5 6 7 8 9 10
|
...
<services>
<service id="package_my.myservice" class="%package_my.myservice.class%">
<argument>15</argument>
<call method="setContainer">
<argument type="service" id="service_container" />
</call>
</service>
</services> |
L'argument ayant pour valeur 15 va donc être passé comme paramètre au constructeur de ma classe.
Oui mais voilà, on souhaite aussi que ce paramètre soit modifiable. Pour cela on va extraire la valeur du noeud argument et
utiliser le système de configuration. Il faut alors créer un fichier config.yml dans le répertoire suivant de votre bundle :
Resources\config\config.yml
Code:
1 2 3
|
package_my:
nbresult: 22 |
Puis modifier le fichier de services :
Code:
1 2 3 4 5 6 7 8 9 10
|
...
<services>
<service id="package_my.myservice" class="%package_my.myservice.class%">
<argument>%package_my.nbresult%</argument>
<call method="setContainer">
<argument type="service" id="service_container" />
</call>
</service>
</services> |
Dans ce cas, on demande au framework de charger la configuration du bundle et on indique également que la class myService depend d'un paramètre de configuration
%package_my.nbresult%
C'est là que ça se gâte un peu. Tout dépend ce que vous avez dans votre répertoire DependancyInjection de votre bundle.
Si vous avez une classe de Configuration et une classe d'Extension vous êtes dans le cas le plus compliqué, mais également le plus puissant.
Le fichier de Configuration permet de controller le contenu du fichier de configuration. Vous allez ainsi définir son contenu via le tree builder :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
// classe de configuration
...
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('package_my');
$rootNode
->children()
->scalarNode('nbresult')->defaultValue(10)->end()
->end()
;
return $treeBuilder;
} |
On pourrait également vérifier le type de donnée (ici un entier), mais dans notre exemple j'ai juste mis une valeur par défaut au cas où le fichier de configuration ne serait pas remplit.
(Regardez deplus prêt le TreeBuilder pour plus d'information sur son fonctionnement).
Et maintenant il faut modifier la classe d'Extension afin de charger le fichier de configuration, mais également le fichier de services, et de définir les paramètres nécessaires.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
// classe de configuration
...
public function load(array $configs, ContainerBuilder $container)
{
// charge le contenu du fichier de configuration et gère sa conformité via le TreeBuilder
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
// charge le fichier services.yml
$loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.xml');
// injecte les paramètres
$container->setParameter('package_my.nbresult', $config['nbresult']);
} |
Et voilà, pas besoin de faire d'import depuis le fichier de configuration principal app/config/config.yml
Ca devrait fonctionner.