How autowiring works
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 SubscribeHey, look! It's our favorite command!
bin/console debug:autowiring
This shows us a list of the services we can autowire in our code. But how does autowiring actually work? Let's run another command:
bin/console debug:container
This gives us a huge list of services, and any Service ID that happens to be a class or interface name is autowirable. This means we can typehint it in the constructor of our service and the service container will inject that service. Conversely, if a Service ID is not an interface or class name, it's not autowirable. This is by design, since most services are low-level and just exist to help other services behind the scenes. We'll rarely ever need to use those low-level services directly, and that's why we can't fetch them via autowiring. And that's why debug:container
has way more entries compared to debug:autowiring
.
Debugging the Container
The service container is basically a giant array where each service has a unique name that points to the corresponding service object. In the case of twig
, for example, the container knows that to instantiate this service, it needs to create an instance of this Twig\Environment
class. And even if we don't see the arguments here, it knows exactly which ones it needs to pass to instantiate it. As a bonus, if we make a request for the same service in more than one place, the service container only creates one instance, so we'll have the exact same instance everywhere.
You also may have noticed these service classes. This CacheInterface
, for example, was used earlier as an alias for our cache.app
service. This is just a way to make a service like cache.app
autowirable. The vast majority of these services use the snake case naming strategy, so to make these autowireable in our code, bundles add some aliases - class names, or interfaces - that we can typehint in our code. So aliases are basically just like symbolic links that just refer to other services. However, there may be times when there are multiple services in the container that implement the same class or interface.
Custom Cache Pool
To handle that, back in our code, let's create a custom cache pool. In config/packages/cache.yaml
, down here, uncomment the pools
key, and instead of this example, say iss_location_pool: null
.
framework: | |
cache: | |
// ... lines 3 - 19 | |
pools: | |
iss_location_pool: null |
Now, at your terminal, run:
bin/console debug:autowiring
And... check it out! This configuration added a brand new service - iss_location_pool
- which has the same CacheInterface
as cache.app
. Back over in src/Controller/MainController.php
, inside homepage()
, let's change this variable name to $issLocationPool
and keep the CacheInterface
typehint the same. Copy that variable name and, down here, paste.
// ... lines 1 - 13 | |
class MainController extends AbstractController | |
{ | |
// ... line 16 | |
public function homepage( | |
// ... lines 18 - 19 | |
CacheInterface $issLocationPool, | |
): Response { | |
// ... lines 22 - 24 | |
$issData = $issLocationPool->get('iss_location_data', function (ItemInterface $item) use ($client): array { | |
// ... lines 26 - 30 | |
}); | |
// ... lines 32 - 37 | |
} | |
} |
This is called "named autowiring" - where our service container looks at the variable name and its typehint to inject the correct service. It's pretty rare, but we can also see this with our logger
service.
Back at our browser, refresh the page and check the cache profile. Here's our iss_location_pool
and our iss_location_data
is written to that pool. If we ever need to clear the cache for this pool, over in our terminal, run:
bin/console cache:pool:clear iss_location_pool
That clears the cache for this exact pool without affecting the other pools. Pretty handy!
We can also configure this pool differently from other pools. For example, let's set the expiration time for our new pool in the config file. Over in cache.yaml
, instead of null
, on a new line, write default_lifetime: 5
.
framework: | |
cache: | |
// ... lines 3 - 19 | |
pools: | |
iss_location_pool: | |
default_lifetime: 5 |
The 5
is in seconds. This should impact all of the cache items in this pool. Now, in MainController.php
, we can remove $item->expiresAfter()
. We can also get rid of this $item
argument altogether.
// ... lines 1 - 13 | |
class MainController extends AbstractController | |
{ | |
// ... line 16 | |
public function homepage( | |
// ... lines 18 - 20 | |
): Response { | |
// ... lines 22 - 24 | |
$issData = $issLocationPool->get('iss_location_data', function () use ($client): array { | |
$response = $client->request('GET', 'https://api.wheretheiss.at/v1/satellites/25544'); | |
return $response->toArray(); | |
}); | |
// ... lines 30 - 35 | |
} | |
} |
To make sure this is working, over in our browser, refresh the homepage again and... no errors. It works!
Next: Let's talk about environments - sets of configurations that help us develop locally versus on production.
Hello, no errors in this video? On reload every time we use HTTP client request with app:
cache.adapter.array