Do you work with Symfony from 2 through 7? Then you know the main challenge in the upgrade path is to trim your YAML configs to a minimum.
Where we needed 300+ lines in configs, we are now good with 2. How to get there fast and reliable?
I'll show a trick we use in the PHP project to get there once and for all.
Physical industry and software have much in common and can learn from each other. The process is being automated, and the simpler beats complex and simple in automated process scales squared.
In the same way, the Tesla car frame used to be built from 170+ pieces, but now it is just 2 pieces with one innovative machine press.
Narrowing YAML or array dinosaur files to a few lines is an area I wrote many times before.
I know that every Symfony project can be loaded with a single line of autodiscovery calls if done correctly. Once we've done the easy part of autowire and autodiscovery, we can focus on the challenges I'll show you today.
If you ask Symfony/Laravel container for a PasswordHasher
service and there is exactly one instance in our whole container, it will be autowired.
services:
aws_client:
class: HttpClient
arguments:
$name: tom
$password: 123
bank_client:
class: HttpClient
arguments:
$name: john
$password: 456
Here, we have 2 instances of the HttpClient
service, but they are the same type.
The proclaimed reason to use config coding here is "to provide manual configuration". We can do better.
To use one of them, we have to define it explicitly:
services:
another_service:
- '@aws_client'
Here is a challenge: how to tell Symfony/Laravel to autowire them uniquely and drop the whole configuration?
We already know that container works for us once we ask it for a unique typed service. So, we create 2 unique classes:
-HttpClient
+AwsHttpClient
-HttpClient
+BankHttpClient
Now we move the configuration to the service itself, we get better:
final class AwsHttpClient extends HttpClient
{
public function __construct()
{
// static checks right in the code
parent::__construct('tom', '123');
}
}
final class BankHttpClient extends HttpClient
{
public function __construct()
{
parent::__construct('john', '456');
}
}
That's it!
We can now:
getenv()
andBANK_NAME
and BANK_PASSWORD
are defined in our .env
filesfinal class BankHttpClient extends HttpClient
{
public function __construct()
{
parent::__construct(getenv('BANK_NAME'), getenv('BANK_PASSWORD'));
}
}
As a bonus, any PHP DI container will now handle autowire for you, so you can switch back and forth with no config refactorings.
Happy coding!
Do you learn from my contents or use open-source packages like Rector every day?
Consider supporting it on GitHub Sponsors.
I'd really appreciate it!