This tutorial has a new version, check it out!

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.

Start your All-Access Pass
Buy just this tutorial for $6.00

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.

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.1.*", // v3.1.4
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.4
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.3.11
        "symfony/monolog-bundle": "^2.8", // 2.11.1
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
        "doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0", // v3.1.3
        "nelmio/alice": "^2.1", // 2.1.4
        "doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
    }
}