PHPackages                             trm42/cache-decorator - 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. trm42/cache-decorator

AbandonedLibrary[Caching](/categories/caching)

trm42/cache-decorator
=====================

Magical (as in saves manual work quite a lot) Cache Decorator for Laravel 5. Designed for usage with repositories but easily usable for other uses. If there's enough interest, can be made framework agnostic.

0.9.3(10y ago)52461[1 issues](https://github.com/trm42/CacheDecorator/issues)GPL-2.0PHP

Since Jan 4Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/trm42/CacheDecorator)[ Packagist](https://packagist.org/packages/trm42/cache-decorator)[ RSS](/packages/trm42-cache-decorator/feed)WikiDiscussions master Synced 3w ago

READMEChangelog (3)Dependencies (5)Versions (4)Used By (0)

(Magical) Cache Decorator for Laravel
=====================================

[](#magical-cache-decorator-for-laravel)

A transparent caching decorator for any Laravel-side class — services, API clients, query objects, repositories, you name it. Sub-class `CacheDecorator`, point it at the object you want to cache, and every public method call is automatically cached on first run and served from the cache on subsequent calls.

Stop writing boilerplate like this for every class whose results you want to cache:

```
namespace something\nice;

class CachedReportingService {

    protected $service;
    protected $cache;

    public function __construct(ReportingService $service, Cache $cache) {
        $this->service = $service;
        $this->cache = $cache;
    }

    public function dailyTotals($date)
    {
        $key = 'daily-totals-' . $date;
        if (!$this->cache->has($key)) {
            $results = $this->service->dailyTotals($date);
            $this->cache->save($key, $results);
        } else {
            $results = $this->cache->get($key);
        }

        return $results;
    }

    // ... and the same for every other method
}
```

With `CacheDecorator` the above shrinks to:

```
namespace My\Services;

use Trm42\CacheDecorator\CacheDecorator;

class CachedReportingService extends CacheDecorator {

    protected $ttl = 300; // cache ttl in seconds (or a DateInterval / DateTimeInterface)
    protected $prefix_key = 'reports';
    protected $excludes = ['recompute']; // methods listed here are never cached
}
```

…and use it like this:

```
$cached = new CachedReportingService(new ReportingService);

$cached->dailyTotals('2026-05-12'); // cache miss → calls ReportingService::dailyTotals
$cached->dailyTotals('2026-05-12'); // cache hit  → returns the cached value
```

The decorator forwards any method not listed in `$excludes` to the underlying object via `__call()` and caches the result. *The current version doesn't support objects as method arguments — coming in v1.0.0.*

### Optional: have the decorator instantiate the inner class for you

[](#optional-have-the-decorator-instantiate-the-inner-class-for-you)

If you don't want to wire the inner instance yourself, override `decoratedClass()` to return its FQCN and you can construct the decorator with no arguments:

```
class CachedReportingService extends CacheDecorator {
    protected $prefix_key = 'reports';

    protected function decoratedClass(): ?string
    {
        return ReportingService::class;
    }
}

$cached = new CachedReportingService;
```

### Custom caching logic for a single method

[](#custom-caching-logic-for-a-single-method)

If a particular method needs hand-tuned caching, override it in the subclass and use the protected helpers:

```
public function findByX($x)
{
    $key = $this->generateCacheKey(__FUNCTION__, compact('x'));

    $res = $this->getCache($key);

    if ($res === $this->cacheMiss()) {
        $res = $this->decorated->findX($x);

        $this->putCache($key, $res);
    }

    return $res;
}
```

`getCache()` returns the `cacheMiss()` sentinel (a shared `stdClass` instance) when the entry is absent — comparing with `===` against `cacheMiss()` lets the override round-trip falsy payloads (`0`, `''`, `[]`, `false`, `null`) correctly. Don't use truthiness checks like `if (!$res)`: they would treat a legitimately cached `false`/`0`/`[]` as a miss and refetch on every call.

### Cache tags

[](#cache-tags)

If your cache driver supports tags, declare which methods invalidate the tag bucket:

```
protected $tag_cleaners = ['recompute'];
protected $tags = ['reports'];
```

Using with repositories
-----------------------

[](#using-with-repositories)

For repository-flavored use cases the package ships `RepositoryCacheDecorator`. It behaves exactly like `CacheDecorator` but reads its config from the `repository_cache.*` namespace instead of `cache_decorator.*`, so repository caches can be tuned independently of other decorators.

```
namespace My\Repositories;

use Trm42\CacheDecorator\RepositoryCacheDecorator;

class CachedUserRepository extends RepositoryCacheDecorator {

    protected $ttl = 300;
    protected $prefix_key = 'users';
    protected $excludes = ['allWithoutCache'];
    protected $tag_cleaners = ['create'];
    protected $tags = ['users'];

    protected function decoratedClass(): ?string
    {
        return UserRepository::class;
    }

    // optional per-method override
    public function findByX($x)
    {
        $key = $this->generateCacheKey(__FUNCTION__, compact('x'));

        $res = $this->getCache($key);

        if ($res === $this->cacheMiss()) {
            $res = $this->decorated->findX($x);
            $this->putCache($key, $res);
        }

        return $res;
    }
}
```

Install
-------

[](#install)

Install with composer:

```
composer require trm42/cache-decorator
```

Publish whichever config you need (or both):

```
# Generic CacheDecorator config
php artisan vendor:publish --tag=cache-decorator-config

# Repository-flavored config
php artisan vendor:publish --tag=repository-cache-config
```

Environment variables:

ConfigEnv varDefault`cache_decorator.enabled``CACHE_DECORATOR_ENABLED``true``cache_decorator.ttl``CACHE_DECORATOR_TTL``300``cache_decorator.use_tags``CACHE_DECORATOR_TAGS``true``repository_cache.enabled``REPOSITORY_CACHE``true``repository_cache.ttl``REPOSITORY_CACHE_TTL``300``repository_cache.use_tags``REPOSITORY_CACHE_TAGS``true`Upgrading
---------

[](#upgrading)

### From the previous 0.x line

[](#from-the-previous-0x-line)

A few breaking changes tightened the public contract:

- **TTL bypass uses `null`, not `false`.** The "skip the cache" sentinel for `$ttl` is now `null`. The property type is `int|DateInterval|DateTimeInterface|null` (default `null`) and `setTtl()` has the same typed signature — replace any `protected $ttl = false;` with `protected $ttl = null;` and any `setTtl(false)` with `setTtl(null)`.
- **`$tags` and `$tag_cleaners` are plain arrays.** Both default to `[]` (no longer `array|false`). If the cache driver doesn't support tags (or `use_tags` is disabled in config), they are reset to `[]` rather than `false`. Custom subclasses that initialized either property to `false` should switch to `[]`.
- **Falsy cached values round-trip correctly.** Previously a method returning `0`, `''`, `[]`, or `false` would look like a cache miss and be refetched on every call. `getCache()` now returns a `cacheMiss()` sentinel (a shared `stdClass`) on a true miss, and `__call()` compares with `===` — so falsy results are cached and served from cache as expected. If you wrote a custom method override with `if (!$res)` around `getCache()`, switch it to `if ($res === $this->cacheMiss())` (see the override example above).
- **The `enabled` flag now actually short-circuits caching.** Setting `$enabled = false` (via the property, `setEnabled(false)`, or `{$config_key}.enabled = false`) now causes `__call()` to forward straight to the decorated object, skipping cache reads, writes, and tag flushing.

### From the repository-only version

[](#from-the-repository-only-version)

The base class is now generic and the repository-specific glue has been removed in favor of the generic hooks:

- Replace `extends CacheDecorator` with `extends RepositoryCacheDecorator` if you want to keep reading config from the `repository_cache.*` namespace.
- Rename your `repository()` method to `decoratedClass()` (return type `?string`). The old `repository()` abstract has been removed.
- In custom method overrides, replace `$this->repository->…` with `$this->decorated->…`. The `$this->repository` alias has been removed.
- `initRepository()` has been removed; pass the instance via the constructor, or override `decoratedClass()`.

```
-use Trm42\CacheDecorator\CacheDecorator;
+use Trm42\CacheDecorator\RepositoryCacheDecorator;

-class CachedUserRepository extends CacheDecorator {
+class CachedUserRepository extends RepositoryCacheDecorator {

-    public function repository()
+    protected function decoratedClass(): ?string
     {
         return UserRepository::class;
     }

-    $res = $this->repository->findX($x);
+    $res = $this->decorated->findX($x);
```

### From Laravel 5.x versions

[](#from-laravel-5x-versions)

This release targets Laravel 12 and 13 on PHP 8.2+. A few breaking changes:

- **TTL semantics changed from minutes to seconds** (matching Laravel 5.8+'s `Cache::put` API). Update any `$ttl` property and the `repository_cache.ttl` config value accordingly — e.g. `5` (minutes) becomes `300` (seconds).
- `$ttl` may now also be a `DateInterval` or `DateTimeInterface`, in addition to `int` and `null` (which bypasses the cache entirely).
- Minimum PHP version is 8.2.

*Tested with Laravel 12 and Laravel 13.*

###  Health Score

37

—

LowBetter than 81% of packages

Maintenance57

Moderate activity, may be stable

Popularity17

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity55

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 98.1% 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

Every ~57 days

Total

3

Last Release

3706d ago

### Community

Maintainers

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

---

Top Contributors

[![trm42](https://avatars.githubusercontent.com/u/4980285?v=4)](https://github.com/trm42 "trm42 (51 commits)")[![theel0ja](https://avatars.githubusercontent.com/u/5832930?v=4)](https://github.com/theel0ja "theel0ja (1 commits)")

---

Tags

laravelcachelaravel 5repositorydecorator

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/trm42-cache-decorator/health.svg)

```
[![Health](https://phpackages.com/badges/trm42-cache-decorator/health.svg)](https://phpackages.com/packages/trm42-cache-decorator)
```

###  Alternatives

[spatie/laravel-responsecache

Speed up a Laravel application by caching the entire response

2.8k8.7M64](/packages/spatie-laravel-responsecache)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9742.3M121](/packages/roots-acorn)[propaganistas/laravel-disposable-email

Disposable email validator

6012.9M7](/packages/propaganistas-laravel-disposable-email)[psalm/plugin-laravel

Psalm plugin for Laravel

3345.1M337](/packages/psalm-plugin-laravel)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k14.1M122](/packages/laravel-pulse)[mike-bronner/laravel-model-caching

Automatic caching for Eloquent models.

2.4k55.1k1](/packages/mike-bronner-laravel-model-caching)

PHPackages © 2026

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