Steven Maguire
I've been building web software since 2004.
Browser
Static Content, HTML, JSON, XML, etc.
Static Content, HTML, JSON, XML
Why? Save trips upstream for content; save computation cycles.
Static Content, HTML, JSON, XML
Applications
(File system, APC, Memcached, Redis)
Serialized representations of data
Serialized representations of data
Serialized representations of data
Why? Save trips to database; save computation cycles.
Serialized representations of data
The sole topic of
this conversation.
Controllers depend on Models for data resolution.
Controllers depend on Service who depend on Models for data resolution.
Controllers depend on Service who depend on Models for data resolution when cache is unavailable or expired.
<?php namespace App\Http\Controllers;
use App\User;
class UserController
{
public function getUsers()
{
return User::all();
}
}
We need to list all users.
<?php namespace App\Http\Controllers;
use App\User;
use Illuminate\Support\Facades\Cache;
class UserController
{
public function getUsers()
{
return Cache::remember('users', 10, function() {
return User::all();
});
}
}
We need to add caching.
laravel.com/docs/5.1/cache
<?php namespace App\Http\Controllers;
use App\User;
use Illuminate\Support\Facades\Cache;
class UserController
{
public function getUsers()
{
return Cache::remember('users', 10, function() {
return User::paginate();
});
}
}
We have too many users for "all."
<?php namespace App\Http\Controllers;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class UserController
{
public function getUsers(Request $request)
{
$page = $request->input('page', 1);
return Cache::remember('users.page('.$page.')', 10, function() {
return User::paginate();
});
}
}
"getUsers" always returns page 1.
<?php namespace App\Http\Controllers;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class UserController
{
public function getUsers(Request $request)
{
$take = $request->input('take', 15);
$page = $request->input('page', 1);
return Cache::remember(
'users.page('.$page.').take('.$take.')',
10,
function() use ($take) {
return User::paginate($take);
}
);
}
}
We need to support dynamic records per page.
<?php namespace App\Http\Controllers;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class UserController
{
public function getUsers(Request $request)
{
$take = $request->input('take', 15);
$page = $request->input('page', 1);
return Cache::remember('users.page('.$page.').take('.$take.')', 10, function() use ($take) {
return User::paginate($take);
});
}
public function addUser(Request $request)
{
$user = User::create($request->all());
Cache::flush();
return $user;
}
}
New users don't appear in "getUsers" for 10 minutes.
<?php namespace App\Http\Controllers;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class UserController
{
public function getUsers(Request $request)
{
$take = $request->input('take', 15);
$page = $request->input('page', 1);
return Cache::remember('users.page('.$page.').take('.$take.')', 10, function() use ($take) {
return User::paginate($take);
});
}
public function addUser(Request $request)
{
$user = User::create($request->all());
// Something that will only clear cache
// keys that start with "users"?
return $user;
}
}
We can't clear all application cache when a user is added.
<?php namespace App\Services;
use App\Contracts\UserServiceable;
use App\User;
class UserService implements UserServiceable
{
public function getPaginatedUsers($perPage = 15, $page = 1)
{
return User::paginate($perPage);
}
public function createUser($attributes = [])
{
return User::create($attributes);
}
}
Create a service class
<?php namespace App\Contracts;
interface UserServiceable
{
public function getPaginatedUsers($perPage = 15, $page = 1);
public function createUser($attributes = []);
}
<?php namespace App\Http\Controllers;
use App\Contracts\UserServiceable;
use Illuminate\Http\Request;
class UserController
{
public function __construct(UserServiceable $user)
{
$this->user = $user;
}
public function getUsers(Request $request)
{
//
}
public function addUser(Request $request)
{
//
}
}
Inject service object into controller
<?php namespace App\Http\Controllers;
use App\Contracts\UserServiceable;
use Illuminate\Http\Request;
class UserController
{
public function __construct(UserServiceable $user)
{
$this->user = $user;
}
public function getUsers(Request $request)
{
$take = $request->input('take', 15);
$page = $request->input('page', 1);
return $this->user->getPaginatedUsers($take, $page);
}
public function addUser(Request $request)
{
return $this->user->createUser($request->all());
}
}
Update controller methods
<?php namespace App\Services;
use App\Contracts\UserServiceable;
use App\User;
use Illuminate\Support\Facades\Cache;
class UserService implements UserServiceable
{
public function getPaginatedUsers($perPage = 15, $page = 1)
{
return Cache::remember('users.page('.$page.').take('.$perPage.')', 10, function() use ($perPage) {
return User::paginate($perPage);
});
}
public function createUser($attributes = [])
{
$user = User::create($attributes);
Cache::flush();
return $user;
}
}
Add DIY caching to service class
$ composer require stevenmaguire/laravel-cache
Install with Composer
github.com/stevenmaguire/laravel-cache
<?php namespace App\Services;
use App\Contracts\UserServiceable;
use App\User;
use Illuminate\Support\Facades\Cache;
use Stevenmaguire\Laravel\Services\EloquentCacheTrait;
class UserService implements UserServiceable
{
use EloquentCacheTrait;
// Service methods
}
Include the base service
<?php namespace App\Services;
use App\Contracts\UserServiceable;
use App\User;
use Illuminate\Support\Facades\Cache;
use Stevenmaguire\Laravel\Services\EloquentCache;
class UserService extends EloquentCache implements UserServiceable
{
// Service methods
}
as a trait
as a parent class
Build queries using Eloquent and request cache object
<?php namespace App\Services;
use App\Contracts\UserServiceable;
use App\User;
use Illuminate\Support\Facades\Cache;
use Stevenmaguire\Laravel\Services\EloquentCache;
class UserService extends EloquentCache implements UserServiceable
{
public function __construct(User $user)
{
$this->user = $user;
}
public function getPaginatedUsers($perPage = 15, $page = 1)
{
$query = $this->user->query();
$key = 'users.page('.$page.').take('.$perPage.')';
$verb = 'paginate:'.$perPage;
return $this->cache($key, $query, $verb);
}
public function createUser($attributes = [])
{
$user = User::create($attributes);
$this->flushCache();
return $user;
}
}
Takes three parameters:
If the method associated with the optional verb takes parameters, like paginate, the parameters can be expressed as a comma separated list following the verb and a colon. If a parameter expects an array of literal values, these may be expressed as a pipe delimited sting.
public function getPaginatedUsers($perPage = 15, $page = 1)
{
$query = $this->user->query();
$key = 'users.page('.$page.').take('.$perPage.')';
$verb = 'paginate:'.$perPage;
return $this->cache($key, $query, $verb);
// $query->paginate(15);
}
Configure cache policy
<?php namespace App\Services;
use App\Contracts\UserServiceable;
use App\User;
use Illuminate\Support\Facades\Cache;
use Stevenmaguire\Laravel\Services\EloquentCache;
class UserService extends EloquentCache implements UserServiceable
{
// Cache duration in minutes, default 15
protected $cacheForMinutes = 15;
// Enable caching, default true
protected $enableCaching = false;
// Enable logging, default true
protected $enableLogging = false;
// Service methods
}
Flush all cache for service
<?php namespace App\Services;
use App\Contracts\UserServiceable;
use App\User;
use Illuminate\Support\Facades\Cache;
use Stevenmaguire\Laravel\Services\EloquentCache;
class UserService extends EloquentCache implements UserServiceable
{
// Service methods
public function flushServiceCache()
{
$this->flushCache();
}
}
Flush cache with regex pattern matching
<?php namespace App\Services;
use App\Contracts\UserServiceable;
use App\User;
use Illuminate\Support\Facades\Cache;
use Stevenmaguire\Laravel\Services\EloquentCache;
class UserService extends EloquentCache implements UserServiceable
{
public function getPaginatedUsers($perPage = 15, $page = 1)
{
$query = $this->user->query();
$key = 'users.page('.$page.').take('.$perPage.')';
$verb = 'paginate:'.$perPage;
return $this->cache($key, $query, $verb);
}
public function flushUserCache()
{
$this->flushCache('^users\.');
}
}
github.com/stevenmaguire/bydreco-service
(bit.ly/laravel-caching-layer-demo)
@stevenmaguire
on twitter
stevenmaguire@gmail.com
on electronic mail
stevenmaguire
on github
By Steven Maguire
Deck originally created for a presentation to a gathering of the Chicago Laravel Meetup group - bit.ly/laravel-caching-layer