table des matières
Une application moderne PHP est plein d'objets. Un objet peut faciliter l'acheminement des e-mails alors qu'un autre peut vous permettre de conserver les informations dans une base de données. Dans votre application, vous pouvez créer un objet qui gère votre inventaire de produits, ou tout autre objet qui traite des données à partir d'une API tiers. Le fait est que l'application moderne fait beaucoup de choses et est organisé en de nombreux objets qui gèrent chaque tâche.
Dans ce chapitre, nous allons parler d'un objet PHP particulier dans Symfony2 qui vous aide à instancier, organiser et récupérer les nombreux objets de votre application. Cet objet, appelé un conteneur de services, vous permettra de normaliser et de centraliser la manière dont les objets sont construits dans votre application. Le conteneur qui rend votre vie plus facile, est super rapide, et insiste sur une architecture qui favorise le code réutilisable et découplées. Et puisque toutes les classes de base Symfony2 utilise le conteneur, vous allez apprendre à étendre, configurer et utiliser n'importe quel objet dans Symfony2. En grande partie, le conteneur de services est le plus gros contributeur à la vitesse et l'extensibilité de Symfony2.
Enfin, la configuration et l'utilisation du conteneur de services est facile. À la fin de ce chapitre, vous serez à l'aise pour créer vos propres objets par le biais du conteneur et des objets de personnalisation à partir de n'importe quel ensemble d'une tierce partie. Vous allez commencer à écrire du code qui est plus réutilisable, testables et découplées, simplement parce que le conteneur de services rend l'écriture d'un bon code plus facile.
Plus simplement, un service est un objet PHP qui effectue une sorte de tâche «globale». C'est un nom générique utilisé à dessein-en informatique pour décrire un objet qui est créé dans un but précis (par exemple, la prestation e-mails). Chaque service est utilisé tout au long de votre application lorsque vous avez besoin des fonctionnalités spécifiques qu'elle fournit. Vous n'avez pas à faire quelque chose de spécial pour rendre un service: il suffit d'écrire une classe PHP avec un code qui accomplit une tâche spécifique. Félicitations, vous venez de créer un service!
En règle générale, un objet PHP est un service si elle est utilisée dans le monde dans votre application. Un service unique Mailer est utilisé globalement pour envoyer des messages électroniques tandis que les objets des messages qu'il délivre de nombreux ne sont pas des services. De même, un objet produit n'est pas un service, mais un objet qui persiste objets produit une base de données est un service.
Alors, quel est le gros problème alors? L'avantage de penser "Services", c'est que vous commencez à penser à la séparation de chaque morceau de fonctionnalité dans votre application dans une série de services. Étant donné que chaque service fait seulement un emploi, vous pouvez facilement accéder à chaque service et utiliser ses fonctionnalités partout où vous en avez besoin. Chaque service peut également être plus facilement testé et configuré car il est séparé de l'autre fonctionnalité dans votre application. Cette idée est appelée architecture orientée services et n'est pas unique à Symfony2 ou même PHP. Structurer votre application autour d'un ensemble de classes de services indépendants est une bien connue et de confiance orientée objet des meilleures pratiques. Ces compétences sont essentielles pour être un bon développeur dans presque n'importe quelle langue.
Un Container Service (ou conteneur d'injection de dépendance) est simplement un objet PHP qui gère l'exécution des services (c.-à-objets). Par exemple, supposons que nous ayons une classe PHP simple qui donne des messages électroniques. Sans un conteneur de services, nous devons créer manuellement l'objet chaque fois que nous en avons besoin:
use Acme\HelloBundle\Mailer; $mailer = new Mailer('sendmail'); $mailer->send('ryan@foobar.net', ... );
C'est assez facile. La classe imaginaire Mailer nous permet de configurer la méthode utilisée pour diffuser les messages e-mail (par exemple sendmail, smtp, etc). Mais que faire si nous voulions utiliser le service mailer ailleurs? Nous ne voulons certainement pas à répéter la configuration mailer à chaque fois que nous avons besoin d'utiliser l'objet Mailer. Que faire si nous avions besoin de changer le transport de sendmail smtp partout dans l'application? Nous aurions besoin de traquer tous les lieux, nous créons un service Mailer et le changer.
Une meilleure réponse consiste à laisser le conteneur de services créer l'objet Mailer pour vous. Pour que cela fonctionne, nous devons enseigner au conteneur comment créer le service Mailer. Cela se fait via la configuration, qui peut être spécifié en YAML, XML ou PHP:
# app/config/config.yml services: my_mailer: class: Acme\HelloBundle\Mailer arguments: [sendmail]
<!-- app/config/config.xml --> <services> <service id="my_mailer" class="Acme\HelloBundle\Mailer"> <argument>sendmail</argument> </service> </services>
// app/config/config.php use Symfony\Component\DependencyInjection\Definition; $container->setDefinition('my_mailer', new Definition( 'Acme\HelloBundle\Mailer', array('sendmail') ));
Lorsque Symfony2 initialise, il construit le conteneur de services en utilisant la configuration de l'application (app/config/config.yml par défaut). Le fichier exact qui est chargé est dicté par la méthode AppKernel::registerContainerConfiguration(), qui permet de charger un fichier de configuration spécifique à l'environnement (par exemple config_dev.yml pour l'environnement de dev ou config_prod.yml pour prod).
Une instance de l'objet Acme\HelloBundle\Mailer est maintenant disponible via le conteneur de services. Le conteneur est disponible dans toute traditionnelle Symfony2 contrôleur où vous pouvez accéder aux services du conteneur via la méthode raccourcie get().
class HelloController extends Controller { // ... public function sendEmailAction() { // ... $mailer = $this->get('my_mailer'); $mailer->send('ryan@foobar.net', ... ); } }
Lorsque nous demandons pour le service my_mailer du conteneur, le conteneur construit l'objet et le renvoie. C'est un autre avantage majeur d'utiliser le conteneur de services. A savoir, un service n'est jamais construit jusqu'à ce qu'il en a besoin. Si vous définissez un service et ne jamais l'utiliser sur une demande, le service n'est jamais créé. Cela permet d'économiser la mémoire et augmente la vitesse de votre demande. Cela signifie aussi qu'il y a des performances très peu ou pas touché à la définition de nombreux services. Les services qui ne sont jamais utilisés ne sont jamais construit.
Comme un bonus supplémentaire, le service Mailer est créé uniquement une fois la même instance est retournée chaque fois que vous demandez le service. C'est presque toujours le comportement que vous aurez besoin (c'est plus flexible et puissant), mais nous apprendrons plus tard comment vous pouvez configurer un service qui dispose de plusieurs instances.
La création de nouveaux services (c.-à-objets) via le conteneur est assez simple. Paramètres de service permet une définition des services plus organisée et plus souple:
# app/config/config.yml parameters: my_mailer.class: Acme\HelloBundle\Mailer my_mailer.transport: sendmail services: my_mailer: class: %my_mailer.class% arguments: [%my_mailer.transport%]
<!-- app/config/config.xml --> <parameters> <parameter key="my_mailer.class">Acme\HelloBundle\Mailer</parameter> <parameter key="my_mailer.transport">sendmail</parameter> </parameters> <services> <service id="my_mailer" class="%my_mailer.class%"> <argument>%my_mailer.transport%</argument> </service> </services>
// app/config/config.php use Symfony\Component\DependencyInjection\Definition; $container->setParameter('my_mailer.class', 'Acme\HelloBundle\Mailer'); $container->setParameter('my_mailer.transport', 'sendmail'); $container->setDefinition('my_mailer', new Definition( '%my_mailer.class%', array('%my_mailer.transport%') ));
Le résultat final est exactement le même que précédemment - la différence est seulement dans la façon dont nous avons défini le service. En entourant la my_mailer.class et cordes my_mailer.transport en pour cent (%) des signes, le conteneur sait rechercher les paramètres de ces noms. Lorsque le conteneur est construit, il lève les yeux la valeur de chaque paramètre et l'utilise dans la définition du service.
Le but des paramètres est de nourrir l'information dans les services. Bien sûr, il n'y avait rien de mal à définir le service sans utiliser de paramètres. Paramètres, cependant, présentent plusieurs avantages:
Le choix d'utiliser ou non en utilisant des paramètres est à vous. De haute qualité des tiers parties des bundles sauront toujours utiliser des paramètres comme ils font pour les services stockés dans le conteneur plus configurable. Pour les services offerts dans votre application, cependant, vous ne pouvez pas avoir besoin de la flexibilité des paramètres.
Les paramètres n'ont pas besoin d'être des chaînes plates, elles peuvent aussi être des tableaux. Pour le format XML, vous devez utiliser le type = "collection" attribut pour tous les paramètres qui sont des tableaux.
# app/config/config.yml parameters: my_mailer.gateways: - mail1 - mail2 - mail3 my_multilang.language_fallback: en: - en - fr fr: - fr - en
<!-- app/config/config.xml --> <parameters> <parameter key="my_mailer.gateways" type="collection"> <parameter>mail1</parameter> <parameter>mail2</parameter> <parameter>mail3</parameter> </parameter> <parameter key="my_multilang.language_fallback" type="collection"> <parameter key="en" type="collection"> <parameter>en</parameter> <parameter>fr</parameter> </parameter> <parameter key="fr" type="collection"> <parameter>fr</parameter> <parameter>en</parameter> </parameter> </parameter> </parameters>
// app/config/config.php use Symfony\Component\DependencyInjection\Definition; $container->setParameter('my_mailer.gateways', array('mail1', 'mail2', 'mail3')); $container->setParameter('my_multilang.language_fallback', array('en' = > array('en', 'fr'), 'fr' = > array('fr', 'en'), ));
Dans cette section, nous allons faire référence aux fichiers de configuration du service en tant que ressources. C'est pour mettre en évidence le fait que, alors que la plupart des ressources de configuration seront des fichiers (par exemple YAML, XML, PHP), Symfony2 est si flexible que la configuration pourrait être chargé à partir de n'importe où (par exemple une base de données ou même via un service Web externe).
Le conteneur de services est construit en utilisant une ressource de configuration unique (app/config/config.yml par défaut). Toute la configuration d'autres services (y compris le noyau et la configuration Symfony2 faisceau tiers) doivent être importés à l'intérieur de ce fichier d'une manière ou d'une autre. Cela vous donne une flexibilité absolue sur les services dans votre application.
Les configurations du service externe peuvent être importés de deux manières différentes. Tout d'abord, nous allons parler de la méthode que vous utiliserez le plus souvent dans votre application: la directive importation. Dans la section suivante, nous allons présenter la deuxième méthode, qui est la méthode flexible et pratique pour l'importation de la configuration du service de tiers parties des bundles.
Jusqu'à présent, nous avons placé notre définition my_mailer conteneur de services directement dans le fichier de configuration d'application (p. ex app /config/config.yml). Bien sûr, puisque la classe Mailer lui-même vit à l'intérieur de la AcmeHelloBundle, il est plus logique de mettre la définition du conteneur my_mailer l'intérieur du faisceau ainsi.
Tout d'abord, déplacer la définition contenant my_mailer dans un fichier de ressources nouveau conteneur à l'intérieur AcmeHelloBundle. Si les ressources ou des ressources ou des répertoires de configuration n'existent pas, créez-les.
# src/Acme/HelloBundle/Resources/config/services.yml parameters: my_mailer.class: Acme\HelloBundle\Mailer my_mailer.transport: sendmail services: my_mailer: class: %my_mailer.class% arguments: [%my_mailer.transport%]
<!-- src/Acme/HelloBundle/Resources/config/services.xml --> <parameters> <parameter key="my_mailer.class">Acme\HelloBundle\Mailer</parameter> <parameter key="my_mailer.transport">sendmail</parameter> </parameters> <services> <service id="my_mailer" class="%my_mailer.class%"> <argument>%my_mailer.transport%</argument> </service> </services>
// src/Acme/HelloBundle/Resources/config/services.php use Symfony\Component\DependencyInjection\Definition; $container->setParameter('my_mailer.class', 'Acme\HelloBundle\Mailer'); $container->setParameter('my_mailer.transport', 'sendmail'); $container->setDefinition('my_mailer', new Definition( '%my_mailer.class%', array('%my_mailer.transport%') ));
La définition elle-même n'a pas changée, seulement son emplacement. Bien sûr, le conteneur de services ne connait pas le nouveau fichier de ressources. Heureusement, nous pouvons facilement importer le fichier de ressources en utilisant la clé des importations dans la configuration de l'application.
# app/config/config.yml imports: - { resource: @AcmeHelloBundle/Resources/config/services.yml }
<!-- app/config/config.xml -->
<imports>
<import resource="@AcmeHelloBundle/Resources/config/services.xml"/>
</imports>
// app/config/config.php $this->import('@AcmeHelloBundle/Resources/config/services.php');
La directive importations permet à votre application afin d'inclure les ressources du service de conteneurs de configuration de n'importe quel autre endroit (le plus souvent à partir de bundle). L'emplacement des ressources, pour les fichiers, est le chemin absolu vers le fichier de ressources. La syntaxe spéciale @AcmeHello résout le chemin du répertoire de l'ensemble AcmeHelloBundle. Cela vous permet de spécifier le chemin d'accès à la ressource sans se soucier plus tard, si vous déplacez le AcmeHelloBundle vers un répertoire différent.
Lors de l'élaboration de Symfony2, vous avez le plus souvent utiliser la directive Imports pour importer la configuration du conteneur à partir des bundle que vous avez créés spécifiquement pour votre application. Troisième partie configuration du conteneur paquet, y compris Symfony2 services de base, sont habituellement chargés en utilisant une autre méthode qui est plus flexible et facile à configurer dans votre application.
Voici comment cela fonctionne. En interne, chaque faisceau définit ses services beaucoup comme nous l'avons vu jusqu'ici. A savoir, un paquet utilise un ou plusieurs fichiers de ressources de configuration (généralement XML) pour spécifier les paramètres et les services pour ce paquet. Cependant, au lieu d'importer chacune de ces ressources directement à partir de la configuration de votre application à l'aide de la directive les importations, vous pouvez tout simplement invoquer une extension de conteneur de service à l'intérieur du paquet qui fait le travail pour vous. Une extension de conteneur de service est une classe PHP créée par l'auteur faisceau d'accomplir deux choses:
En d'autres termes, une extension de conteneur de service configure les services pour un ensemble en votre nom. Et comme nous le verrons dans un instant, l'extension fournit un sensible, de haut niveau interface pour la configuration du faisceau.
Prenez le FrameworkBundle - le faisceau cadre de base Symfony2 - à titre d'exemple. La présence du code suivant dans votre configuration de l'application invoque l'extension du service à l'intérieur du conteneur FrameworkBundle:
# app/config/config.yml framework: secret: xxxxxxxxxx charset: UTF-8 form: true csrf_protection: true router: { resource: "%kernel.root_dir%/config/routing.yml" }
<!-- app/config/config.xml --> <framework:config charset="UTF-8" secret="xxxxxxxxxx"> <framework:form /> <framework:csrf-protection /> <framework:router resource="%kernel.root_dir%/config/routing.xml" /> <!-- ... --> </framework>
// app/config/config.php $container->loadFromExtension('framework', array( 'secret' => 'xxxxxxxxxx', 'charset' => 'UTF-8', 'form' => array(), 'csrf-protection' => array(), 'router' => array('resource' => '%kernel.root_dir%/config/routing.php'), // ... ));
Lorsque la configuration est analysée, le conteneur cherche une extension qui peut gérer la directive de configuration-cadre. L'extension en question, qui vit dans le FrameworkBundle, est invoquée et la configuration du service pour la FrameworkBundle est chargé. Si vous retirez la clé de votre cadre de fichier de configuration d'application tout à fait, les services de base Symfony2 ne sera pas chargé. Le point est que vous êtes en contrôle: le cadre Symfony2 ne contient aucune magie ou effectue toutes les actions que vous n'avez pas de contrôle dessus.
Bien sûr, vous pouvez faire beaucoup plus que simplement «activer» l'extension conteneur de services de l'FrameworkBundle. Chaque extension vous permet de personnaliser facilement le faisceau, sans se soucier de la façon dont les services internes sont définies.
Dans ce cas, l'extension vous permet de personnaliser le jeu de caractères, error_handler, csrf_protection, la configuration du routeur et bien plus encore. En interne, le FrameworkBundle utilise les options spécifiées ici pour définir et configurer les services qui lui sont propres. Le faisceau prend soin de la création de tous les paramètres et les services nécessaires pour le conteneur de services, tout en permettant une grande partie de la configuration pour être facilement personnalisé. Comme un bonus supplémentaire, la plupart des extensions de conteneurs de service sont aussi assez intelligent pour effectuer la validation - pour vous informer des options qui sont manquants ou le mauvais type de données.
Lors de l'installation ou la configuration d'un paquet, consultez la documentation du paquet pour savoir comment les services pour le faisceau doit être installé et configuré. Les options disponibles pour les bundle de base peut être trouvée à l'intérieur du Guide de référence .
Nativement, le conteneur de services ne reconnaît que les paramètres, les services, et les directives des importations. Les autres directives sont traitées par une extension de service de conteneurs.
Si vous souhaitez exposer configuration convivial dans vos propres paquets, lisez le Comment faire pour exposer une configuration sémantique d'un Bundle
Jusqu'à présent, notre service d'origine my_mailer est simple: il suffit d'un seul argument dans son constructeur, qui est facilement configurable. Comme vous le verrez, le pouvoir réel du conteneur est réalisé lorsque vous avez besoin pour créer un service qui dépend d'un ou plusieurs autres services dans le conteneur.
Commençons par un exemple. Supposons que nous ayons un nouveau service, NewsletterManager, qui permet de gérer la préparation et la livraison d'un message e-mail à une collection d'adresses. Bien sûr, le service my_mailer est déjà très bon à délivrer des messages par courrier électronique, nous allons donc utiliser à l'intérieur NewsletterManager pour gérer la livraison effective des messages. Cette classe semblant pourrait ressembler à ceci:
namespace Acme\HelloBundle\Newsletter; use Acme\HelloBundle\Mailer; class NewsletterManager { protected $mailer; public function __construct(Mailer $mailer) { $this->mailer = $mailer; } // ... }
Sans l'aide du conteneur de service, nous pouvons créer un nouveau NewsletterManager assez facilement de l'intérieur d'un contrôleur:
public function sendNewsletterAction() {
$mailer = $this->get('my_mailer'); $newsletter = new Acme\HelloBundle\Newsletter\NewsletterManager($mailer); // ... }
Cette approche est très bien, mais que faire si nous décidons plus tard que la classe a besoin d'un NewsletterManager second argument du constructeur ou du troisième? Que faire si nous décidons de remanier notre code et renommer la classe? Dans les deux cas, vous aurez besoin de trouver tous les endroits où l'NewsletterManager est instancié et le modifier. Bien sûr, le conteneur de services nous donne une option beaucoup plus attrayante:
# src/Acme/HelloBundle/Resources/config/services.yml parameters: # ... newsletter_manager.class: Acme\HelloBundle\Newsletter\NewsletterManager services: my_mailer: # ... newsletter_manager: class: %newsletter_manager.class% arguments: [@my_mailer]
<!-- src/Acme/HelloBundle/Resources/config/services.xml --> <parameters> <!-- ... --> <parameter key="newsletter_manager.class">Acme\HelloBundle\Newsletter\NewsletterManager</parameter> </parameters> <services> <service id="my_mailer" ... > <!-- ... --> </service> <service id="newsletter_manager" class="%newsletter_manager.class%"> <argument type="service" id="my_mailer"/> </service> </services>
// src/Acme/HelloBundle/Resources/config/services.php use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; // ... $container->setParameter('newsletter_manager.class', 'Acme\HelloBundle\Newsletter\NewsletterManager'); $container->setDefinition('my_mailer', ... ); $container->setDefinition('newsletter_manager', new Definition( '%newsletter_manager.class%', array(new Reference('my_mailer')) ));
En YAML, la syntaxe spéciale @my_mailer indique au conteneur pour trouver un service nommé my_mailer et de transmettre cet objet dans le constructeur de NewsletterManager. Dans ce cas, cependant, le service de my_mailer spécifié doit exister. Si ce n'est pas le cas, une exception sera levée. Vous pouvez marquer vos dépendances en option - ce point sera discuté dans la section suivante.
Utilisation de références est un outil très puissant qui vous permet de créer des classes de services indépendants avec bien définis dépendances. Dans cet exemple, le service a besoin du service newsletter_manager my_mailer pour fonctionner. Lorsque vous définissez cette dépendance dans le récipient de service, le conteneur prend soin de tout le travail de l'instanciation des objets.
L'injection de dépendances dans le constructeur de cette manière est un excellent moyen de veiller à ce que la dépendance est disponible à utiliser. Si vous avez des dépendances optionnelles pour une classe, puis "l'injection par mutateur" peut être une meilleure option. Cela signifie l'injection de la dépendance à l'aide un appel de méthode plutôt que par le constructeur. La classe devrait ressembler à ceci:
namespace Acme\HelloBundle\Newsletter; use Acme\HelloBundle\Mailer; class NewsletterManager { protected $mailer; public function setMailer(Mailer $mailer) { $this->mailer = $mailer; } // ... }
L'injection de la dépendance par la méthode setter a juste besoin d'un changement de la syntaxe:
# src/Acme/HelloBundle/Resources/config/services.yml parameters: # ... newsletter_manager.class: Acme\HelloBundle\Newsletter\NewsletterManager services: my_mailer: # ... newsletter_manager: class: %newsletter_manager.class% calls: - [ setMailer, [ @my_mailer ] ]
</sr<!-- src/Acme/HelloBundle/Resources/config/services.xml --> <parameters> <!-- ... --> <parameter key="newsletter_manager.class">Acme\HelloBundle\Newsletter\NewsletterManager</parameter> </parameters> <services> <service id="my_mailer" ... > <!-- ... --> </service> <service id="newsletter_manager" class="%newsletter_manager.class%"> <call method="setMailer"> <argument type="service" id="my_mailer" /> </call> </service> </services>c> *** Version Php <src lang="php"> // src/Acme/HelloBundle/Resources/config/services.php use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; // ... $container->setParameter('newsletter_manager.class', 'Acme\HelloBundle\Newsletter\NewsletterManager'); $container->setDefinition('my_mailer', ... ); $container->setDefinition('newsletter_manager', new Definition( '%newsletter_manager.class%' ))->addMethodCall('setMailer', array( new Reference('my_mailer') ));
Les approches présentées dans cette section sont appelés "l'injection de constructeur» et «l'injection par mutateur". Le conteneur de services Symfony2 prend également en charge "par injection de propriété".
Parfois, un de vos services peuvent avoir une dépendance optionnelle, ce qui signifie que la dépendance n'est pas requise pour votre service pour fonctionner correctement. Dans l'exemple ci-dessus, le service my_mailer doit exister, sinon, une exception sera levée. En modifiant la définition du service newsletter_manager, vous pouvez faire cette référence facultative. Le conteneur sera ensuite injecté si il existe et ne rien faire si ce n'est pas:
# src/Acme/HelloBundle/Resources/config/services.yml parameters: # ... services: newsletter_manager: class: %newsletter_manager.class% arguments: [@?my_mailer]
<!-- src/Acme/HelloBundle/Resources/config/services.xml --> <services> <service id="my_mailer" ... > <!-- ... --> </service> <service id="newsletter_manager" class="%newsletter_manager.class%"> <argument type="service" id="my_mailer" on-invalid="ignore" /> </service> </services>
// src/Acme/HelloBundle/Resources/config/services.php use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\ContainerInterface; // ... $container->setParameter('newsletter_manager.class', 'Acme\HelloBundle\Newsletter\NewsletterManager'); $container->setDefinition('my_mailer', ... ); $container->setDefinition('newsletter_manager', new Definition( '%newsletter_manager.class%', array(new Reference('my_mailer', ContainerInterface::IGNORE_ON_INVALID_REFERENCE)) ));
En YAML, l'@ spéciale? Syntaxe indique au conteneur de service que la dépendance est facultative. Bien sûr, le NewsletterManager doit aussi être écrite pour permettre une dépendance optionnelle:
public function __construct(Mailer $mailer = null) { // ... }
Depuis Symfony2 et tous les bundle tiers configurer et de récupérer leurs services via le conteneur, vous pouvez facilement y accéder, voire de les utiliser dans vos propres services. Pour garder les choses simples, Symfony2 par défaut ne nécessite pas que les contrôleurs soient définis comme des services. En outre Symfony2 injecte le conteneur de services complet dans votre contrôleur. Par exemple, pour gérer le stockage de l'information sur la session d'un utilisateur, Symfony2 fournit un service de session, auquel vous pouvez accéder à l'intérieur d'un contrôleur standard comme suit:
public function indexAction($bar) {
$session = $this->get('session'); $session->set('foo', $bar);
// ... }
En Symfony2, vous constamment utiliser les services fournis par le noyau de Symfony ou d'autres tiers bundle pour effectuer des tâches telles que les modèles de rendu (templates), l'envoi de courriels (mailer), ou d'accéder à des informations sur la demande (la demande).
Nous pouvons prendre un peu plus loin en utilisant ces services à l'intérieur des services que vous avez créés pour votre application. Essayons de modifier l'NewsletterManager à utiliser le réel Symfony2 mailer de service (au lieu de la my_mailer semblant). Disons aussi passer le service du moteur de template à l'NewsletterManager afin qu'il puisse générer le contenu du courrier électronique par l'intermédiaire d'un modèle:
namespace Acme\HelloBundle\Newsletter; use Symfony\Component\Templating\EngineInterface; class NewsletterManager { protected $mailer; protected $templating; public function __construct(\Swift_Mailer $mailer, EngineInterface $templating) { $this->mailer = $mailer; $this->templating = $templating; } // ... }
La configuration du conteneur de services est simple:
services: newsletter_manager: class: %newsletter_manager.class% arguments: [@mailer, @templating]
<service id="newsletter_manager" class="%newsletter_manager.class%"> <argument type="service" id="mailer"/> <argument type="service" id="templating"/> </service>
$container->setDefinition('newsletter_manager', new Definition( '%newsletter_manager.class%', array( new Reference('mailer'), new Reference('templating') ) ));
Le service newsletter_manager a désormais accès à l'expéditeur de base et les services de modèles. Il s'agit d'une voie commune pour créer des services spécifiques à votre application qui s'appuient sur la puissance des différents services dans le cadre.
Assurez-vous que l'entrée SwiftMailer apparaît dans votre configuration de l'application. Comme nous l'avons mentionné dans l'importation de configuration via des extensions de conteneurs , la clé SwiftMailer invoque l'extension du service de la SwiftmailerBundle, qui enregistre le service courrier.
Comme nous l'avons vu, la définition des services à l'intérieur du conteneur est facile, impliquant généralement une clé de configuration de service et quelques paramètres. Toutefois, le conteneur a plusieurs autres outils disponibles qui aident à étiqueter des services pour la fonctionnalité spéciale, créer des services plus complexes, et effectuer des opérations après que le récipient est construit.
Lors de la définition des services, vous souhaitez généralement pouvoir accéder à ces définitions au sein de votre code d'application. Ces services sont appelés public. Par exemple, le service doctrine enregistré avec le conteneur lors de l'utilisation de la DoctrineBundle est un service public que vous pouvez y accéder via:
$doctrine = $container->get('doctrine');
Cependant, il y a des cas d'utilisation lorsque vous ne voulez pas un service à caractère public. Ceci est courant quand un service est uniquement défini, car il pourrait être utilisé comme un argument pour un autre service.
Si vous utilisez un service privé comme un argument pour plus d'un autre service, cela se traduira par deux instances différentes d'être utilisés comme l'instanciation du service privé se fait en ligne (par exemple PrivateFooBar nouvelle ()).
Autrement dit: Un service sera privé lorsque vous ne voulez pas y accéder directement depuis votre code.
Voici un exemple:
services: foo: class: Acme\HelloBundle\Foo public: false
<service id="foo" class="Acme\HelloBundle\Foo" public="false" />
$definition = new Definition('Acme\HelloBundle\Foo'); $definition->setPublic(false); $container->setDefinition('foo', $definition);
Maintenant que le service est privé, vous ne pouvez pas appeler:
$container->get('foo');
Toutefois, si un service a été marqué comme privé, vous pouvez toujours alias celui-ci (voir ci-dessous) pour accéder à ce service (via l'alias).
Les services sont par défaut public.
Lors de l'utilisation de base ou des bundle de tiers au sein de votre application, vous pouvez utiliser des raccourcis pour accéder à certains services. Vous pouvez le faire en utilisant un alias eux et, en outre, vous pouvez même alias services non publics.
services: foo: class: Acme\HelloBundle\Foo bar: alias: foo
<service id="foo" class="Acme\HelloBundle\Foo"/> <service id="bar" alias="foo" />
$definition = new Definition('Acme\HelloBundle\Foo'); $container->setDefinition('foo', $definition); $containerBuilder->setAlias('bar', 'foo');
Cela signifie que lorsque vous utilisez le récipient directement, vous pouvez accéder au service foo en demandant le service au bar comme celui-ci:
$container->get('bar'); // Would return the foo service
Il pourrait y avoir des cas d'utilisation lorsque vous avez besoin d'inclure un autre fichier, juste avant le service lui-même est chargé. Pour ce faire, vous pouvez utiliser la directive du fichier.
services: foo: class: Acme\HelloBundle\Foo\Bar file: %kernel.root_dir%/src/path/to/file/foo.php
<service id="foo" class="Acme\HelloBundle\Foo\Bar"> <file>%kernel.root_dir%/src/path/to/file/foo.php</file> </service>
$definition = new Definition('Acme\HelloBundle\Foo\Bar'); $definition->setFile('%kernel.root_dir%/src/path/to/file/foo.php'); $container->setDefinition('foo', $definition);
Notez que symfony interne appeler la fonction PHP require_once qui signifie que votre fichier sera inclus qu'une seule fois par la demande.
De la même manière qu'un billet de blog sur le Web pourrait être balisé avec des choses comme "Symfony" ou "PHP", des services configurés dans votre conteneur peut également être étiqueté. Dans le conteneur de services, une étiquette implique que le service est destiné à être utilisé dans un but précis. Prenons l'exemple suivant:
services: foo.twig.extension: class: Acme\HelloBundle\Extension\FooExtension tags: - { name: twig.extension }
<service id="foo.twig.extension" class="Acme\HelloBundle\Extension\FooExtension"> <tag name="twig.extension" /> </service>
$definition = new Definition('Acme\HelloBundle\Extension\FooExtension'); $definition->addTag('twig.extension'); $container->setDefinition('foo.twig.extension', $definition);
La balise twig.extension est une balise spéciale que le TwigBundle utilise lors de la configuration. En donnant le service ce tag twig.extension, le faisceau sait que le service foo.twig.extension devrait être enregistrée comme une brindille d'extension avec Twig. En d'autres termes, Twig trouve tous les services taggés avec twig.extension et enregistre automatiquement les extensions comme.
Mots-clés, puis, sont une façon de dire Symfony2 ou d'autres tiers bundle que votre service doit être enregistré ou utilisé d'une manière spéciale par le faisceau.
Ce qui suit est une liste de tags disponibles avec les principaux bundle Symfony2. Chacun d'eux a un effet différent sur votre service et de nombreux tags requièrent des arguments supplémentaires (au-delà du paramètre name).