Service Action Injection
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeYou may have noticed that I seem to be avoiding "action" injection. For both QuestionRepository
and ChartBuilderInterface
, normally, when I'm in a controller, I'll like to be lazy and autowire them directly into the controller method.
The Problem with Action Injection
Let's actually try that, at least for ChartBuilderInterface
. Remove ChartBuilderInterface
from the constructor... and, instead add it to the method: ChartBuilderInterface $chartBuilder
.
// ... lines 1 - 24 | |
class DashboardController extends AbstractDashboardController | |
{ | |
// ... lines 27 - 28 | |
public function __construct(QuestionRepository $questionRepository) | |
{ | |
$this->questionRepository = $questionRepository; | |
} | |
// ... lines 33 - 35 | |
public function index(ChartBuilderInterface $chartBuilder = null): Response | |
{ | |
// ... lines 38 - 49 | |
} | |
// ... lines 51 - 125 | |
} |
And now... I need to pass $chartBuilder
into createChart()
... because, down here we can't reference the property anymore. So add ChartBuilderInterface $chartBuilder
... and use that argument.
// ... lines 1 - 35 | |
public function index(ChartBuilderInterface $chartBuilder = null): Response | |
{ | |
// ... lines 38 - 44 | |
return $this->render('admin/index.html.twig', [ | |
// ... lines 46 - 47 | |
'chart' => $this->createChart($chartBuilder), | |
]); | |
} | |
// ... lines 51 - 99 | |
private function createChart(ChartBuilderInterface $chartBuilder): Chart | |
{ | |
$chart = $chartBuilder->createChart(Chart::TYPE_LINE); | |
// ... lines 103 - 124 | |
} | |
// ... lines 126 - 127 |
Cool. So in theory, this should work... because this is a normal controller and... this is how action injection works! But you might already notice that PhpStorm is pretty mad. And, it's right! If we refresh, huge error!
DashboardController::index
must be compatible withAbstractDashboardController::index
.
The problem is that our parent class - AbstractDashboardController
- has an index()
method with no arguments. So it's not legal for us to override that and add a required argument.
The Workaround
But if you do want action injection to work, there is a workaround: allow the argument to be optional. So add = null
.
That makes PHP happy and, in practice, even though it's optional, Symfony will pass the chart builder service. So this will work... but to code defensively just in case, I'm going to add a little assert()
function.
This may or may not be a function you're familiar with. It comes from PHP itself. You put an expression inside like null !== $chartBuilder
- and if that expression is false, an exception will be thrown.
// ... lines 1 - 35 | |
public function index(ChartBuilderInterface $chartBuilder = null): Response | |
{ | |
assert(null !== $chartBuilder); | |
// ... lines 39 - 49 | |
} | |
// ... lines 51 - 127 |
So now we can confidently know that if our code gets this far, we do have a ChartBuilderInterface
object.
Refresh now and... got it! So action injection does still work... but it's not as awesome as it normally is. Though, it does have one concrete advantage over constructor injection: the ChartBuilderInterface
service won't be instantiated unless the index()
method is called. So if you were in a normal Crud controller with multiple actions, action injection allows you to make sure that a service is only instantiated for the action that needs it, instead of in all situations.
Next: let's learn how to override templates, like EasyAdmin's layout template, or how an IdField
is rendered across our entire admin area.
I know you use that trick a lot, but for me
assert(null === 'test');
doesn't throw an exception.Your statement might be misleading.
In fact, I think it will not throw an exception in most cases. I use
assert()
when I want my IDE to know what I expect when I can't use type hints.See https://www.php.net/manual/en/function.assert.php