Let's create a DataCache layer for your Laravel apps

Search for a command to run...

No comments yet. Be the first to comment.
In this series/category, I would share with you guys all of my exp and best practices, to build the awesome products
Hey guys, Authentication topic today 😎 We all know that Laravel ships the default Email & Password authentication for us. The good'ol traditional way. However, it's 2024 now. By registering using Email & Password, then users have to verify their ema...
Hey guys, An update after I posted the “Laravel Excel: How to append rows to an existing Excel file“ a while back. This post will give you another solution and probably a better way to export that fits every case. Problems Previously, when trying to ...

Hey guys, An easy topic for today! Make something a default (e.g., category, product, campaign, etc). Let’s go with the categories table for the examples. Let’s say we only want one category to be the default one. And find out which is the optimal wa...
Hey guys, Probably a chapter 2 of this https://sethphat.dev/why-is-wordpress-still-an-awesome-cms blog post, haha. Here was the story: I’ve been spending like 2 days figuring out which CMS I want to use for my new blog (for my new hobby lol) Let’s se...

Hey guys, Well damn, it has been a while since my last post lol, lazy & busy. But here I am, back to write useful tips for y’all 🥹 Let us do some exporting tasks for today, using Laravel Excel to export XLSX (yeah CSV is so 2010 lol). But, append mo...

Hey guys, Recently, I just bought Herd PRO (a yearly subscription). I’ve been using the basic Herd since the first release. So, my latest work required interaction with S3 storage, there are several ways to achieve that in the local env: Use Cloud (...

Hey guys,
We all know that Cache facade plays a good role across the Laravel application lifecycle, isn't it?
Cache simply boosts up the requests by retaining the computed data from file storage or memory (aka RAM) depending on our configuration.
Today I'm gonna show you an awesome way to maintain your cache flow.
Not only for other facades but also Cache provide us with a really simple way to use things (the beauty of Laravel - simple yet elegant)
However, it's a double-edged sword. We code things and we see it's simple, no biggie.
But by the nature of software development, from time to time, the codebase increases, and maintaining becomes harder.
Then people start to blame Laravel. It's not Laravel's fault, right? Laravel doesn't do anything, Laravel allows us to use any design patterns, and any approaches out there.
So let's get back to the main topic. We usually use cache like this:
// for example, Country is an entity that we don't change much
// CountryController.php
public function index(): JsonResponse
{
$countries = Cache::rememberForever(
'countries-list',
fn () => Country::query()->pluck('name', 'code'),
);
return new JsonResponse(compact('countries'));
}
public function store(CountryStoreRequest $request): JsonResponse
{
$country = Country::create($request->validated());
Cache::forget('countries-list');
return new JsonResponse();
}
public function update(CountryUpdateRequest $request, Country $country): JsonResponse
{
$country->update($request->validated());
Cache::forget('countries-list');
return new JsonResponse();
}
public function destroy(CountryDestroyRequest $request, Country $country)): JsonResponse
{
$country->delete();
Cache::forget('countries-list');
return new JsonResponse();
}
The problems:
Hardcoded cache key across the codebase, which can easily leak to human error when developing features.
The literal 'countries-list' could be anything in the codebase, imagine a big codebase and you want to search for the cache stuff only, it's a nightmare.
And that's just for a fixed cache key, imagine you want to cache for specific users, or businesses, etc. The complex just becomes bigger.
From the statement above:
Laravel allows us to use any design patterns, and any approaches out there.
Yes, don't limit our implementation, let's add more spicy stuff into the codebase. Let's go above and beyond ✈️
I'll create an AbstractDataCache with some simple implementations:
<?php
namespace App\DataCache;
use Carbon\CarbonImmutable;
use Illuminate\Support\Facades\Cache;
abstract class AbstractDataCache
{
/**
* Concrete class must implement and return the cache key
*/
abstract public function getCacheKey(): string;
/**
* Concrete class must know how to compute and return the data that need to be cached
*/
abstract public function computeCache(): mixed;
/**
* By default, data will be cached for an hour
*
* @return CarbonImmutable
*/
public function getCacheExpiration(): CarbonImmutable
{
return CarbonImmutable::now()->addHour();
}
public function get(): mixed
{
return Cache::remember(
$this->getCacheKey(),
$this->getCacheExpiration(),
fn () => $this->computeCache()
);
}
/**
* Clear the cache data
*/
public function clear(): void
{
Cache::forget($this->getCacheKey());
}
/**
* Clear and re-cache the data
*/
public function rebuild(): void
{
$this->clear();
$this->get();
}
}
Then I'll create CountriesListDataCache and extends and implements the required methods:
class CountriesListDataCache extends AbstractDataCache
{
protected function getCacheKey(): string
{
return 'countries-list';
}
protected function computeCache(): array
{
return Country::query()->pluck('name', 'id');
}
protected function getCacheExpiration(): CarbonImmutable
{
return CarbonImmutable::now()->addYear();
}
}
Taking into action:
public function index(CountriesListDataCache $dataCache): JsonResponse
{
$countries = $dataCache->get();
return new JsonResponse(compact('countries'));
}
public function store(
CountryStoreRequest $request,
CountriesListDataCache $dataCache
): JsonResponse {
$country = Country::create($request->validated());
$dataCache->rebuild(); // or clear
return new JsonResponse();
}
So with that, what do we achieve?
A new layer to manage the caches (key, time, values)
No more hardcoded string or string concat/manipulation
No more duct tape caching stuff
Maintaining the cache across the app is way better.
Can use the dependency injection just fine.
Testable and super easy-to-write test cases.
Q: What if I need some additional entities to compute the cache data?
constructor and then you can use them normally fine (same approaches as Queue)Q: How to use dependency injection?
A: use the helper app function to get your desired instance for your computation.
computeCache as same as the handle of Queue class, will do soonWell, that's that for the DataCache topic. I hope you guys enjoy it. I have been using that approach for some of my projects, so far so good.
If you have any other ideas or questions, comment below!
Cheers, and see you next time!