cult3

Laravel 4 Cache as a Service

Feb 03, 2014

Table of contents:

  1. Why a service?
  2. How will Cache as a service work?
  3. What is the Decorator Pattern?
  4. Create the Cache Interface
  5. Create the Laravel Cache Service
  6. Create the Abstract Decorator
  7. Cache Decorator
  8. Add to the Service Provider
  9. Tests
  10. Conclusion

Cache is an incredibly important component of a web application stack. If you are not caching the data in your application you will likely hit premature bottlenecks and your users will suffer with slow response times. Implementing caching is a big step towards scaling an application and will significantly reduce the overhead of your application.

A couple of weeks ago I looked at using Cache in your Laravel 4 application. Laravel makes it really easy to implement caching in your application by providing a clear and powerful API whilst abstracting the specific implementation of the actual caching mechanism.

At the end of that tutorial I noted that whilst it’s easy to actually use the Laravel Cache API, how to structure and use Cache in terms of your application is not quite so obvious.

In this article I’m going to be looking at setting up Cache as a service in your application. As with my article on setting up advanced Validation Services in Laravel 4, this article was also heavily inspired by the excellent work of Chris Fidao from his book, Implementing Laravel. If you are looking to learn more about these types of patterns in modern web applications, I highly recommend that you grab yourself a copy.

Why a service?

If you have been following a long with this series you will have read my articles on advanced Validation Services, using Repositories and creating Entities.

By structuring our code as separate services we benefit in a number of ways.

Firstly by separating the caching layer from the storage or entity layer you make testing in isolation much simpler. If your Cache was tightly coupled with your storage your tests would involve lots of mocks and it would be difficult to ensure that you were really testing the right thing.

Secondly, by creating separate layers of functionality, we make combining these services really easy. This also has the benefit that if we need to change something in the future, we only have to change a single aspect of our code once, or we could either swap out the implementation or even remove it all together.

Dealing with Cache in your Controller would be a terrible idea, but dealing with it as a service as part of your application specific code means that we can reuse this service for our application, API or even a command line client.

How will Cache as a service work?

Before I get into actually writing the code to create a Cache Service, first I will explain how this is going to work and how it will be structured.

Cache Interface Firstly I will create a Cache Interface that my Cache Service will implement. As I’ve mentioned in previous articles, this ensures that the methods that are defined on this contract are available on the specific implementation of the class and we can type hint the class to ensure that the methods are available.

Laravel Cache Service Secondly, I will create a Laravel implementation of my Cache Service. This is basically the same as what I did when I created my Laravel implementation of my Validation Service in this article. This means if I wanted to move away from Laravel’s Cache API and use a generic third party implementation instead I’m not going to be handcuffed to Laravel.

Abstract Decorator In order for this to work I will be using the Decorator Pattern (I’ll explain what that is below) as a wrapper to my Repository. So for my User Repository I will create an Abstract User Decorator.

Cache Decorator Now that I have the Abstract Decorate I can create a specific Cache Decorator which can be wrapped around the Repository to implement caching.

Add to the Service Provider And finally I can add the Cache Decorator to the Repository Service Provider so my caching layer is automatically implemented on the Repository. This means I can continue to use the Repository in exactly the same way up until this point, but instead of hitting the database, the cache layer will return data from the cache if it is available. The rest of my application does not need to know whether caching is implemented or not.

What is the Decorator Pattern?

The Decorator Pattern is a way of adding functionality to an object without modifying the original object. This means you can extend the functionality of an object without effecting any other instances of that same object or you can add many different decorators to an object to modify the functionality of the object in different ways.

So how will this work in this case?

In the Service Provider we will create the Repository as normal by binding the EloquentUserRepository class to the UserInterface to implement the Repository pattern.

We will then pass that object into the Cache Decorator. The Cache Decorator will have a method for each of the methods that we want to implement caching on.

If the Cache Decorator has the data that we require in the cache, we can simply return the cache. However if that data is not in the cache, we can fall back to the Repository and return the data from the database whilst also caching the returned data for subsequent requests.

Finally in the Service Provider we can return the User Repository with the Cache Decorator. This means if we wanted to add multiple Decorators to the Repository, we could do so in the Service Provider but the rest of our application wouldn’t know the difference.

Hopefully that makes sense. Basically we are just taking the original Repository and “wrapping” it in additional functionality. If the Cache layer has the correct data we can return it without touching the database. If the Cache layer does not have the data we can go down a layer to the Repository and return the data from the database whilst also caching the returned data for the next request.

Create the Cache Interface

So the first thing to do is to create the Cache Interface:

<?php namespace Cribbb\Service\Cache;

interface CacheInterface
{
    /**
     * Get
     *
     * @param string $key
     * @return mixed
     */
    public function get($key);

    /**
     * Put
     *
     * @param string $key
     * @param mixed $value
     * @param integer $minutes
     * @return mixed
     */
    public function put($key, $value, $minutes = null);

    /**
     * Has
     *
     * @param string $key
     * @return bool
     */
    public function has($key);
}

Currently this Interface is basically the same as the Laravel Cache interface. However, by defining our own interface, we are fee to modify or add methods or functionality that we need for this particular application.

Hopefully the above code should be pretty familiar by now. Here I’m basically defining a contract that will enforce any class that uses this interface to have the above methods.

Create the Laravel Cache Service

Next we can create the Laravel specific Cache Service. As with the Validation Service, this is a Laravel implementation of the Cache Service using Laravel’s built in Cache class. This class will implement the CacheInterface that we just created. If you wanted to use a different Caching system in the future, you would just need to create a new implementation like this to make the change:

<?php namespace Cribbb\Service\Cache;

use Illuminate\Cache\CacheManager;

class LaravelCache implements CacheInterface
{
    /**
     * @var Illuminate\Cache\CacheManager
     */
    protected $cache;

    /**
     * @var string
     */
    protected $tag;

    /**
     * @var integer
     */
    protected $minutes;

    /**
     * Construct
     *
     * @param Illuminate\Cache\CacheManager $cache
     * @param string $tag
     * @param integer $minutes
     */
    public function __construct(CacheManager $cache, $tag, $minutes = 60)
    {
        $this->cache = $cache;
        $this->tag = $tag;
        $this->minutes = $minutes;
    }

    /**
     * Get
     *
     * @param string $key
     * @return mixed
     */
    public function get($key)
    {
        return $this->cache->tags($this->tag)->get($key);
    }

    /**
     * Put
     *
     * @param string $key
     * @param mixed $value
     * @param integer $minutes
     * @return mixed
     */
    public function put($key, $value, $minutes = null)
    {
        if (is_null($minutes)) {
            $minutes = $this->minutes;
        }

        return $this->cache->tags($this->tag)->put($key, $value, $minutes);
    }

    /**
     * Has
     *
     * @param string $key
     * @return bool
     */
    public function has($key)
    {
        return $this->cache->tags($this->tag)->has($key);
    }
}

In the construct method I will inject an instance of Laravel’s CacheManager. This will make testing possible because we can just mock the CacheManager.

Next we can set a tag for this specific cache section. If you are unfamiliar with how tags work using the Laravel Cache API, take a look at Working with Cache in Laravel 4.

Finally I will set a default number of minutes to be used by the cache. However this can be overwritten if required.

Next we need to implement the get, put and has methods to fulfil the CacheInterface contract.

Create the Abstract Decorator

Next we need to create an AbstractUserDecorator class for the UserRepository. This file will be used as a base for all of the decorators that we might possibly need for the UserRepository:

<?php namespace Cribbb\Repository\User;

use Cribbb\Repository\RepositoryInterface;

abstract class AbstractUserDecorator implements
    RepositoryInterface,
    UserRepository
{
    /**
     * @var UserRepository
     */
    protected $user;

    /**
     * Construct
     *
     * @param UserRepository $user
     */
    public function __construct(UserRepository $user)
    {
        $this->user = $user;
    }

    /**
     * All
     *
     * @return Illuminate\Database\Eloquent\Collection
     */
    public function all()
    {
        return $this->user->all();
    }

    /**
     * Find
     *
     * @param int $id
     * @return Illuminate\Database\Eloquent\Model
     */
    public function find($id)
    {
        return $this->user->find($id);
    }

    /**
     * Create
     *
     * @param array $data
     * @return boolean
     */
    public function create(array $data)
    {
        return $this->user->create($data);
    }

    /**
     * Update
     *
     * @param array $data
     * @return boolean
     */
    public function update(array $data)
    {
        return $this->user->update($data);
    }

    /**
     * Delete
     *
     * @param int $id
     * @return boolean
     */
    public function delete($id)
    {
        return $this->user->delete($id);
    }
}

In this file we inject an instance of UserRepository and then implement all of the methods that are defined on the interface.

As you can see from the code above, I’m basically just passing the methods through to the Repository. You can think of this like the bottom layer of the stack with each decorator as a layer on top.

Cache Decorator

Next I can create the CacheDecorator:

<?php namespace Cribbb\Repository\User;

use Cribbb\Service\Cache\CacheInterface;

class CacheDecorator extends AbstractUserDecorator
{
    /**
     * @var CacheInterface
     */
    protected $cache;

    /**
     * Construct
     *
     * @param UserRepository $user
     * @param CacheInterface $cache
     */
    public function __construct(UserRepository $user, CacheInterface $cache)
    {
        parent::__construct($user);
        $this->cache = $cache;
    }

    /**
     * All
     *
     * @return Illuminate\Database\Eloquent\Collection
     */
    public function all()
    {
        $key = md5("all");

        if ($this->cache->has($key)) {
            return $this->cache->get($key);
        }

        $users = $this->user->all();

        $this->cache->put($key, $users);

        return $users;
    }

    /**
     * Find
     *
     * @param int $id
     * @return Illuminate\Database\Eloquent\Model
     */
    public function find($id)
    {
        $key = md5("id." . $id);

        if ($this->cache->has($key)) {
            return $this->cache->get($key);
        }

        $user = $this->user->find($id);

        $this->cache->put($key, $user);

        return $user;
    }
}

In this class I inject an instance of the UserRepository and pass it to the AbstractUserDecorator that we created above. I also inject an instance of the CacheInterface that we created at the beginning. This will be the LaravelCache class.

Next we can overwrite the methods that we want to use caching for by implementing them in this class.

For example, the find method:

/**
 * Find
 *
 * @param int $id
 * @return Illuminate\Database\Eloquent\Model
 */
public function find($id)
{
    $key = md5('id.'.$id);

    if ($this->cache->has($key))
    {
        return $this->cache->get($key);
    }

    $user = $this->user->find($id);

    $this->cache->put($key, $user);

    return $user;
}

First I create a unique key by using the md5 function to hash the $id.

Next we can check to see if the cache already has this key. If the key is already in the cache we can just return it.

If the key was not found we can fall back to the database to retrieve the user. After we’ve found the user we can put it into the cache so the next request can use the same copy.

Finally we can return the $user.

Add to the Service Provider

Now in the RepositoryServiceProvider we need to wrap the Repository with the CacheDecorator:

<?php namespace Cribbb\Repository;

use User;
use Cribbb\Service\Cache\LaravelCache;
use Illuminate\Support\ServiceProvider;
use Cribbb\Repository\User\CacheDecorator;
use Cribbb\Repository\User\EloquentUserRepository;
use Cribbb\Repository\Post\EloquentPostRepository;

class RepositoryServiceProvider extends ServiceProvider
{
    public function register()
    {
        /**
         * User Repository
         *
         * @return Cribbb\Repository\User\EloquentUserRepository
         */
        $this->app->bind("Cribbb\Repository\User\UserRepository", function (
            $app
        ) {
            $user = new EloquentUserRepository(
                new User(),
                $app->make("Cribbb\Repository\Post\PostRepository")
            );

            return new CacheDecorator(
                $user,
                new LaravelCache($app["cache"], "user")
            );
        });
    }
}

First we need to create an instance of the UserRepository and save it as a variable.

Next we can create a new instance of the CacheDecorator and pass the $user into it.

Notice how I’m creating the instance of the LaravelCache service here, passing in the instance of the CacheManager and setting the tag to user.

This might seem a bit like inception, but believe me, once you get your head around what is injected into what, it makes a lot of sense. All of the set up of this object happens in the Service Provider so you don’t have to worry about it anywhere else in your code because it will all magically work for you when you need to call on the Repository. By injecting the objects as dependencies we also make it much easier to test.

By setting the tag in the Service Provider we can invalidate this whole section of the cache by changing this tag. You might want to do that if you push a big change and you want to completely flush this section of the cache. In this case it probably make sense to set the tag based on a configuration option, rather than hardcoding it into the Service Provider.

Tests

A common question with these kind of things is, What do I need to write tests for?

It might seem like we’ve done a lot here, but in all honesty there isn’t much to test. Our Cache Service is basically just a wrapper around the Laravel Cache API, and the Decorator is just a wrapper around the Repository (which in itself is just a wrapper around Eloquent).

Whenever you are simply wrapping existing components, you don’t need to test the return values. Those components already have their own tests so you don’t need to waste time to write your own tests.

So the only tests we need to write are for the getters and setters in our own classes. For example you could write unit tests to ensure that only the correct instance of a particular class can be injected, or a method should only accept a particular type of argument and it should return an instance of a particular object.

Don’t worry about going nuts and testing every possible character of code. Unit test the main stuff and write integration tests for the rest. You don’t want to spend more time writing and maintaining tests than you do actually shipping your product.

Conclusion

I love how easy Laravel makes implementing Cache in an application. By abstracting the actually caching mechanism and providing a clean and clear API to use in your code, Laravel has made using cache a total no-brainer.

However, actually implementing caching in your application is not as clear. Whilst I really like that Laravel does not force any conventions on you, sometimes it is beneficial to be told the best way to structure a certain component of your application so you don’t run into trouble at some point in the future.

As I mentioned at the top of this post, this tutorial was heavily inspired by Chris Fidao from his book, Implementing Laravel. I really love the use of the Decorator pattern to wrap the Repository in a layer of cache. This means the rest of your application does not need to know if the data is cached or not which seems like a pretty nice solution to me.

This is a series of posts on building an entire Open Source application called Cribbb. All of the tutorials will be free to web, and all of the code is available on GitHub.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.