Injecting the Cache Service
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.
Phew! Dependency injection, check! Registering new services, check! Delicious snack, check! Well, I hope you just had a delicious snack.
This tutorial is the start to our victory lap. We need to add caching to MarkdownTransformer
:
it should be pretty easy. Copy part of the old caching code and paste that into
the parse()
function. Remove the else
part of the if
and just return $cache->fetch()
:
// ... lines 1 - 6 | |
class MarkdownTransformer | |
{ | |
// ... lines 9 - 15 | |
public function parse($str) | |
{ | |
$cache = $this->get('doctrine_cache.providers.my_markdown_cache'); | |
$key = md5($str); | |
if ($cache->contains($key)) { | |
return $cache->fetch($key); | |
} | |
// ... lines 23 - 29 | |
} | |
} |
Below, assign the method call to the $str
variable and go copy the old $cache->save()
line. Return $str
and re-add the sleep()
call so that things are really slow - that
keeps it interesting:
// ... lines 1 - 6 | |
class MarkdownTransformer | |
{ | |
// ... lines 9 - 15 | |
public function parse($str) | |
{ | |
// ... lines 18 - 23 | |
sleep(1); | |
$str = $this->markdownParser | |
->transformMarkdown($str); | |
$cache->save($key, $str); | |
return $str; | |
} | |
} |
On top, change the $funFact
variables to $str
. Perfect!
We know this won't work: there is no get()
function in this class. And more importantly,
we don't have access to the doctrine_cache.provider.my_markdown_cache
service.
How can we get access? Dependency injection.
Dependency Inject!
This time, add a second argument to the constructor called $cache
. And hmm,
we should give this a type-hint. Copy the service name and run:
./bin/console debug:container doctrine_cache.providers.my_markdown_cache
This service is an instance of ArrayCache
. But wait! Do not type-hint that. In
our earlier course on environments, we setup a cool system that uses ArrayCache
in
the dev
environment and FilesystemCache
in prod
:
// ... lines 1 - 7 | |
parameters: | |
locale: en | |
cache_type: file_system | |
// ... lines 11 - 65 | |
doctrine_cache: | |
providers: | |
my_markdown_cache: | |
type: %cache_type% | |
file_system: | |
directory: %kernel.cache_dir%/markdown_cache |
If we type-hint with ArrayCache
, this will explode in prod
because this service
will be a different class.
Let's do some digging: open up ArrayCache
:
// ... lines 1 - 32 | |
class ArrayCache extends CacheProvider | |
{ | |
// ... lines 35 - 93 | |
} |
This extends CacheProvider
:
// ... lines 1 - 31 | |
abstract class CacheProvider implements Cache, FlushableCache, ClearableCache, MultiGetCache | |
{ | |
// ... lines 34 - 276 | |
} |
That might work. But it implements several interface - one of them is just
called Cache
. Let's try that. If this isn't the right interface - meaning it
doesn't contain the methods we're using - PhpStorm will keep highlighting
those after we add the type-hint:
// ... lines 1 - 7 | |
class MarkdownTransformer | |
{ | |
// ... lines 10 - 12 | |
public function __construct(MarkdownParserInterface $markdownParser, Cache $cache) | |
{ | |
// ... lines 15 - 16 | |
} | |
// ... lines 18 - 33 | |
} |
I'll use a keyboard shortcut - option
+enter
on a Mac - and select initialize fields:
// ... lines 1 - 7 | |
class MarkdownTransformer | |
{ | |
// ... line 10 | |
private $cache; | |
public function __construct(MarkdownParserInterface $markdownParser, Cache $cache) | |
{ | |
// ... line 15 | |
$this->cache = $cache; | |
} | |
// ... lines 18 - 33 | |
} |
All this did was add the private $cache
property and set it in __construct()
.
You can also do that by hand.
Cool! Update parse()
with $cache = $this->cache
:
// ... lines 1 - 7 | |
class MarkdownTransformer | |
{ | |
// ... lines 10 - 18 | |
public function parse($str) | |
{ | |
$cache = $this->cache; | |
// ... lines 22 - 32 | |
} | |
} |
And look! All of the warnings went away. That was the right interface to use. Yay!
Because we added a new constructor argument, we need to update any code that instantiates
the MarkdownTransformer
. But now, that's not done by us: it's done by Symfony,
and we help it in services.yml
. Under arguments, add a comma and quotes. Copy
the service name - @doctrine_cache.providers.my_markdown_cache
and paste it here:
// ... lines 1 - 5 | |
services: | |
app.markdown_transformer: | |
class: AppBundle\Service\MarkdownTransformer | |
arguments: ['@markdown.parser', '@doctrine_cache.providers.my_markdown_cache'] |
That's it! That's the dependency injection pattern.
Go back to refresh. The sleep()
should make it really slow. And it is slow.
Refresh again: still slow because we setup caching to really only work in the prod
environment.
Clear the prod
cache:
./bin/console cache:clear --env=prod
And now add /app.php/
in front of the URI to use this environment. This should be
slow the first time... but then fast after. Super fast! Caching is working. And
dependency injection is behind us.
I get the following error at the end: Catchable Fatal Error: Argument 2 passed to AppBundle\Service\MarkdownTransformer::__construct() must be an instance of Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache, instance of Doctrine\Common\Cache\ArrayCache given.
MarkdownTransformer.php:
namespace AppBundle\Service;
use Knp\Bundle\MarkdownBundle\MarkdownParserInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
class MarkdownTransformer
{
private $markdownParser;
private $cache;
public function __construct(MarkdownParserInterface $markdownParser, Cache $cache)
{
$this->markdownParser = $markdownParser;
$this->cache = $cache;
}
public function parse($str)
{
$cache = $this->cache;
$key = md5($str);
if ($cache->contains($key)) {
return $cache->fetch($key);
}
sleep(1);
$str = $this->markdownParser
->transformMarkdown($str);
$cache->save($key, $str);
return $str;
}
}
services.yml:
# Learn more about services, parameters and containers at
# http://symfony.com/doc/curr...
parameters:
# parameter_name: value
services:
app.markdown_transformer:
class: AppBundle\Service\MarkdownTransformer
arguments: ['@markdown.parser','@doctrine_cache.providers.my_markdown_cache']
When I run /bin/console debug:container doctrine_cache.providers.my_markdown_cache It shows the following line:
Class Doctrine\Common\Cache\ArrayCache