Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Cache Bundle Laravel Package

fm/cache-bundle

fm/cache-bundle is a caching package for Laravel and PHP applications that bundles cache configuration, stores, and helpers into one reusable component, making it easier to set up and manage application caching with less boilerplate and more consistency across projects.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: it is not a Laravel package. fm/cache-bundle is a Symfony2 bundle (Financial Media's Doctrine ORM caching layer), last released 2013-10-25, archived, zero dependents. There is no ServiceProvider, no facade, no config/cache-bundle.php, no publishes() hooks. composer require fm/cache-bundle in a modern Laravel app will fail on symfony/* and doctrine/* version constraints before you ever write a line of code.

So the realistic "first steps" are:

  1. Skim the repo on GitHub, don't install it. Look at the DependencyInjection/ extension and the Doctrine event subscriber/listener classes — that is where the entire caching behaviour lives. Everything else is wiring.
  2. Extract the model, not the code. The bundle's core idea is: cache reads at the entity boundary, key by (entity class, primary key, version), and evict on postPersist / postUpdate / postRemove. That idea ports to Laravel cleanly; the implementation does not.
  3. First concrete use case in Laravel — a hot User::find($id) on every request:
// app/Cache/KeyBuilder.php
final class KeyBuilder
{
    public static function entity(string $class, int|string $id, int $version = 1): string
    {
        return sprintf('%s:%s:v%d', class_basename($class), $id, $version);
    }
}

// app/Models/User.php
public static function cached(int $id): ?self
{
    return Cache::remember(
        KeyBuilder::entity(static::class, $id),
        now()->addMinutes(15),
        fn () => static::find($id)
    );
}

That's ~10 lines and reproduces 90% of what the bundle was doing on a 2013 Symfony2 stack.

If you genuinely need to run the original (e.g. auditing legacy behaviour), do it in a disposable Symfony 2.8 container, never inside the Laravel app.

Implementation Patterns

The bundle operates as a Doctrine event subscriber + annotation-configured entity cache. The patterns worth translating:

1. Read-through at the repository boundary. In Symfony the listener intercepts hydration. In Laravel you wrap the query:

public function findCached(int $id): ?User
{
    $key = KeyBuilder::entity(User::class, $id);

    return Cache::remember($key, $this->ttl, fn () => $this->find($id));
}

2. Model-event invalidation — the Laravel equivalent of postUpdate/postRemove:

protected static function booted(): void
{
    static::saved(fn (self $m) => Cache::forget(KeyBuilder::entity(self::class, $m->getKey())));
    static::deleted(fn (self $m) => Cache::forget(KeyBuilder::entity(self::class, $m->getKey())));
}

Note saved covers both insert and update — the bundle's separate persist/update handlers collapse neatly here.

3. Query/collection caching with version stamps. Rather than deleting list keys, bump a version counter:

public function paginateCached(int $page): LengthAwarePaginator
{
    $version = Cache::get('users:version', 1);

    return Cache::remember("users:list:{$version}:{$page}", 600, fn () => $this->paginate(20));
}
// invalidation: Cache::increment('users:version');

This is the pattern the bundle approximated with Doctrine's second-level cache regions — regions map almost 1:1 onto Laravel cache tags (Redis/Memcached only).

4. Stampede protection. The bundle predates this entirely. Wrap cold reads in Cache::lock() when a hot key expires under load.

5. Keep key construction in one class. If you end up with keys scattered across models, controllers and jobs, invalidation becomes a archaeology exercise. Centralise in KeyBuilder and unit-test it.

Integration tips:

  • Use tags for group eviction (Cache::tags(['users'])->flush()) only on taggable stores — the file driver silently throws.
  • Set a per-entity TTL constant on the model (public int $cacheTtl = 900;) so policy lives next to the data.
  • Assert cache behaviour in tests with Cache::spy() + shouldHaveReceived('put') rather than hitting a real store.

Gotchas and Tips

  • No framework glue whatsoever. Adding it to Laravel means writing an adapter against an unmaintained API surface. You will spend more time on Composer resolution than on caching.
  • Doctrine assumptions don't survive the port. The bundle leans on UnitOfWork, change sets and second-level cache regions. Eloquent has no change-set concept for cache invalidation — saved fires even when nothing changed, and updated doesn't fire on no-op saves. Decide deliberately: invalidate eagerly (safe, slightly wasteful) or compare getDirty() (tighter, easy to get wrong).
  • Annotations are dead. 2013-era Symfony bundles use doctrine/annotations, which is deprecated and removed from the Doctrine ecosystem; PHP 8 attributes would be required. Another reason nothing here is drop-in.
  • postUpdate only fires on an actual flush change set in the original — a classic source of stale data if you assume "save = invalidate". Your Laravel port should not inherit that ambiguity.
  • Invalidation is the whole problem. Caching is three lines; knowing when to evict is the product. The bundle shipped with a conservative listener strategy precisely because aggressive invalidation was worse than a short TTL. Start with short TTLs (60–120s) and add explicit invalidation only where staleness is provably unacceptable.
  • Debugging: in Symfony the profiler showed hit/miss per region. In Laravel, use Laravel Debugbar's cache panel, or log misses inside your remember callback temporarily. Cache::has() before and after a request is a cheap smoke test.
  • Extension points, were you to fork it: the bundle exposes a cache-manager service and a key generator — those are the two things you'd swap for a PSR-6/PSR-16 backend. In Laravel the equivalents are the Cache manager and your KeyBuilder.
  • Recommendation: do not resurrect it. Laravel's ecosystem has maintained alternatives — genealabs/laravel-model-caching for transparent model-level caching, spatie/laravel-responsecache for HTTP-level, or the ~50 lines above with Cache::remember + booted() hooks. Given zero stars, zero dependents and a decade of dormancy, the bundle is a historical reference for what to cache, never how to do it today.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core