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

Rememberable Laravel Package

laravel-enso/rememberable

Laravel Enso Rememberable adds transparent query caching to Eloquent models via a trait. Cache results with optional tags and automatically invalidate them when models are created, updated, or deleted, reducing database load with minimal code changes.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install

    composer require laravel-enso/rememberable
    

    The package is a thin trait library — no service provider bootstrapping beyond package discovery, and (depending on version) an optional rememberable.php config you can publish with php artisan vendor:publish --tag=rememberable-config. Verify the tag by running php artisan vendor:publish and picking the package from the list.

  2. Configure a real cache store first. This package is only as good as the store behind it. Use redis (or memcached) in .env:

    CACHE_DRIVER=redis
    

    File/database stores will work but are far slower and may not support the tagging/invalidation behavior you expect.

  3. Look here first, in this order:

    • src/Traits/Rememberable.php — the public API (remember(), cache(), forget(), and the key builder). This is the whole package; read it in five minutes.
    • src/Traits/Cacheable.php (if present) — the model-event hooks that auto-invalidate on saved/deleted.
    • config/rememberable.php — default TTL and cache prefix.
    • README.md — examples of the intended call signature.
  4. First use case — cache an expensive accessor. Add the trait to a model and wrap the computation:

    use LaravelEnso\Rememberable\Traits\Rememberable;
    
    class User extends Model
    {
        use Rememberable;
    
        public function getStatsAttribute(): array
        {
            return $this->remember('stats', fn () => [
                'orders' => $this->orders()->count(),
                'spent'  => $this->orders()->sum('total'),
            ], now()->addMinutes(10));
        }
    }
    

    The first call hits the DB; subsequent calls return the cached array until TTL expiry or explicit forget('stats').

When it's a fit: small/medium apps that want per-model caching without pulling in a full cache-decorator library. When it isn't: you need tag-based invalidation across many models, or your cache store can't support it — the package is 28 stars and low-activity, so treat it as a readable utility, not an actively maintained dependency.

Implementation Patterns

1. Cache computed/aggregate attributes, not raw rows. The sweet spot is expensive derivations: counts, sums, formatted payloads, permission flags. Caching whole Eloquent models works but serializes everything and goes stale the moment any column changes.

public function getBadgeAttribute(): string
{
    return $this->remember('badge', fn () => match (true) {
        $this->orders_count > 100 => 'gold',
        $this->orders_count > 10  => 'silver',
        default                   => 'bronze',
    });
}

2. Pair Rememberable with Cacheable for automatic invalidation. If the package ships the Cacheable trait, use both. Cacheable hooks saved/deleted and flushes that model's keys. Without it you must call forget() manually in every mutation path — a reliable source of stale data.

class User extends Model
{
    use Rememberable, Cacheable;
}

3. Invalidate related models from observers. Caching a user's stats means an Order save should flush the owning user's cache. Wire it in an observer:

class OrderObserver
{
    public function saved(Order $order): void
    {
        $order->user?->forget('stats');
    }
}

This is the pattern that actually keeps caches correct — the trait only gives you the primitives.

4. Scope keys by tenant/user when the data is scoped. Default keys are usually class:id:key. In multi-tenant apps, override the key builder so the tenant is part of the key, otherwise tenant A reads tenant B's cached attribute:

protected function cacheKey(string $key): string
{
    return sprintf('%s:%s:%s:%s', tenant()->id, static::class, $this->getKey(), $key);
}

(Confirm the exact method name in the trait — it's the single most important thing to override.)

5. Use a short TTL as a safety net, long TTL + explicit invalidation as the goal. Set a sane default in config (e.g. 1 hour) and pass shorter/longer TTLs per call. Treat TTL as a backstop for missed invalidations, not the primary correctness mechanism.

6. Warm caches in a queued command, not in web requests. Because the first call is a miss, cache warming avoids slow first-page loads:

User::chunk(200, fn ($users) => $users->each->getStatsAttribute());

Run it post-deploy and after bulk imports.

7. Cache at the resource/API layer, not the ORM, when responses span many models. Rememberable is model-scoped. If your response combines several models, cache the final array in a dedicated service class and invalidate it from observers — don't try to compose model-level caches into one payload.

Gotchas and Tips

  • Mass updates bypass model events. User::where(...)->update(...), insert(), and raw SQL never fire saved, so Cacheable won't flush. Either invalidate manually in the same job or avoid caching columns touched by bulk queries.
  • Pivot/relation changes aren't tracked. Attaching a role or updating a pivot won't invalidate the parent's cached relations. Add explicit forget() calls in the pivot mutation path.
  • Tag support depends on the driver. If invalidation relies on Cache::tags(...), redis/memcached are required; file/database/array drivers will throw or silently misbehave. Check before you ship.
  • Key collisions are silent. Always include the model class and primary key. Two models with a stats key and the same id will stomp each other otherwise.
  • Caching null is sticky. remember() caches falsy values too. If an accessor legitimately returns null while data loads asynchronously, you'll serve null until TTL. Use Cache::has() checks or short TTLs for that path.
  • Serialization cost. Caching large Eloquent collections can be slower than the query it replaces. Measure; prefer arrays of scalars.
  • Config caching in production. After changing rememberable.php, run php artisan config:clear (or config:cache on deploy) — otherwise you'll debug a config change that never took effect.
  • Tests need isolation. Use the array cache driver and flush between tests, or cached values leak across test cases and cause phantom failures:
    protected function setUp(): void
    {
        parent::setUp();
        Cache::flush();
    }
    
  • Debugging stale values: dump the resolved key ($model->cacheKey('stats') or equivalent), then Cache::get($key) and Cache::forget($key) in tinker. Nine times out of ten it's a key mismatch or a missed observer, not the trait.
  • Extension points to lean on: override the key builder (tenant/user scoping), override the default TTL, and inject a specific cache store via the config rather than relying on the app default. Those three overrides cover almost every real-world variation.
  • Maintenance risk. At 28 stars and 0.26 score, expect no upstream fixes. The package is small enough to vendor, read, and patch yourself — do that before depending on it in a critical path. Pin the version in composer.json.
  • Don't cache authorization-sensitive attributes across users unless the key is user-scoped; a shared key on a per-user permission flag is a data-leak bug waiting to happen.
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