Updated Rector YAML to PHP configuration, as current standard.
Action Injections are much fun, but it can turn your project to legacy very fast. How to refactor out of the legacy back to constructor injection and still keep that smile on your face?
I wrote about How to Slowly Turn your Symfony Project to Legacy with Action Injection a few weeks ago. It surprised me that the approach had mostly positive feedback:
Couldn't agree more with pretty much everything said! Action injection makes it really confusing on whether an object is treated stateful or stateless (very grey area with the Session for example).
I'm a Symfony trainer and I'm told to teach people how to use Symfony and talk about this injection pattern. Sob.
I'm working on a Project that uses action injection pattern and I hate it. I like autowiring but the whole idea about action injection is broken. And this project is in sf28 do we don't use autowiring. Maintenance and development with this pattern is a total nightmare.
It's natural to try new patterns with an open heart and validate them in practice, but what if you find this way as not ideal and want to go to constructor injection instead?
How would you change all your 50 controllers with action injections...
<?php declare(strict_types=1);
namespace App\Controller;
final class SomeController
{
public function detail(int $id, Request $request, ProductRepository $productRepository)
{
$this->validateRequest($request);
$product = $productRepository->find($id);
// ...
}
}
...to the constructor injection:
<?php declare(strict_types=1);
namespace App\Controller;
final class SomeController
{
/**
* @var ProductRepository
*/
private $productRepository;
public function __construct(ProductRepository $productRepository)
{
$this->productRepository = $productRepository;
}
public function detail(int $id, Request $request)
{
$this->validateRequest($request);
$product = $this->productRepository->find($id);
// ...
}
}
Request
objects and Argument Resolver objects
I find the time of my team very precious, don't you? So I Let Rector do the work.
composer install rector/rector --dev
Add the action-injection-to-constructor-injection
set and configure your Kernel class name.
use Rector\Core\Configuration\Option;
use Rector\Set\ValueObject\SetList;
use Rector\Config\RectorConfig;
return function (RectorConfig $rectorConfig): void {
$rectorConfig->import(SetList::ACTION_INJECTION_TO_CONSTRUCTOR_INJECTION);
$parameters = $rectorConfig->parameters();
// the default value
$parameters->set('kernel_class', 'App\Kernel');
};
vendor/bin/rector process /app --dry-run
You should see diffs like:
<?php declare(strict_types=1);
namespace App\Controller;
final class SomeController
{
+ /**
+ * @var ProductRepository
+ */
+ private $productRepository;
+
+ public function __construct(ProductRepository $productRepository)
+ {
+ $this->productRepository = $productRepository;
+ }
+
- public function detail(int $id, Request $request, ProductRepository $productRepository)
+ public function detail(int $id, Request $request)
{
$this->validateRequest($request);
- $product = $productRepository->find($id);
+ $product = $this->productRepository->find($id);
// ...
}
}
Are all looking good? Run it:
vendor/bin/rector process /app
You've probably noticed that code itself is not looking too good. Rector's job is not to clean, but to change the code. It's not a hipster designer, but rather a thermonuclear engineer. That's why there are coding standards. You can apply your own or if not good enough use Rector's prepared set:
composer require symplify/easy-coding-standard --dev
vendor/bin/ecs --config vendor/rector/rector/ecs-after-rector.php --fix
And your code is now both refactored and clean. That's it!
Happy instant refactoring!
Do you learn from my contents or use open-souce packages like Rector every day?
Consider supporting it on GitHub Sponsors.
I'd really appreciate it!