PHPackages                             ehmedp/laravel-redis-cache - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Caching](/categories/caching)
4. /
5. ehmedp/laravel-redis-cache

ActiveLibrary[Caching](/categories/caching)

ehmedp/laravel-redis-cache
==========================

A performant, scalable, and flexible Redis cache package for Laravel with tags, groups, attributes, closure caching, and artisan commands.

v1.0.0(2mo ago)01↓100%MITPHPPHP ^8.2

Since Mar 11Pushed 2mo agoCompare

[ Source](https://github.com/ehmedP/laravel-redis-cache)[ Packagist](https://packagist.org/packages/ehmedp/laravel-redis-cache)[ RSS](/packages/ehmedp-laravel-redis-cache/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (6)Versions (2)Used By (0)

Laravel Redis Cache
===================

[](#laravel-redis-cache)

A performant, scalable, and flexible Redis cache package for Laravel with **tags**, **groups**, **PHP 8 attributes**, **closure caching**, **Blade directives**, **automatic model invalidation**, and **Artisan commands**.

Features
--------

[](#features)

- 🚀 **High Performance** — SCAN-based key iteration (non-blocking), Redis pipelines for batch operations
- 🏷️ **Tag-based Caching** — Associate cache entries with tags, flush by tag
- 📁 **Group-based Caching** — Organize cache entries into logical groups
- 🎯 **PHP 8 Attributes** — `#[Cacheable]` on classes and methods, `#[CacheInvalidation]` for write operations
- 🔒 **Closure Caching** — Cache results of closures with automatic key generation
- 🧩 **Blade Directives** — `@cache` / `@endcache` for caching view fragments
- 🔄 **Auto Invalidation** — Automatic cache clearing on model create/update/delete
- 📊 **Dual Strategy** — Individual (per-key) or Bulk (per-tag) invalidation
- 🧹 **Artisan Commands** — Clear cache by key, tag, or flush everything
- 📡 **Events** — Hook into cache operations for monitoring and debugging
- 🔌 **Extensible** — Every component is interface-driven and replaceable
- 🧪 **Fully Testable** — 60+ test cases covering all functionality
- 📦 **Laravel Auto-Discovery** — Zero configuration required

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 10, 11, or 12
- Redis PHP extension (phpredis) or Predis

Installation
------------

[](#installation)

```
composer require ehmedp/laravel-redis-cache
```

Publish the configuration:

```
php artisan vendor:publish --tag=redis-cache-config
```

Configuration
-------------

[](#configuration)

```
// config/redis-cache.php
return [
    // Master switch — set to false to disable all caching
    'enabled' => env('REDIS_CACHE_ENABLED', true),

    // Redis connection from config/database.php
    'connection' => env('REDIS_CACHE_CONNECTION', 'default'),

    // Prefix for all cache keys (e.g., "rc:users:42")
    'prefix' => env('REDIS_CACHE_PREFIX', 'rc'),

    // Key segment separator
    'separator' => ':',

    // Default TTL in seconds (0 = no expiration)
    'default_ttl' => (int) env('REDIS_CACHE_TTL', 3600),

    // Tag and group prefixes
    'tag_prefix' => 'tag',
    'group_prefix' => 'group',

    // Blade @cache / @endcache directives
    'blade' => [
        'enabled' => true,
        'default_ttl' => null, // null = use global default_ttl
    ],

    // Cache events (CacheHit, CacheMissed, CacheWritten, etc.)
    'events' => [
        'enabled' => env('REDIS_CACHE_EVENTS_ENABLED', true),
    ],

    // Automatic cache invalidation on model changes
    'invalidation' => [
        'enabled' => env('REDIS_CACHE_INVALIDATION_ENABLED', true),
        'default_strategy' => 'individual', // or 'bulk'

        'models' => [
            // Per-record caching — only clears the specific user's cache:
            // \App\Models\User::class => [
            //     'strategy' => 'individual',
            //     'tags' => ['users'],
            // ],
            //
            // Collection caching — clears ALL subjects cache on any change:
            // \App\Models\Subject::class => [
            //     'strategy' => 'bulk',
            //     'tags' => ['subjects'],
            // ],
        ],
    ],
];
```

Usage
-----

[](#usage)

### Basic Operations

[](#basic-operations)

```
use EhmedP\RedisCache\Facades\RedisCache;

// Put & Get
RedisCache::put('user:1', $userData, 3600);
$user = RedisCache::get('user:1');
$user = RedisCache::get('user:1', 'default_value');

// Check existence
if (RedisCache::has('user:1')) { /* ... */ }

// Remove
RedisCache::forget('user:1');

// Remember (get or compute & cache)
$user = RedisCache::remember('user:1', 3600, function () {
    return User::find(1);
});

// Remember forever
$settings = RedisCache::rememberForever('app:settings', function () {
    return Settings::all();
});

// Batch operations
RedisCache::putMany(['key1' => 'val1', 'key2' => 'val2'], 3600);
$values = RedisCache::many(['key1', 'key2']);

// Increment / Decrement
RedisCache::increment('page:views');
RedisCache::decrement('stock:item:42', 5);
```

### Tag-based Caching

[](#tag-based-caching)

```
// Cache with tags
RedisCache::tags('users')->put('user:1', $user, 3600);
RedisCache::tags(['users', 'api'])->put('api:user:1', $data, 3600);

// Remember with tags
$user = RedisCache::tags('users')->remember('user:1', 3600, fn () => User::find(1));

// Flush all entries for a tag
RedisCache::flushTag('users');

// Flush multiple tags
RedisCache::flushTags(['users', 'api']);

// Inspect keys under a tag
$keys = RedisCache::getTaggedKeys('users');
```

### Group-based Caching

[](#group-based-caching)

```
$group = RedisCache::group('dashboard');
$group->put('stats', $dashboardStats, 3600);
$group->put('charts', $chartData, 3600);

$stats = RedisCache::group('dashboard')->remember('stats', 3600, fn () => calculateStats());

RedisCache::flushGroup('dashboard');
```

### Closure Caching

[](#closure-caching)

```
// Auto-generated key from source location
$result = RedisCache::rememberClosure(3600, function () {
    return heavyComputation();
});

// With arguments
$result = RedisCache::rememberClosure(3600, fn($id) => User::find($id), [42]);
```

### PHP 8 Attributes — Method-Level

[](#php-8-attributes--method-level)

```
use EhmedP\RedisCache\Attributes\Cacheable;
use EhmedP\RedisCache\Traits\HasCacheable;

class UserService
{
    use HasCacheable;

    #[Cacheable(ttl: 3600, tags: ['users'])]
    public function findById(int $id): ?User
    {
        return User::find($id);
    }
}

$service = new UserService();
$user = $service->cached('findById', 42); // cached!
$service->flushMethodCache('findById', 42); // clear specific cache
```

### PHP 8 Attributes — Class-Level (Repository/Service)

[](#php-8-attributes--class-level-repositoryservice)

Apply `#[Cacheable]` to a class and **ALL public methods** are automatically cached:

```
use EhmedP\RedisCache\Attributes\Cacheable;
use EhmedP\RedisCache\Invalidation\InvalidationStrategy;

#[Cacheable(ttl: 3600, tags: ['subjects'], invalidation: InvalidationStrategy::Bulk)]
class SubjectRepository
{
    use HasCacheable;

    public function all(): Collection       // ← cached (from class attribute)
    {
        return Subject::all();
    }

    public function findById(int $id): ?Subject  // ← cached (from class attribute)
    {
        return Subject::find($id);
    }

    #[Cacheable(ttl: 60, tags: ['subjects', 'active'])]  // ← overrides class-level
    public function active(): Collection
    {
        return Subject::where('active', true)->get();
    }
}
```

### Cache Invalidation on Write Operations

[](#cache-invalidation-on-write-operations)

```
use EhmedP\RedisCache\Attributes\CacheInvalidation;
use EhmedP\RedisCache\Invalidation\InvalidationStrategy;

class UserRepository
{
    use HasCacheable;

    // Individual: only clears cache for the specific user ID
    #[CacheInvalidation(
        tags: ['users'],
        strategy: InvalidationStrategy::Individual,
        entityKey: 'id'
    )]
    public function update(int $id, array $data): User
    {
        return User::find($id)->update($data);
    }

    // Bulk: clears ALL "users" tagged cache
    #[CacheInvalidation(
        tags: ['users'],
        strategy: InvalidationStrategy::Bulk
    )]
    public function importUsers(array $data): bool
    {
        // mass import...
        return true;
    }
}

// Usage
$repo->cachedInvalidate('update', 42, ['name' => 'John']);  // clears only user:42
$repo->cachedInvalidate('importUsers', $csvData);            // clears ALL users cache
```

### Automatic Model Cache Invalidation

[](#automatic-model-cache-invalidation)

Configure models in `config/redis-cache.php`:

```
'invalidation' => [
    'enabled' => true,
    'models' => [
        // Large table: clear only the changed record
        \App\Models\User::class => [
            'strategy' => 'individual',
            'tags' => ['users'],
        ],

        // Small lookup table: clear everything on any change
        \App\Models\Subject::class => [
            'strategy' => 'bulk',
            'tags' => ['subjects'],
        ],
    ],
],
```

Add the trait to your models for convenience methods:

```
use EhmedP\RedisCache\Traits\HasCacheableModel;

class User extends Model
{
    use HasCacheableModel;

    protected array $cacheTags = ['users'];
    protected ?int $cacheTtl = 3600;
}

// Cache a model
$user->cacheThis();

// Retrieve from cache
$data = User::fromCache(42);

// Retrieve from cache or DB (with auto-cache)
$user = User::fromCacheOrFind(42);

// Clear single model cache
$user->clearCache();

// Clear ALL user cache
User::flushModelCache();
```

Now when you do:

```
$user->update(['name' => 'New Name']);
// → ModelCacheObserver automatically clears cache for user:{id}

Subject::create(['title' => 'History']);
// → ModelCacheObserver automatically clears ALL "subjects" tagged cache
```

### Blade Directives — @cache / @endcache

[](#blade-directives--cache--endcache)

Cache rendered HTML blocks directly in your Blade templates:

```
{{-- Basic: cache with default TTL --}}
@cache('sidebar')

        @foreach($menuItems as $item)
            {{ $item->label }}
        @endforeach

@endcache

{{-- With custom TTL (2 hours) --}}
@cache('navigation', 7200)
    @include('partials.nav')
@endcache

{{-- With tags for targeted invalidation --}}
@cache('user-profile-' . $user->id, 3600, ['users', 'profiles'])

        {{ $user->name }}
        {{ $user->bio }}

@endcache

{{-- Dynamic keys with model data --}}
@cache('product-card-' . $product->id, 1800, ['products'])
    @include('components.product-card', ['product' => $product])
@endcache
```

### Events

[](#events)

Listen to cache events for monitoring, logging, or triggering side effects:

```
use EhmedP\RedisCache\Events\CacheHit;
use EhmedP\RedisCache\Events\CacheMissed;
use EhmedP\RedisCache\Events\CacheWritten;
use EhmedP\RedisCache\Events\CacheFlushed;
use EhmedP\RedisCache\Events\CacheInvalidated;

// In EventServiceProvider or via Event::listen()
Event::listen(CacheHit::class, function (CacheHit $event) {
    Log::debug("Cache HIT: {$event->key}");
});

Event::listen(CacheMissed::class, function (CacheMissed $event) {
    Log::debug("Cache MISS: {$event->key}");
});

Event::listen(CacheInvalidated::class, function (CacheInvalidated $event) {
    Log::info("Cache invalidated: {$event->type} -> {$event->entity}", $event->context);
});
```

Disable events for maximum performance:

```
REDIS_CACHE_EVENTS_ENABLED=false
```

### Extending — Custom Invalidation Logic

[](#extending--custom-invalidation-logic)

Implement `CacheInvalidatorInterface` for custom behavior:

```
use EhmedP\RedisCache\Invalidation\CacheInvalidator;

class CustomInvalidator extends CacheInvalidator
{
    public function onUpdated(string $entity, string|int $id, array $changed = []): void
    {
        // Only invalidate if critical fields changed
        $criticalFields = ['name', 'email', 'role'];

        if (empty(array_intersect(array_keys($changed), $criticalFields))) {
            return; // Skip invalidation for non-critical changes
        }

        parent::onUpdated($entity, $id, $changed);
    }
}
```

Bind it in your `AppServiceProvider`:

```
use EhmedP\RedisCache\Contracts\CacheInvalidatorInterface;

$this->app->bind(CacheInvalidatorInterface::class, CustomInvalidator::class);
```

Artisan Commands
----------------

[](#artisan-commands)

```
# Clear ALL cache (with confirmation)
php artisan redis-cache:clear

# Clear ALL cache (forced, for CI/CD)
php artisan redis-cache:clear --force

# Clear by tag(s)
php artisan redis-cache:clear-tag users
php artisan redis-cache:clear-tag users products api
php artisan redis-cache:clear-tag users --force

# Clear by key(s)
php artisan redis-cache:clear-key user:42
php artisan redis-cache:clear-key user:42 user:43 settings:theme
```

Architecture
------------

[](#architecture)

```
src/
├── Attributes/
│   ├── Cacheable.php                 # Class/method caching attribute
│   └── CacheInvalidation.php         # Write-operation invalidation attribute
├── Cache/
│   ├── CacheManager.php              # Main entry point (Facade root)
│   ├── CacheStore.php                # Redis store (SCAN-based, pipeline)
│   ├── TaggedCache.php               # Tag management via Redis SETs
│   └── CacheKeyGenerator.php         # Key generation strategy
├── Commands/
│   ├── ClearCacheCommand.php         # php artisan redis-cache:clear
│   ├── ClearTagCommand.php           # php artisan redis-cache:clear-tag
│   └── ClearKeyCommand.php           # php artisan redis-cache:clear-key
├── Contracts/
│   ├── CacheableInterface.php        # Cache store contract
│   ├── TaggableInterface.php         # Tagging contract
│   ├── KeyGeneratorInterface.php     # Key generator contract
│   └── CacheInvalidatorInterface.php # Invalidation contract
├── Events/
│   ├── CacheHit.php                  # Cache key found
│   ├── CacheMissed.php               # Cache key not found
│   ├── CacheWritten.php              # Value stored in cache
│   ├── CacheFlushed.php              # Cache entries flushed
│   └── CacheInvalidated.php          # Cache invalidated by data change
├── Facades/
│   └── RedisCache.php                # Laravel Facade
├── Invalidation/
│   ├── CacheInvalidator.php          # Default invalidation engine
│   ├── InvalidationStrategy.php      # Individual vs Bulk enum
│   └── ModelCacheObserver.php        # Eloquent model observer
├── Middleware/
│   └── CacheResponse.php             # HTTP response caching
├── Support/
│   ├── CacheGroup.php                # Group management
│   └── ClosureSerializer.php         # Closure fingerprinting
├── Traits/
│   ├── HasCacheable.php              # Class/method attribute caching
│   └── HasCacheableModel.php         # Eloquent model caching
├── View/
│   └── CacheBladeDirective.php       # @cache / @endcache
└── RedisCacheServiceProvider.php     # Service Provider

```

Testing
-------

[](#testing)

```
composer test
# or
./vendor/bin/phpunit
```

License
-------

[](#license)

MIT License. See [LICENSE](LICENSE) for details.

###  Health Score

37

—

LowBetter than 83% of packages

Maintenance88

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

62d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/a188a7f1c68673c28301bcebb50c111957bb8175d7d8f78f8e946385ef849826?d=identicon)[ehmedP](/maintainers/ehmedP)

---

Top Contributors

[![ehmedP](https://avatars.githubusercontent.com/u/129995403?v=4)](https://github.com/ehmedP "ehmedP (6 commits)")

---

Tags

laravelperformancerediscacheattributestagsgroups

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/ehmedp-laravel-redis-cache/health.svg)

```
[![Health](https://phpackages.com/badges/ehmedp-laravel-redis-cache/health.svg)](https://phpackages.com/packages/ehmedp-laravel-redis-cache)
```

###  Alternatives

[spatie/laravel-responsecache

Speed up a Laravel application by caching the entire response

2.8k8.2M51](/packages/spatie-laravel-responsecache)[awssat/laravel-visits

Laravel Redis visits counter for Eloquent models

975163.6k2](/packages/awssat-laravel-visits)[namoshek/laravel-redis-sentinel

An extension of Laravels Redis driver which supports connecting to a Redis master through Redis Sentinel.

38679.0k](/packages/namoshek-laravel-redis-sentinel)[nexxai/laravel-cfcache

A handful of Cloudflare cache helpers for Laravel

1317.7k](/packages/nexxai-laravel-cfcache)[yangusik/laravel-balanced-queue

Laravel queue management with load balancing between partitions (user groups)

786.4k](/packages/yangusik-laravel-balanced-queue)[byerikas/cache-tags

Allows for Redis/Valkey cache flushing multiple tagged items by a single tag.

1413.9k](/packages/byerikas-cache-tags)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
