Content Index
- Cache::get()
- Cache::has()
- Cache::put()
- Cache::put(key, data, duration)
- Cache::putMany()
- Cache::add()
- Cache::pull()
- Cache::many()
- Cache::remember()
- Increasing or decreasing cached values
- Cache::forget()
- Cache::flush()
- Case study
- Controller types
- File
- Database
- Memcached
- Redis
- Array
- Laravel: Error Class "Redis" Not Found Error
- Why does this error occur?
- Missing composer package
- Redis installation missing
- Cache and Conditionals in Blade, the WORST Laravel Combination
- Context
- Eloquent Query Caching in Laravel
- What is Eloquent AutoCache and How Does It Work?
- Configuration and Implementation in the Model
- Granular Query Control
- Practical Demonstration: Cache Behavior and Invalidation
- 1. Cache Persistence Against External Changes
- 2. Automatic Invalidation via Laravel Operations
- Conclusion
We already know how to download files in Laravel; another fundamental aspect is the cache.
In any system, it is essential to manage the management of resources to improve the overall performance of the system; one of the most important points in a system is about the types of requests that are repeated consecutively; A good example of this is a rest resource from a rest api; For example, a resource that always returns the same type of list is a good candidate to improve its performance; to analyze this point, suppose we have a resource that returns 10,000 records or more:
app\Http\Controllers\Api\PostController.php
class PostController extends Controller
{
public function all(): JsonResponse
{
return response()->json(Post::get());
}
}We can improve its performance by storing this data in cache; In Laravel, cache management is a crucial functionality to improve the performance of our web applications. Laravel offers support for multiple caching systems, including in-memory, disk, database, and even systems like Redis. This allows us to temporarily store data that is frequently consumed to speed up the response time of our applications and with this, consume less resources.
In short, caching data reduces the number of times a repeated request needs to be processed and optimizes application response time, since cached data can be quickly retrieved without having to run the process that handles it. generate said data; traditionally, it would be the access to the database, but it can be anything else like HTML.
Basic use of the cache
With the cache in Laravel, we have different methods and options that we can use to adapt it to different schemes; let's see the main ones:
- get()
- has()
- put()
- putMany()
- add()
- pull()
- many()
- remember()
- rememberForever()
- increment()
- decrement()
- forever()
- forget()
- flush()
If you only need to clear a group of caches (for example, all posts), consider using `Cache Tags` (`Cache::tags(['posts', 'blog'])->remember(...)`). This is much cleaner than using `Cache::forget()` one by one.
All of these methods are available through the cache Facade; all these cache methods have in common that they receive a key (with the exception of the putMany() method), in order to access the cache; for example, if you want to save the detail of a post, this key can be post or post_detail for example, if you want a list of posts, it can be posts or posts_index; and using this key, is that we can make a reference to the cache.
Cache::get()
The get() method is used to retrieve items from the cache. If the item does not exist in the cache, a null value will be returned:
$value = Cache::get('key');Optionally, a second argument can be passed that specifies the default value to be used if the element does not exist:
$value = Cache::get('key', 'default');Cache::has()
The has() method is used to determine if an item exists in the cache; returns false if the value does not exist and true otherwise:
Cache::has('key');Cache::put()
The put() method accepts three parameters:
- key/key.
- Cache duration.
- The data to be cached.
Cache::put(key, data, duration)
For example:
Cache::put('key', 'value', $seconds = 10);
// Cache::put('key', 'value', now()->addMinutes(10));Cache::putMany()
The putMany() method stores an array of data in the cache, otherwise it has the same parameters as the put() function except for the key:
Cache::putMany(Post::all(), now()->addMinutes(10));Cache::add()
The add() method only adds the item to the cache if it doesn't already exist in the cache store. The method will return true if the item is actually added to the cache, otherwise it returns false:
Cache::add('key', 'value', now()->addMinutes(10));Cache::pull()
The pull() method can be used if you need to retrieve an item from the cache and then remove it; like the get() method, it will return null if the item doesn't exist in the cache:
$value = Cache::pull('key');Cache::many()
The many() method is used to retrieve multiple data based on an array of keys:
const $keys = [
'post_1',
'post_2',
'post_3',
***
];
cache::many(keys);Cache::remember()
- Many times you may want to retrieve an item from the cache, but also store a default value if the requested item does not exist. and for this we can use the remember() method that accepts three parameters:
- The key.
cache duration - The data to retrieve if the same is not found.
Cache::remember('posts', now()->addMinutes(10), function(){
return Post::all();
});In the above script, the function returns all posts (Post::all()) if the cached data does not exist.
cache::rememberForever()You can use the rememberForever() method to retrieve an item from the cache or store it forever (there is no parameter to pass the cache lifetime) if it doesn't exist:
$value = Cache::rememberForever('posts', function () {
return Post::all();
});Increasing or decreasing cached values
You can change the values
is a cached integer value using the increment and decrement methods, respectively:
Cache::increment('key'); //$value = 1
Cache::increment('key', $value);
Cache::decrement('key'); //$value = 1
Cache::decrement('key', $value);
Cache::forever()The forever() method stores the data in the cache forever without specifying any duration:
Cache::forever('key', 'value');Cache::forget()
The forget() method removes an item from the cache with a specified key parameter:
Cache::forget('key');Cache::flush()
This method clears all items from the cache:
Cache::flush();Case study
There are many approaches that we can use when using the cache in Laravel; one of the classics is, when making a query we do the following steps:
- We check if they already exist stored in the cache.
- If they exist, the cached data is returned.
- If they don't exist, the database is queried and with the response, we use it both to set in the cache and as part of the response.
With this in mind, we can change the all() function seen earlier in our Rest API to look like:
app\Http\Controllers\Api\PostController.php
class PostController extends Controller
{
public function all(): JsonResponse
{
if (Cache::has('posts_index2')) {
return response()->json(Cache::get('posts_index2'));
} else {
$posts = Post::get();
Cache::put('posts_index2', $posts);
return response()->json($posts);
}
}
}Another important factor is that, with the cache system that we defined for the details of the posts, we are not avoiding the connection to the database which is implicit in the route:
use Illuminate\Support\Facades\Cache;
***
class BlogController extends Controller
{
public function show(Post $post): String // Post::find(<PK>)
***To avoid this, you can change the route type and do the search manually only when the cache does not exist:
use Illuminate\Support\Facades\Cache;
***
class BlogController extends Controller
{
public function show(int $id /*slug*/): String
{
if (Cache::has('post_show_' . $id)) {
return Cache::get('post_show_' . $id);
} else {
$post = Post::find($id);
$cacheView = view('blog.show', compact('post'))->render();
Cache::put('post_show_' . $post->id, $cacheView);
return $cacheView;
}
// return view('blog.show', compact('post'));
}
}Finally, we can also use the cache() helper function instead of Facade for all the previous methods, for example:
cache()->has('post_show_'.$post->id);Controller types
| Driver | Speed | Configuration | When to use it? |
|---|---|---|---|
| File | Medium | None (Default) | Development and small, low-traffic apps. |
| Redis | Extremely High | External Server | High-traffic production applications. |
| Memcached | High | External Server | Alternative to Redis in specific environments. |
| Database | Slow | Table in the database | Only if you cannot install other services. |
| Array | Snapshot | PHP memory | Testing or fast execution of a single request. |
In Laravel, there are different types of controllers available, among them are:
File
The file type driver saves the cache to files on the file system; this is the default scheme used in Laravel and the generated encrypted file is located in storage/framework/; It does not require additional configurations and we can use it both in development and in production.
CACHE_STORE=fileDatabase
The database driver stores the cache in a database table; this type requires additional configurations, to generate the table:
$ php artisan cache:tableFor most of the cases, this scheme is not ideal since, with the use of the cache, we usually want to free up database resources and using the driver for the database cache, we could generate a neck of bottle.
CACHE_STORE=databaseMemcached
Memcached type driver stores the cache on a Memcached cache server; Memcached is a high-performance memory-based data store; therefore, in order to use it, you need to install an additional package.
CACHE_STORE=memcachedRedis
The Redis driver stores the cache in a database engine called Redis; Redis is one of the most popular configurations in Laravel as it is very fast; although, it requires installation and configuration of a program external to the project in Laravel.
CACHE_STORE=redisArray
The Array controller stores data in an array in PHP and does not require installation of additional programs.
CACHE_STORE=arrayThese settings are found in the file:
config\cache.php
'default' => env('CACHE_STORE', 'file'),
/*
|--------------------------------------------------------------------------
| Cache Stores
|--------------------------------------------------------------------------
|
| Here you may define all of the cache "stores" for your application as
| well as their drivers. You may even define multiple stores for the
| same cache driver to group types of items stored in your caches.
|
| Supported drivers: "apc", "array", "database", "file",
| "memcached", "redis", "dynamodb", "octane", "null"
|
*/
***And at the environment variables:
.env
CACHE_STORE=fileLaravel: Error Class "Redis" Not Found Error
When we want to use Redis in a new Laravel application, the following error may appear:
Class "Redis" Not Found ErrorWhy does this error occur?
The reason for this error is usually a missing Composer package or a missing Redis installation. In both cases, it appears once you try to use Redis on your system for the following:
CACHE_DRIVER=redisBased on this, one of the following situations may occur.
Missing composer package
This is the most common reason for having this error. To fix it, run the following command:
composer require predis/predisAnd once the package is installed, you should be able to use Redis.
Redis installation missing
If you have installed the package, but the error is still there, you may need to install Redis. To install Redis, follow the instructions on the official Redis website.
Cache and Conditionals in Blade, the WORST Laravel Combination
I wanted to tell you about something curious that happened to me when using cache together with conditionals in the view. When explained, it seems obvious, but it can still be a small challenge if you're not paying attention. This happened to me in my Desarrollo Libre application and I want to share it so the same thing doesn't happen to you.
Context
In my controller, I have a cache system that is only activated in production:
if (config('app')['env'] == 'production') {
// Flujo normal: busca en cache
$keyCache = 'post_show_' . $category_url_clean . '_' . $post_url_clean;
if (Cache::has($keyCache)) {
return Cache::get($keyCache);
}
}In the master template, I show or hide content based on the device:
@if (!isMobile()) content movile @endifThe Problem
When working with cache and conditionals based on the device, a problem can occur:
If a user enters from a mobile device and there is no cache, a cache is generated for mobile mode.
If another user enters from a PC, Laravel returns the existing cache, which corresponds to the mobile mode, generating a display error.
In other words, the cache is shared between devices, which is not correct when the content depends on the device type.
The Solution: One Cache Per Mode
The solution is simple: add the device type to the cache name:
$type = isMobile() ? 'm' : 'd';
if (config('app')['env'] == 'production') {
$keyCache = 'post_show_' . $category_url_clean . '_' . $post_url_clean . '_' . $type;
if (Cache::has($keyCache)) {
return Cache::get($keyCache);
}
}In this way, Laravel generates two separate caches for the same page:
- One for mobile (m)
- Another for PC (d)
This maintains the correct display according to the device and prevents a user from receiving content from another mode.
Eloquent Query Caching in Laravel
One of the most expensive operations in any web application is database communication. Constantly accessing disk storage to retrieve identical records slows down response times. To solve this performance issue, there is an extremely interesting package that allows you to automate the caching of our Eloquent queries in a completely transparent way.
What is Eloquent AutoCache and How Does It Work?
The Eloquent AutoCache package:
https://packagist.org/packages/wddyousuf/eloquent-autocache
Allows you to automatically cache the results of Eloquent queries. Instead of running the SQL query directly on the database with every request, the system retrieves the data from the system cache (such as Redis or Memcached).
The highlight of this package is that it does not require modifying your existing queries. Its operation is based on two fundamental pillars:
- Read Caching: The first time a query is executed, the data is retrieved from the database and saved in the cache. In subsequent requests, Laravel directly returns the cached result.
- Automatic Write Invalidation (Flush): The cache is automatically invalidated and cleared as soon as a write operation is detected from the framework (processes for creating, updating, saving, or deleting records). This ensures that the application never serves stale data after a legitimate modification.
Configuration and Implementation in the Model
Integrating the package is extremely simple. After performing the usual installation via Composer:
$ composer require wddyousuf/eloquent-autocacheAnd publishing the configuration file:
$ php artisan vendor:publish --tag=autocache-configWhere you can change the cache driver, time, among other parameters, the global cache expiration time is defined (configured by default to one hour, equivalent to 3600 seconds).
To enable automatic caching on a specific model, we only need to import and implement the respective trait:
use Wddyousuf\AutoCache\Traits\Cacheable;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use Cacheable; // Enables automatic caching for this model
}From this moment on, any query made through the Post model will be stored in the cache without altering the Eloquent syntax:
// This query is automatically cached
$posts = Post::all();Granular Query Control
Although global behavior is automated, the package offers specific methods to control caching at the individual query level if custom behavior is required:
withoutCache(): Allows you to bypass the cache entirely for a specific query that requires strict real-time data.cacheFor(seconds): Defines a custom and unique expiration time for that particular query, overriding the global value.
Practical Demonstration: Cache Behavior and Invalidation
To understand the impact of the cache and how it responds to external or internal changes, let's analyze the following scenario.
1. Cache Persistence Against External Changes
If we view the records through a database client and make a manual modification directly in the SQLite database table—for example, changing the title of a record to "Test"—we will observe the following:
- When querying the Laravel REST API endpoint, the response JSON will still display the old value.
- This behavior occurs because Laravel does not interact with the database to resolve this request; the framework retrieves the response directly from fast cache storage.
2. Automatic Invalidation via Laravel Operations
For the cache to update and reflect the changes, a write action must be executed using Eloquent tools or Laravel mechanisms (such as a Database Seeder to insert a new post, or a model update via a controller):
Post::create([
***
]);When this insertion is processed from Laravel, the package detects the write event, immediately flushes the cache for the Post model, and forces the application to perform a fresh query on the next HTTP request. When reloading the API, it will be confirmed that the response has been updated with the new record and with the modifications previously made externally.
Automated caching is one of the most efficient and easiest optimizations to implement in medium and large projects. By reducing the read load on the database engine, we free up valuable resources on the server and substantially improve our application's Time to First Byte (TTFB), resulting in much faster, more robust, and more professional software.
Conclusion
This small detail can cause errors that are difficult to detect if you are not aware of how the cache works. Whenever you work with conditionals that affect presentation, make sure to differentiate the cache based on the context, such as the device type in this case.
This happened to me in production and it took me a while to realize, but now the solution works perfectly.
Continuing with the optimizations of our application, the next step is to limit the number of user requests.