Attach arbitrary cached values to Eloquent model instances or classes using any Laravel cache driver. By default each authenticated user gets an isolated cache namespace, so two users never share the same cached value for the same model.
Install the package via Composer:
composer require foxws/laravel-modelcacheOptionally publish the config file:
php artisan vendor:publish --tag="modelcache-config"| Variable | Default | Description |
|---|---|---|
MODEL_CACHE_ENABLED |
true |
Toggle caching on/off globally |
MODEL_CACHE_STORE |
CACHE_STORE |
Cache store to use (any store from config/cache.php) |
MODEL_CACHE_LIFETIME |
604800 (1 week) |
Default TTL in seconds |
use Foxws\ModelCache\Concerns\InteractsWithModelCache;
use Illuminate\Database\Eloquent\Model;
class Video extends Model
{
use InteractsWithModelCache;
}That is all the setup required. Every public method below becomes available on the model.
These methods are scoped to a specific model record (e.g. Video with id = 5).
Store a value:
$video = Video::findOrFail(5);
$video->modelCache('playback_position', 142);
// With a custom TTL
$video->modelCache('random_seed', 0.73, now()->addHours(6));
$video->modelCache('last_viewed', now(), 3600); // TTL as secondsRetrieve a value:
$position = $video->modelCached('playback_position'); // null if not cached
$seed = $video->modelCached('random_seed', 0.5); // 0.5 as fallbackCheck existence:
if (! $video->modelCacheHas('playback_position')) {
$video->modelCache('playback_position', 0);
}Forget a value:
$video->modelCacheForget('playback_position');Practical example — lazy-load an expensive computed value:
class VideoController extends Controller
{
public function show(Video $video): JsonResponse
{
if (! $video->modelCacheHas('stats')) {
$stats = $this->computeExpensiveStats($video);
$video->modelCache('stats', $stats, now()->addDay());
}
return response()->json($video->modelCached('stats'));
}
}These static methods are scoped to the model class rather than a specific record. Useful for values shared across all instances, such as global seeds or configuration.
Store a value:
Video::setModelCache('random_seed', 0.42);
Video::setModelCache('random_seed', 0.42, now()->addWeek());Retrieve a value:
$seed = Video::getModelCache('random_seed'); // null if not cached
$seed = Video::getModelCache('random_seed', 0.5); // with fallbackCheck existence:
if (! Video::hasModelCache('random_seed')) {
Video::setModelCache('random_seed', rand() / getrandmax());
}Forget a value:
Video::forgetModelCache('random_seed');Use the ModelCache facade when you need to interact with caching outside of a model — for example in an action class or a service.
use Foxws\ModelCache\Facades\ModelCache;
class RecordPlaybackPosition
{
public function handle(Video $video, int $seconds): void
{
if (! ModelCache::enabled()) {
return;
}
if (! ModelCache::shouldCache($video, 'playback_position', $seconds)) {
return;
}
ModelCache::cache($video, 'playback_position', $seconds, now()->addDay());
}
}// Read from cache
$position = ModelCache::getCachedValue($video, 'playback_position');
// Check
$exists = ModelCache::hasBeenCached($video, 'playback_position');
// Forget one or multiple keys
ModelCache::forget($video, 'playback_position');
ModelCache::forget($video, ['playback_position', 'random_seed']);Override shouldModelCache on the model to conditionally skip caching certain keys or values:
class Video extends Model
{
use InteractsWithModelCache;
public function shouldModelCache(string $key, mixed $value = null): bool
{
// Never cache a null value
if ($value === null) {
return false;
}
// Only cache specific keys
if (! in_array($key, ['playback_position', 'random_seed', 'stats'])) {
return false;
}
return true;
}
}A cache profile controls global caching behaviour: whether caching is enabled, when values expire, and which per-user namespace suffix to use. The default is CacheAllSuccessful, which caches all values for all users.
Create your own by implementing CacheProfile:
use Foxws\ModelCache\CacheProfiles\BaseCacheProfile;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
class AuthenticatedUserCacheProfile extends BaseCacheProfile
{
public function shouldUseCache(Model $model, string $key): bool
{
// Only cache for authenticated users
return Auth::check();
}
public function shouldCacheValue(mixed $value = null): bool
{
// Do not cache null or empty strings
return $value !== null && $value !== '';
}
}Register it in config/modelcache.php:
'cache_profile' => AuthenticatedUserCacheProfile::class,By default, BaseCacheProfile::useCacheNameSuffix returns the authenticated user's ID, isolating each user's cache. You can override this per model:
class Video extends Model
{
use InteractsWithModelCache;
protected function cacheNameSuffix(string $key): string
{
// Shared cache regardless of who is logged in
return '';
}
}class Post extends Model
{
use InteractsWithModelCache;
protected function cacheNameSuffix(string $key): string
{
// Separate cache per user
return Auth::check() ? (string) Auth::id() : '';
}
}class Report extends Model
{
use InteractsWithModelCache;
protected function cacheNameSuffix(string $key): string
{
// Separate cache per key type, shared across users
return $key;
}
}composer testPlease see CHANGELOG for more information on what has changed recently.
Please see CONTRIBUTING for details.
Please review our security policy on how to report security vulnerabilities.
This package is heavily inspired by and based on the spatie/laravel-responsecache package by Spatie.
If you find their work valuable, please consider sponsoring Spatie or purchasing one of their courses and products.
The MIT License (MIT). Please see License File for more information.