An open API service indexing awesome lists of open source software.

https://github.com/nette-examples/di-example-newsletter

Dependency Injection Container example
https://github.com/nette-examples/di-example-newsletter

Last synced: 11 months ago
JSON representation

Dependency Injection Container example

Awesome Lists containing this project

README

          

Example of using Nette DI Container
===================================

The essence of Dependency Injection (DI) is to unburden classes from creating objects they rely on. Such objects are termed **services**. Further details can be found on the [official website](https://doc.nette.org/dependency-injection).

Installation
------------

To get started:

```shell
git clone https://github.com/nette-examples/di-example-newsletter
cd di-example-newsletter
composer install
```

Run the Demo
------------

Execute the demo via:

```shell
php example.php
```

PHP Requirements
----------------

Ensure your environment runs PHP version 8.1.

Using the Application
---------------------

Consider an application designed for newsletter distribution:

The `Mail` class represents an email:

```php
class Mail
{
public string $subject;
public string $message;
}
```

The `Mailer` interface defines email sending:

```php
interface Mailer
{
function send(Mail $mail, string $to): void;
}
```

The `Logger` interface provides logging capabilities:

```php
interface Logger
{
function log(string $message): void;
}
```

The `NewsletterManager` class manages newsletter distribution:

```php
class NewsletterManager
{
private Mailer $mailer;
private Logger $logger;

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

public function distribute(array $recipients): void
{
$mail = new Mail;
$mail->subject = '...';
$mail->message = '...';

foreach ($recipients as $recipient) {
$this->mailer->send($mail, $recipient);
}
$this->logger->log('...');
}
}
```

The code respects Dependency Injection, ie. **each object uses only variables which we had passed into it.**

Also, we have a ability to implement own `Logger` or `Mailer`, like this:

```php
class SendMailMailer implements Mailer
{
public function send(Mail $mail, string $to): void
{
mail($to, $mail->subject, $mail->message);
}
}

class FileLogger implements Logger
{
private string $file;

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

public function log(string $message): void
{
file_put_contents($this->file, $message . "\n", FILE_APPEND);
}
}
```

**DI container is the central orchestrator** that can instantiate individual objects (referred to as services in DI terminology) and configure them as required.

An example container for our application:

```php
class Container
{
private ?Logger $logger;
private ?Mailer $mailer;

public function getLogger(): Logger
{
if (!isset($this->logger)) {
$this->logger = new FileLogger('log.txt');
}
return $this->logger;
}

public function getMailer(): Mailer
{
if (!isset($this->mailer)) {
$this->mailer = new SendMailMailer;
}
return $this->mailer;
}

public function createNewsletterManager(): NewsletterManager
{
return new NewsletterManager($this->getMailer(), $this->getLogger());
}
}
```

The key advantage of DI is that no class is directly dependent on the container, enabling easy substitution with another container, like one generated by Nette DI.

Let's instantiate `Container`, let it create manager and we can start spamming users with newsletters :-)

```php
$container = new Container;
$manager = $container->createNewsletterManager();
$manager->distribute(...);
```

Significant to Dependency Injection is that no class depends on the container. Thus it can be easily replaced with another one. For example with the container generated by Nette DI.

Introduction to Nette DI
------------------------

Nette DI acts as a dynamic container generator, primarily utilizing configuration files to shape its behavior. The configuration shown below exemplifies a structure similar to the manually defined `Container` class:

```neon
services:
- FileLogger( log.txt )
- SendMailMailer
- NewsletterManager
```

A standout feature of Nette DI is the succinctness of its configuration syntax.

One of the pivotal aspects of Nette DI is its ability to generate the actual PHP code for the container. This ensures not just high performance but also transparency, allowing developers to delve into the generated code for clarity and debugging purposes.

Setting up and using Nette DI is intuitive:

```php
$loader = new Nette\DI\ContainerLoader(__DIR__ . '/temp');
$class = $loader->load(function($compiler) {
$compiler->loadConfig(__DIR__ . '/config.neon');
});
$container = new $class;
```

Once the container is set up, it's easy to instantiate services like the `NewsletterManager` and perform actions, such as sending out emails:

```php
$manager = $container->getByType(NewsletterManager::class);
$manager->distribute(['john@example.com', ...]);
```

For efficiency, the container is generated just once, with the resultant code cached in the specified directory (`__DIR__ . '/temp'`). The configuration file is thus loaded inside the closure passed to `$loader->load()`, ensuring it's invoked a singular time.

To enhance the development experience, Nette DI offers an auto-refresh mode. When enabled, it detects and incorporates changes from any modified class or configuration file. This feature can be activated by passing `true` as the second argument during `ContainerLoader` instantiation:

```php
$loader = new Nette\DI\ContainerLoader(__DIR__ . '/temp', autoRebuild: true);
```