Purpose
The purpose of the design pattern
To implement a loosely coupled architecture in order to get better testable, maintainable and extendable code.
Examples
Examples of how the design pattern can be used
- The Doctrine2 ORM uses dependency injection e.g. for configuration that is injected into a Connection object. For testing purposes, one can easily create a mock object of the configuration and inject that into the Connection object
- Many frameworks already have containers for DI that create objects via a configuration array and inject them where needed (i.e. in Controllers)
UML
UML design pattern diagram
Code
Code snippets
DatabaseConfiguration
The configuration class - ideally this needs to implement an Interface with the Interface being type hinted on the DatabaseConnection class.
namespace DesignPatterns\Structural\DependencyInjection;
class DatabaseConfiguration
{
public function __construct(
private string $host,
private int $port,
private string $username,
private string $password
) { }
public function getHost(): string
{
return $this->host;
}
public function getPort(): int
{
return $this->port;
}
public function getUsername(): string
{
return $this->username;
}
public function getPassword(): string
{
return $this->password;
}
}
DatabaseConnection
All you need to look at here really is this line:
public function __construct(private DatabaseConfiguration $configuration)
The database configuration is being injected into the database connection class, which means we could potentially pass in a different configuration if needed (which is useful for testing etc). If we didn’t do this, we’d have to amend the DatabaseConnection some other way, and it can get messy!
Note - we're using PHP 8 property promotion.
public function __construct(private DatabaseConfiguration $configuration)
The database configuration is being injected into the database connection class, which means we could potentially pass in a different configuration if needed (which is useful for testing etc). If we didn’t do this, we’d have to amend the DatabaseConnection some other way, and it can get messy!
Note - we're using PHP 8 property promotion.
namespace DesignPatterns\Structural\DependencyInjection;
class DatabaseConnection
{
public function __construct(private DatabaseConfiguration $configuration)
{
}
public function getDsn(): string
{
// this is just for the sake of demonstration, not a real DSN
// notice that only the injected config is used here, so there is
// a real separation of concerns here
return sprintf(
'%s:%s@%s:%d',
$this->configuration->getUsername(),
$this->configuration->getPassword(),
$this->configuration->getHost(),
$this->configuration->getPort()
);
}
}