This tutorial is out of date and no longer maintained.
Caching is one aspect of web development I am guilty of overlooking a lot and I am sure a lot us are guilty of that too. I have come to realize how important it is and I will explain the importance with Scotch as a case study.
From experience and a little observation, Scotch schedules articles daily. Therefore, articles are not (for now) released within 24 hours of the last post. It follows that data on the landing page will remain the same for 24 hours. The main point here is, it is pointless to ask the database for articles within that 24 (or to be safe 22 - 23) hour range.
Cache to the rescue! In Laravel, we can cache the results for 22 hours and when a request is made, the controller responds with a cached value until the cache time expires.
We’re going to look at the basic usage of the Laravel cache and then get into a quick demo app to see just how much faster caching can make our applications.
Laravel makes it easy for us to switch out how we want caching to be generated. It is extremely easy for us to switch out the drivers. Just check out config/cache.php
to see the available drivers which are:
You can then set the following line in your .env
file to change the cache driver:
CACHE_DRIVER=file
You may go ahead and try these examples out without bothering about configuration, as it defaults to
file
.
The Cache
facade exposes a lot of static methods to create, update, get, delete, and check for existence of a cache content. Let us explore some of these methods before we build a demo app.
We can add or update cache values with the put()
method. The method accepts 3 necessary arguments:
key
value
expiration time
in minutesFor example:
Cache::put('key', 'value', 10);
The key
is a unique identifier for the cache and will be used to retrieve it when needed.
Furthermore, we can use the remember()
method to automate retrieving and updating a cache. The method first checks for the key
and if it has been created, returns it. Otherwise, it will create a new key
with the value returned from it’s closure like so:
Cache::remember('articles', 15, function() {
return Article::all();
});
The value 15
is the number of minutes it will be cached. This way, we don’t even have to do a check if a cache has expired. Laravel will do that for us and retrieve or regenerate the cache without us having to explicitly tell it to.
The values of a cache can be retrieved using the get()
method which just needs a key
passed as argument to know which cache value to grab:
Cache::get('key');
Sometimes it is very important to check if a cache key exists before we can retrieve or update it. The has()
method becomes handy:
if (Cache::has('key')){
Cache::get('key');
} else {
Cache::put('key', $values, 10);
}
Cache values can be removed with the forget()
method and passing the key
to it:
Cache::forget('key');
We can also retrieve a cache value and delete it immediately. I like referring to this one as one-time caching:
$articles = Cache::pull('key');
We can also clear the cache even before they expire from the console using:
- php artisan cache:clear
This is going to be a really simple demo based on my research on the time taken to process requests with or without caches. To get straight to the point, I suggest you set up a Laravel instance on your own and follow along with the tutorial.
Create a model named Article
using the command below:
- php artisan make:model Article -m
The -m
option will automatically create a migration for us so we need not run a “create migration” command. This single command will create an App/Article.php
and a database/migrations/xxxx_xx_xx_xxxxxx_create_articles_table.php
file.
Update your new migration file to add two table fields:
public function up() {
Schema::create('articles', function (Blueprint $table) {
$table->increments('id');
// add the following
$table->string("title");
$table->string("content");
$table->timestamps();
});
}
Now we can migrate our database using the artisan
command:
- php artisan migrate
Next up is to seed the articles table. In database/seeds/DatabaseSeeder.php
, update the run()
command with:
public function run() {
Model::unguard();
// use the faker library to mock some data
$faker = Faker::create();
// create 30 articles
foreach(range(1, 30) as $index) {
Article::create([
'title' => $faker->sentence(5),
'content' => $faker->paragraph(6)
]);
}
Model::reguard();
}
The Faker
library is included in Laravel to help with quickly generating fake data. We are using the PHP’s range()
method to generate 30 fake columns.
Now we can seed our database with the artisan
command:
- php artisan db:seed
Next up, we can create a controller that will process the requests and caching. It will be empty for now:
- php artisan make:controller ArticlesController
…then we add a route in the app/Http/routes.php
which will point to the controller’s index method:
Route::group(['prefix' => 'api'], function() {
Route::get('articles', 'ArticlesController@index');
});
Now that our database is all set up with sample data, we can finally get to testing.
Let us see what our conventional controller action methods look like without caching and how long it takes to process the response. In the index()
method, return a resource of articles:
public function index() {
$articles = Articles::all();
return response()->json($articles);
}
You can now run the app and access the URL (http://localhost/api/articles
) from Postman or in browser as seen below.
Take note of the time taken to complete this request on a local development server.
Let us now try to use caching and see if there will be any significant difference in the time taken to respond with data. Change the index()
method to:
public function index() {
$articles = Cache::remember('articles', 22*60, function() {
return Article::all();
});
return response()->json($articles);
}
Now we are caching the articles using the remember()
method we discussed for 22 hours. Run again and observe the time taken. See my screenshot:
From my standard development PC, the time taken to produce a response when using cache is less compared to when not as seen in the table:
Server Hits | Time |
---|---|
1st | 4478ms |
2nd | 4232ms |
3rd | 2832ms |
4th | 3428ms |
Avg | 3742ms |
Server Hits | Time |
---|---|
1st | 4255ms |
2nd | 3182ms |
3rd | 2802ms |
4th | 3626ms |
Avg | 3466ms |
Server Hits | Time |
---|---|
1st | 3626ms |
2nd | 566ms |
3rd | 1462ms |
4th | 1978ms |
Avg | 1908ms :) |
It is required to install predis/predis via composer
Server Hits | Time |
---|---|
1st | 3549ms |
2nd | 1612ms |
3rd | 920ms |
4th | 575ms |
Avg | 1664ms :) |
Awesome enough right? Two things to note:
The speed difference might not be so obvious with a file/database driver, but if we use external providers, we will see a lot better performance. It pays to invest in caching.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!