Creative Commons License
This work is licensed under a
Creative Commons
Attribution-Share Alike 3.0
Unported License.

Master Symfony2 fundamentals

Be trained by SensioLabs experts (2 to 6 day sessions -- French or English).
trainings.sensiolabs.com

Symfony hosting done right

ServerGrove, outstanding support at the right price for your Symfony hosting needs.
servergrove.com

Discover the SensioLabs Support

Access to the SensioLabs Competency Center for an exclusive and tailor-made support on Symfony
sensiolabs.com

Il componente Dependency Injection

Caution: You are browsing the documentation for Symfony version 2.0 which is not maintained anymore. If some of your projects are still using this version, consider upgrading.

Il componente Dependency Injection

Il componente Dependency Injection consente di standardizzare e centralizzare il modo in cui gli oggetti sono costruiti nelle applicazioni.

Per un'introduzione a Dependency Injection e ai contenitori di servizi, vedere Contenitore di servizi

Installazione

È possibile installare il componente in diversi modi:

  • Utilizzando il repository ufficiale su Git (https://github.com/symfony/DependencyInjection);
  • Installandolo via PEAR ( pear.symfony.com/DependencyInjection);
  • Installandolo via Composer (symfony/dependency-injection su Packagist).

Utilizzo

Si potrebbe avere una semplice classe, come la seguente Mailer, che si vuole rendere disponibile come servizio:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Mailer
{
    private $transport;

    public function __construct()
    {
        $this->transport = 'sendmail';
    }

    // ...
}

La si può registrare nel contenitore come servizio:

1
2
3
4
use Symfony\Component\DependencyInjection\ContainerBuilder;

$sc = new ContainerBuilder();
$sc->register('mailer', 'Mailer');

Si potrebbe migliorare la classe, per renderla più flessibile, consentendo al contenitore di impostare il trasporto usato. Si può cambiare la classe, in modo che il trasporto sia passato al costruttore:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Mailer
{
    private $transport;

    public function __construct($transport)
    {
        $this->transport = $transport;
    }

    // ...
}

Si può quindi impostare la scelta del trasporto nel contenitore:

1
2
3
4
5
use Symfony\Component\DependencyInjection\ContainerBuilder;

$sc = new ContainerBuilder();
$sc->register('mailer', 'Mailer')
    ->addArgument('sendmail'));

La classe ora è molto più flessibile, perché la scelta del trasporto è stata separata dall'implementazione e posta nel contenitore.

La scelta del trasporto potrebbe interessare anche altri servizi. Si può evitare di doverlo cambiare in posti differenti, trasformandolo in un parametro nel contenitore e facendo riferimento a tale parametro per il costruttore del servizio Mailer:

1
2
3
4
5
6
use Symfony\Component\DependencyInjection\ContainerBuilder;

$container = new ContainerBuilder();
$container->setParameter('mailer.transport', 'sendmail');
$container->register('mailer', 'Mailer')
    ->addArgument('%mailer.transport%');

Ora che il servizio mailer è nel contenitore, lo si può iniettare come dipendenza di altre classi. Se si ha una classe NewsletterManager come questa:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
use Mailer;

class NewsletterManager
{
    private $mailer;

    public function __construct(Mailer $mailer)
    {
        $this->mailer = $mailer;
    }

    // ...
}

Allora la si può registrare come servizio e passarle il servizio mailer:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

$container = new ContainerBuilder();

$container->setParameter('mailer.transport', 'sendmail');
$container->register('mailer', 'Mailer')
    ->addArgument('%mailer.transport%');

$container->register('newsletter_manager', 'NewsletterManager')
    ->addArgument(new Reference('mailer');

Se NewsletterManager non richiedesse Mailer e l'iniezione fosse quindi solamente opzionale, la si potrebbe passare usando un setter:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
use Mailer;

class NewsletterManager
{
    private $mailer;

    public function setMailer(Mailer $mailer)
    {
        $this->mailer = $mailer;
    }

    // ...
}

Ora si può scegliere di non iniettare un Mailer dentro NewsletterManager. Se comunque lo si volesse fare, il contenitore può richiamare il metodo setter:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

$container = new ContainerBuilder();

$container->setParameter('mailer.transport', 'sendmail');
$container->register('mailer', 'Mailer')
    ->addArgument('%mailer.transport%');

$container->register('newsletter_manager', 'NewsletterManager')
    ->addMethodCall('setMailer', new Reference('mailer');

Si può quindi ottenere il servizio newsletter_manager dal contenitore, in questo modo:

1
2
3
4
5
6
7
8
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

$container = new ContainerBuilder();

//--

$newsletterManager = $sc->get('newsletter_manager');

Evitare che il proprio codice dipenda dal contenitore

Sebbene si possano recuperare servizi direttamente dal contenitore, sarebbe meglio minimizzarlo. Per esempio, in NewsletterManager abbiamo iniettato il servizio mailer, piuttosto che richiederlo al contenitore. Avremo potuto iniettare il contenitore e recuperare da esso il servizio mailer, ma allora sarebbe stato legato a questo particolare contenitore, rendendo difficile riusare la classe altrove.

A un certo punto si avrà la necessità di ottenere un servizio dal contenitore, ma lo si dovrebbe fare il meno possibile e all'inizio della propria applicazione.

Impostare il contenitore con file di configurazione

Oltre a impostare servizi usando PHP, come sopra, si possono usare dei file di configurazione. Per poterlo fare, occorre installare anche il componente Config:

  • Usare il repository Git (https://github.com/symfony/Config);
  • Installarlo tramite PEAR (pear.symfony.com/Config);
  • Installarlo tramite Composer (symfony/config on Packagist).

Caricare un file di configurazione xml:

1
2
3
4
5
6
7
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;

$container = new ContainerBuilder();
$loader = new XmlFileLoader($container, new FileLocator(__DIR__));
$loader->load('services.xml');

Caricare un file di configurazione yaml:

1
2
3
4
5
6
7
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;

$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(__DIR__));
$loader->load('services.yml');

I servizi newsletter_manager e `` mailer`` possono essere impostati da file di configurazione:

  • YAML
    # src/Acme/HelloBundle/Resources/config/services.yml
    parameters:
        # ...
        mailer.transport: sendmail
    
    services:
        mailer:
            class:     Mailer
            arguments: [%mailer.transport%]
        newsletter_manager:
            class:     NewsletterManager
            calls:
                - [ setMailer, [ @mailer ] ]
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    <!-- src/Acme/HelloBundle/Resources/config/services.xml -->
    <parameters>
        <!-- ... -->
        <parameter key="mailer.transport">sendmail</parameter>
    </parameters>
    
    <services>
        <service id="mailer" class="Mailer">
            <argument>%mailer.transport%</argument>
        </service>
    
        <service id="newsletter_manager" class="NewsletterManager">
            <call method="setMailer">
                 <argument type="service" id="mailer" />
            </call>
        </service>
    </services>
    
  • PHP
    1
    2
    3
    4
    5
    6
    7
    8
    9
    use Symfony\Component\DependencyInjection\Reference;
    
    // ...
    $container->setParameter('mailer.transport', 'sendmail');
    $container->register('mailer', 'Mailer')
       ->addArgument('%mailer.transport%');
    
    $container->register('newsletter_manager', 'NewsletterManager')
       ->addMethodCall('setMailer', new Reference('mailer');